/// <summary> /// Here we have an application used to conduct market and sales research at our /// local farmer's market. As part of our efforts to improve the services we provide /// at the farmer's market, we need to aggregate sales and demographic data already collected /// by our vendors. /// Each class of vendor has different means of collecting its data, and we want to write /// an extensible way to aggregate and report on this data without modifying each class to /// write custom research reports. This would help us write more extensible code (Open/Closed principle) /// and keep us from adding unwanted responsibility to the data processing classes /// (Single Responsibility principle). /// We can leverage the Visitor Pattern, where we make our data processing classes "visitable" /// by anything that generates a Report. Then, we can create individual Visitor classes that /// generate their own specific types of reports. /// If a processor doesn't provide the data we need for a particular report, we can handle that /// logic altogether differently, perhaps notifying the stakeholders that we can't access this data /// for reporting purposes. /// </summary> private static void Main() { var logger = new ConsoleLogger(); var emailer = new Emailer(logger); var farmDatabase = new Database(Configuration.ConnectionString, logger); var floristDatabase = new Database(Configuration.ConnectionString, logger); var bakeryDatabase = new Database(Configuration.ConnectionString, logger); logger.LogInfo("🌾 Welcome to the Farmer's Market Research Application!"); logger.LogInfo("-------------------------------------------------------"); // Various existing components know how to produce the data we need // But we don't want to modify them to add new responsibilities, like // generating reports. We make these Visitable, so that a Visitor // can use them as it needs. var bakeryDataProcessor = new BakeryDataProcessor(emailer, bakeryDatabase); var farmerDataProcessor = new FarmerDataProcessor(farmDatabase); var floristDataProcessor = new FloristDataProcessor(emailer, floristDatabase); var dataProcessors = new List <IVisitable <Report> > { bakeryDataProcessor, farmerDataProcessor, floristDataProcessor }; var reporter = new ReportRunner(logger); logger.LogInfo("==== Generating Sales Reports ===="); reporter.RunReports(dataProcessors, new SaleDataVisitor(logger)); logger.LogInfo("==== Generating Market Reports ===="); reporter.RunReports(dataProcessors, new MarketResearchReportVisitor(logger)); }
/// <summary> /// Here the Florist Co-Op wants to be emailed when we run the Sales Report /// The FloristDataProcessor doesn't know anything other than how to generate /// the data it already works with, and a method enabling it to be "visited." /// </summary> /// <param name="processor"></param> /// <returns></returns> public SalesReport Visit(FloristDataProcessor processor) { Console.ForegroundColor = ConsoleColor.Magenta; _logger.LogInfo("Visiting Florist for Sales Report"); var sales = processor.GetDailyOrderAmounts(); Console.ResetColor(); return(new SalesReport(_logger, DateTime.UtcNow, sales.Sum())); }
public MarketResearchReport Visit(FloristDataProcessor processor) { var customerProfiles = processor.GetMonthlyCustomerProfiles(); var customerAges = customerProfiles.Select(customer => customer.Age).ToList(); var averageAge = (decimal)Math.Round(customerAges.Average(), 2); var youngestAge = customerAges.Min(); var oldestAge = customerAges.Max(); _logger.LogInfo("Visiting Florist for Generating Market Report", ConsoleColor.Green); var report = new MarketResearchReport(_logger); report.SetData(customerProfiles.Count, averageAge, oldestAge, youngestAge); return(report); }