If you’ve read the age old refactoring book by Martin Fowler, you’d have come across his distaste for everything “if / switch”. There are some grounds to this, if you were to believe on some concept of SOLID principles, where
- your class should have only a single responsibility
- your class should be closed for modification and open to extension
There was a discussion this week on why a factory pattern might look ugly on a certain program that was being written and I came across the opportunity on how we can solve a very specific problem.
Given a request, a specific view(s) are created. The view(s) are created based on a certain context (rules).
Naturally the first iteration went to having a factory with switch statements that returns views based on rules.
So here, I have an example on how we can solve the switch statement problem using a known pattern similar to the abstract factory pattern (examples in c#).
namespace SolvingTheManagerProblem | |
{ | |
public abstract class DataViewBase | |
{ | |
} | |
public class FirstView : DataViewBase | |
{ | |
} | |
public class SecondView : DataViewBase | |
{ | |
} | |
} |
using System.Collections.Generic; | |
using System.Linq; | |
namespace SolvingTheManagerProblem | |
{ | |
public interface IProvideRules | |
{ | |
IEnumerable<IRule> GetRules(DataViewBase view); | |
} | |
public abstract class DataViewFactory<T> : IProvideRules where T : class | |
{ | |
public IEnumerable<IRule> GetRules(DataViewBase view) | |
{ | |
var specificView = view as T; | |
if (specificView == null) | |
return Enumerable.Empty<IRule>(); | |
return GetRulesFor(specificView); | |
} | |
protected abstract IEnumerable<IRule> GetRulesFor(T specificView); | |
} | |
public class FirstViewDataViewFactory : DataViewFactory<FirstView> | |
{ | |
protected override IEnumerable<IRule> GetRulesFor(FirstView specificView) | |
{ | |
yield return new DummyRule(); | |
} | |
} | |
public class SecondViewDataViewFactory : DataViewFactory<SecondView> | |
{ | |
protected override IEnumerable<IRule> GetRulesFor(SecondView specificView) | |
{ | |
yield return new DummyRule(); | |
yield return new AnotherDummyRule(); | |
} | |
} | |
} |
namespace SolvingTheManagerProblem | |
{ | |
public interface IRule | |
{ | |
} | |
public class DummyRule : IRule | |
{ | |
} | |
public class AnotherDummyRule : IRule | |
{ | |
} | |
} |
using System.Collections.Generic; | |
using System.Linq; | |
using NUnit.Framework; | |
namespace SolvingTheManagerProblem | |
{ | |
[TestFixture] | |
public class TestThings | |
{ | |
[Test] | |
public void FirstViewShouldReturnJustOneRule() | |
{ | |
var firstView = new FirstView(); | |
var ruleProviders = new List<IProvideRules> | |
{ | |
new FirstViewDataViewFactory(), | |
new SecondViewDataViewFactory() | |
}; | |
var rulesForFirstView = ruleProviders.SelectMany(t => t.GetRules(firstView)); | |
Assert.That(rulesForFirstView.Count(), Is.EqualTo(1)); | |
} | |
[Test] | |
public void SecondViewShouldReturnTwoRules() | |
{ | |
var secondView = new SecondView(); | |
var ruleProviders = new List<IProvideRules> | |
{ | |
new FirstViewDataViewFactory(), | |
new SecondViewDataViewFactory() | |
}; | |
var rulesForSecondView = ruleProviders.Select(t => t.GetRules(secondView)); | |
Assert.That(rulesForSecondView.Count(), Is.EqualTo(2)); | |
} | |
} | |
} |
I hope this pattern helps you in making better software.