Description
One important consideration to take into account when coding is the end result. Client usage of the code should be easy to use, he should not be aware of the complexities of the algorithms used when defining your methods and he should not be concerned of the many types of objects but focus on the “lexical requirements of his needs”
Implementation
static class Extensions{ public static Worker TakeMinimum(this IEnumerable<Worker> workers) { if (workers.Count() == 0) return null; Worker minWorker = workers.ToList()[0]; foreach (Worker w in workers) if (w.HourlyPrice < minWorker.HourlyPrice) minWorker = w; return minWorker; } } class Worker{ public double HourlyPrice { get; set; } public double MetersPerHour{ get; set; } public double EstimateTime(double squareMeters) => squareMeters / MetersPerHour; public double EstimateCost(double sqm) => EstimateTime(sqm) * HourlyPrice; } class Program{ public static Worker GetCheapestWorker(IEnumerable<Worker> workers) => workers.TakeMinimum(); }
Implementation of the GetCheapestWorker is done in the client side and he must be aware of the worker class and the available methods needed to reach his goal. How can expose to the client just a minimal functionality so that he doesn’t have to handle iterations and implementations?
Steps: create a class that handles the sequence and create a factory that handles requirements:
class CompositeWorker { private Func<IEnumerable<IWorker>, IWorker> Reduce; private IEnumerable<IWorker> Workers; public CompositeWorker(IEnumerable<IWorker> workers, Func<IEnumerable<IWorker>, IWorker> reduce) { this.Workers = workers; this.Reduce = reduce; } public IWorker Get() => this.Reduce(Workers); public double GetPrice(double sqm) => this.Reduce(Workers).EstimateCost(sqm); }
Reduce delegate will be a method that will create a selection of 1 worker from the list of workers.
Using Reduce as a func delegate instead of an abstract class will alow us to define this method outside the class body.
abstract class CompositeFactory { public static IWorker GetCheapestWorker(IEnumerable<IWorker> Workers) { return new CompositeWorker(Workers, (workers) => workers.TakeMinimum()).Get(); } public static double GetCheapestWorkerPrice(double sqm, IEnumerable<IWorker> Workers) { return new CompositeWorker(Workers, (workers) => workers.TakeMinimum()).GetPrice(sqm); } }
In the end, by adding more classes to our design we managed to reduce client side exposure:
//given some set of workers: Worker[] workers = new Worker[10]; IWorker w = CompositeFactory.GetCheapestWorker(workers); double p = CompositeFactory.GetCheapestWorkerPrice(20, workers);