We recently ran into a challenging design issue at work. We are working on a web application that must support internationalization since we have customers in many different countries. The profile for each customer has different settings and configurations based upon the customer’s country.
These settings/configurations overlap and differ across countries. The goal, of course, is to design/implement the code in a way that enables us to reuse code that is common across countries. Below is the scenario that we are dealing with:
USCustomer.FeatureA() is the same as
USCustomer.FeatureB() is the same as
CACustomer.FeatureC() is the same as
KRCustomer.FeatureA() differs from
CACustomer.FeatureB() differs from
USCustomer.FeatureC() differs from
The first attempt was of course to try and solve this via sub-classing. Let’s look at the different options that we have for sub-classing:
- Make CA & KR sub-classes of US. This allows us to re-use
USCustomer.FeatureA() for CA and
USCustomer.FeatureB() for KR. But it doesn't allow us to re-use
CACustomer.FeatureC() for KR.
- Okay, no problem, you say. We'll just make KR a sub-class of CA. So, CA is a sub-class of US and KR is a sub-class of CA. But then we run into the issue that
USCustomer.FeatureB() can no longer to be re-used for
Now imagine this kind of situation spread across 15 different countries. Ouch! So sub-classing clearly is not the answer.
It sounds like we need some sort of “strategy” that'll let us swap/differ settings and configurations across countries without all this coupling. Well the strategy is to apply the Strategy design pattern. Below is the overall strategy:
- Encapsulate the features: Each feature gets its own class. So we end up with the following classes:
FeatureA : IFeatureA
FeatureB : IFeatureB
FeatureC : IFeatureC
FeatureA1 : IFeatureA
FeatureB1 : IFeatureB
FeatureC1 : IFeatureC
- Setup the constructor of the
Customer class to take in the concrete class that is specific to the feature that they require as a parameter. For instance:
public class Customer
public Customer() :
this(new FeatureA(), new FeatureB(), new FeatureC())
public Customer(IFeatureA a, IFeatureB b, IFeatureC c)
featureA = a;
featureB = b;
featureC = c;
public void DoInitialSetup()
private IFeatureA featureA;
private IFeatureB featureB;
private IFeatureC featureC;
public class CACustomer : Customer
public CACustomer() :
base(new FeatureA(), new FeatureB1(), new FeatureC1())
public class KRCustomer : Customer
public KRCustomer() :
base(new FeatureA1(), new FeatureB(), new FeatureC1())
And voila! We can now swap implementations across countries and add different implementations for different countries as needed.
The above example was simplified to effectively demonstrate the Strategy pattern without confusing the reader. In particular, in the above example, the customer classes directly instantiate the features that they need. This is generally a bad idea in practice for at least two reasons:
- It makes it difficult to unit test the
- Each type of customer is still coupled to the specific feature-set that it is using. In other words, the features cannot be changed dynamically.
Instead of directly instantiating the classes, we should be using an IOC container to handle the object instantiations and configurations.
For instance, for our application, we are using an XML file to tie concrete implementations with their respective countries. Below is an example:
Based on the current country, the right classes get instantiated. If we ever need to change KR to use
FeatureC instead of
FeatureC1, all we do is make the change in the XML file and we’re done.
So, in conclusion, the key to the strategy pattern is encapsulation: Remove the logic that differs from the logic that stays the same via encapsulation.