Description
In OOP you will often encounter the abundance of using IF/Else which on the negative side may provide a deep layer of nesting which is increasing the complexity of your solution. Imagine having more then 2 variables that must be tested simultaneously.
There are some good design patterns that would replace the classical procedural approach with a nice smooth OOP approach.
One approach is names Strategy Pattern. Consider objects to the right:
public enum EmployeeType { Manager, Administrative, General } public class Employee { public string Name { get; set; } public EmployeeType Type { get; set; } } public class Payments { public double Pay(Employee employee) { switch (employee.Type) { case EmployeeType.Administrative: return 100; case EmployeeType.Manager: return -1000; case EmployeeType.General: return 200; default: return 0; } } } static void Main(string[] args) { Employee me = new Employee { Name = "Me", Type = EmployeeType.Manager }; Payments payments = new Payments(); Console.WriteLine(payments.Pay(me)); }
Solution
We continue by introducing polymorphism into Employee class. Then instead of determining what type of employee it is, we define separate handlers for each
public abstract class Employee { public string Name { get; set; } public EmployeeType Type { get; protected set; } } public class EmployeeAdministrative : Employee { public EmployeeAdministrative() { this.Type = EmployeeType.Administrative; } } public class EmployeeManager : Employee { public EmployeeManager() { this.Type = EmployeeType.Manager; } } public class EmployeeGeneral : Employee { public EmployeeGeneral() { this.Type = EmployeeType.General; } } public class Payments { public double Pay(EmployeeAdministrative employee) => 100; public double Pay(EmployeeManager employee) => -1000; public double Pay(EmployeeGeneral employee) => 200; } static void Main(string[] args) { EmployeeManager me = new EmployeeManager { Name = "Me" }; Payments payments = new Payments(); Console.WriteLine(payments.Pay(me)); }
The following introduces a new pattern , strategy pattern, into the overloaded methods will make the code even cleaner:
public class Payments { private Dictionary<EmployeeType, Func<double>> paymentType = new Dictionary<EmployeeType, Func<double>> { { EmployeeType.Manager, () => -1000 }, { EmployeeType.Administrative, () => 100}, { EmployeeType.General, () => 200 } }; public double Pay(Employee employee) => paymentType[employee.Type](); } static void Main(string[] args) { EmployeeManager me = new EmployeeManager { Name = "Me" }; Payments payments = new Payments(); Console.WriteLine(payments.Pay(me)); }
Sorry, But I Can’t get it. What is the advantage of using this pattern. I have to add another line of code in payment in payments class if I need to add a new EmployeeType such as supervisor. Is there any way to not change the Payment class if I need to add a new EmployeeType?
This patterns removes the project’s dependency on linear non-OOP methods like switch or if clauses. Of course this is only a short example and should not be applied as such to real projects, but adapted.
The advantage of having a dictionary over a switch statement is that you may have multiple sections in your code where you make the same check. Using a dictionary actually cleans up all of the sections and the only part changing is the dictionary it-self.
There are of course other methods you can implement if you want to keep the Payments class non-changeable, for this you can use a factory method, where your Payments class doesn’t change but it accesses child-methods to handle this calculation.
Or you can use State Model Design Pattern (https://dzone.com/articles/design-patterns-state)