Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Friday, 11 September 2015

Below is code from https://github.com/djhmateer/Refactoring,
Based on http://mikehadlow.blogspot.co.uk/2015/08/c-program-entirely-with-static-methods.html  *

The objective of this talk is to refactor a simple Console Application which does an ETL towards code which is:

  • Easier to understand (less WTF’s per minute)
  • Easier to test
  • More pleasing

In general I firstly went in an OO style towards composition.  Then got rid of the ceremony of OO which I didn’t need, and used a simpler functional approach

  • Code looks ‘a little odd to the uninitiated’ *
  • Less lines code
  • Very easy to test
// Functional style of the QuoteImporter app, with logging using a decorator // Firstly decorate each separate function (1,2,3) with it's logger // Then compose together, decorating that top level function too private static void Main() { // 1. ReadFileListOfLines is a function with 0 Parameters, which returns a List of strings // passing it a lambda expression (anonymous function) // Log takes 1 parameter s // ReadFileListOfLines takes 0 parameters // returns a string Func<IEnumerable<string>> readFileListOfLines = () => ReadFileListOfLinesLogger(() => ReadFileListOfLines(), s => Log(s)); // 2. ParseLine takes 1 Parameter string s, which is wired up to lineToParse // using method group - don't need to specify s => Log(s) // returns a Quote Func<string, Quote> parseLine = lineToParse => ParseLineLogger(s => ParseLine(s), Log, lineToParse); // 3. InsertQuoteIntoDatabase takes 1 parameter Quote // returns nothing (therefore its an Action) // quote is wired up to q in the logger Action<Quote> insertQuoteIntoDatabase = quote => InsertQuoteIntoDatabaseLogger(q => InsertQuoteIntoDatabase(q), Log, quote); // Compose the Functions together (without running them) // Passing in a lambda expression (anonymous function) to Action run Action quoteImporter = () => QuoteImporter(readFileListOfLines, parseLine, insertQuoteIntoDatabase); // Decorating logging in the top level function (but no logging in this composition root function) Action run = () => QuoteImporterLogger(quoteImporter,Log); run(); } public static void QuoteImporterLogger(Action quoteImporter, Action<string> log) { log("Start QuoteImporter"); quoteImporter(); log("End QuoteImporter"); } // Top level function, which depends upon 3 other functions (passed in as parameters) public static void QuoteImporter( Func<IEnumerable<string>> readFileListOfLines, Func<string, Quote> parseLine, Action<Quote> insertQuoteIntoDatabase) { IEnumerable<string> lines = readFileListOfLines(); foreach (var line in lines) { var quote = parseLine(line); insertQuoteIntoDatabase(quote); } } // 1 ReadFile List public static IEnumerable<string> ReadFileListOfLinesLogger( Func<IEnumerable<string>> readFileListOfLines, Action<string> log) { log("Start ReadFileListOfLines"); var result = readFileListOfLines(); // Easy way to view contents of IEnumerable to log log($"result of ReadFileListOfLines: {string.Join(Environment.NewLine,result.Select(s => s))}"); log("End ReadFileListOfLines"); return result; } public static IEnumerable<string> ReadFileListOfLines() { string[] fileTextLines = File.ReadAllLines(@"..\..\quotesWithTitles.csv"); return fileTextLines.ToList(); } // 2 ParseLine public static Quote ParseLineLogger( Func<string, Quote> parseLine, Action<string> log, string lineToParse) { log($"Start ParseLine with input {lineToParse}"); Quote result = parseLine(lineToParse); log($"End ParseLine with ouput {result.Title}, {result.Body}"); return result; } public static Quote ParseLine(string line) { string[] values = line.Split(','); if (values.Length < 2) throw new ApplicationException("Unknown state - too few commas"); if (values.Length > 2) throw new ApplicationException("Unknown state - too many commas"); string title = values[0]; string body = values[1]; return new Quote{Title = title,Body = body}; } // 3 InsertQuote public static void InsertQuoteIntoDatabaseLogger( Action<Quote> insertQuoteIntoDatabase,Action<string> log, Quote quote) { log($"Start InsertQuoteIntoDatabase with input {quote.Title} {quote.Body}"); insertQuoteIntoDatabase(quote); log($"End InsertQuoteIntoDatabase"); } private static void InsertQuoteIntoDatabase(Quote quote) { string connectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Dev\Refactoring\Refactoring\Database1.mdf;Integrated Security=True"; using (var connection = new SqlConnection(connectionString)) { var cmd = new SqlCommand("INSERT INTO Quotes (Title, Text) VALUES (@Title, @Quote)", connection); cmd.Parameters.AddWithValue("@Title", quote.Title); cmd.Parameters.AddWithValue("@Quote", quote.Body); connection.Open(); cmd.ExecuteNonQuery(); } } public static void Log(string message) { Console.WriteLine("log: {0}", message); }

The tests:

[Fact] public void ReadFileList_ShouldReturn3Lines() { IEnumerable<string> result = ImporterFunctional.ReadFileListOfLines(); Assert.Equal(3, result.Count()); } [Fact] public void ParseLine_AValidLine_ShouldReturnAQuote() { string line = "Basic,Programming in Basic causes brain damage - Edsger Wybe Dijkstra"; Quote result = ImporterFunctional.ParseLine(line); Assert.Equal("Basic", result.Title); Assert.Equal("Programming in Basic causes brain damage - Edsger Wybe Dijkstra", result.Body); } [Fact] public void ParseLine_AnEmptyLine_ShouldThrow() { string line = ""; Assert.Throws<ApplicationException>(() => ImporterFunctional.ParseLine(line)); } [Fact] public void ParseLine_TooManyCommas_ShouldThrow() { string line = "asdf,asdf,asdf"; Assert.Throws<ApplicationException>(() => ImporterFunctional.ParseLine(line)); } // 2. Testing the top level Orchestration function [Fact] public void QuoteImporter_GivenMockFile_ShouldInsertIntoMockDatabase() { // 1. ReadFileListOfLines mock string actualLineFromFile = ""; Func<IEnumerable<string>> readFileListOfLines = () => { var listOfLines = new[] {"title, quote here"}; actualLineFromFile = listOfLines[0]; return listOfLines; }; // 2. ParseLine mock string actualLineSentToParseLine = ""; Func<string, Quote> parseLine = s => { actualLineSentToParseLine = s; var quote = new Quote {Title = "title2", Body = "quote here2"}; return quote; }; // 3. InsertQuoteIntoDatabase mock // using a closure IList<Quote> actualQuotes = new List<Quote>(); Action<Quote> insertQuoteIntoDatabase = quote => { actualQuotes.Add(quote); }; // compose. Action is a delegate - doesn't return anything Action run = () => ImporterFunctional.QuoteImporter( readFileListOfLines, parseLine, insertQuoteIntoDatabase); run(); // 1. Testing the ReadFileListOfLines mock was called Assert.Equal("title, quote here", actualLineFromFile); // 2. Testing the mock parser received the data from the ReadFileListOfLines mock Assert.Equal("title, quote here", actualLineSentToParseLine); // 3. Testing the mock database received the data from the ParseLine mock Assert.Equal("title2", actualQuotes[0].Title); Assert.Equal("quote here2", actualQuotes[0].Body); }
| | # 
# Thursday, 10 September 2015

Passing 0 Parameters to a function and logging

// Logging using a decorator - passing 0 parameters to // Action is like a function which can take parameters but doesn't return anything. // Passing in 2 method group QuoteImporter, Log // Passing in 2 lambda expression (anonymous function) () => QuoteImporter, s => Log(s) // Passing in 2 anonymous methods delegate() { QuoteImporter(); }, delegate(string s) {Log(s);}; Action run = () => QuoteImporterLogger(QuoteImporter, Log); run(); } public static void QuoteImporterLogger(Action quoteImporter, Action<string> log) { log("Start QuoteImporter"); quoteImporter(); log("End QuoteImporter"); } // Don't want logging code cluttering up public static void QuoteImporter() { Console.WriteLine("In QuoteImporter"); } public static void Log(string message) { Console.WriteLine($"log: {message}"); }

Passing 1 Parameter to a function and logging

// Logging using a decorator - passing in 1 parameter private static void Main() { // Function - given a string, return a string. // s and t represent the inbound parameters into ParseLine and Log (not needed) // line is inbound parameter into ParseLineLogger Func<string, string> run = line => ParseLineLogger(s => ParseLine(s), t => Log(t), line); string result = run("blah"); } public static string ParseLineLogger(Func<string, string> parseLine, Action<string> log, string line) { Console.WriteLine($"Start ParseLine with line: {line}"); string result = parseLine(line); Console.WriteLine($"Result: {result}"); Console.WriteLine("End ParseLine"); return result; } public static string ParseLine(string line) { return line + " ok"; } public static void Log(string message) { Console.WriteLine($"log: {message}"); }
| | # 
# Tuesday, 08 September 2015

From http://programmers.stackexchange.com/a/139113/25850

public class DecoratorSpikeCompositionRoot { static void CompositionRoot() { // Instead of calling Processor.ReadFile directly we call the wrapper var processorLogger = new ProcessorLogger(new Processor(), new Log()); string result = processorLogger.ReadFile("file.csv"); } } public class ProcessorLogger : IProcessor { private readonly IProcessor processor; private readonly ILog log; public ProcessorLogger(IProcessor processor, ILog log) { this.processor = processor; this.log = log; } public string ReadFile(string fileName) { this.log.Debug($"start ReadFile fileName: {fileName}"); string result = this.processor.ReadFile(fileName); this.log.Debug($"end ReadFile with result: {result}"); return result; } } public interface IProcessor { string ReadFile(string fileName); } public class Log : ILog { public void Debug(string message) { Console.WriteLine($"log: {message}"); } } public interface ILog { void Debug(string message); } public class Processor : IProcessor { // Benefit of decorator is we don't have logging cluttering up the ReadFile method public string ReadFile(string fileName) { Console.WriteLine("In ReadFile"); return "stuff in file"; } }
image
| | #