Bookmark and Share

Avoid Parallel Hierarchies

In Implementation Patterns by Kent Beck, he mentions a use of inheritance that can be particularly troublesome: parallel hierarchies. He uses an example of this from an insurance system.

image

As shown in the image above, each subclass from one hierarchy requires one from the other hierarchy. This leads to class explosion! As you can imagine, it’s even worse when there are more than two hierarchies interacting in this manner.

Here’s how the skeleton C# code for the example might look:

public abstract class Contract
{
    public Product Product { get; protected set; }
}

public class InsuranceContract : Contract
{
    public InsuranceContract(InsuranceProduct product)
    {
        this.Product = product;
    }
}

public class PensionContract : Contract
{
    public PensionContract(PensionProduct product)
    {
        this.Product = product;
    }
}

public abstract class Product
{
}

public class InsuranceProduct : Product
{
}

public class PensionProduct : Product
{
}

Kent’s team was able to get around this by restructuring Contract to only use Product without worrying about specific versions.

Generics can alleviate some of this issue.

image

 

public class Contract<T> where T : Product
{
    public T Product { get; protected set; }

    public Contract(T product)
    {
        this.Product = product;
    }
}

public abstract class Product
{
}

public class InsuranceProduct : Product
{
}

public class PensionProduct : Product
{
}

There may be need for variations in the types of Contract, in which case a subclass can be created. However, there may be times which functionality needs to be created on a case by case basis or calculations are being determined based upon formulas in an external source. This can be solved a number of ways. Here’s an example I created using delegates and IronPython.

public class ContractFactory
{
    public Contract<T> CreateContract<T>(T product) where T : Product
    {
        ScriptRuntime py = Python.CreateRuntime();
        dynamic rules = py.UseFile("rules.py");            
        var contract = new Contract<T>(product);
        contract.CalculatePremium = p => rules.CalculatePremium(p);
        return contract;
    }
}

public class Contract<T> where T : Product
{
    public T Product { get; protected set; }
    public Func<T, double> CalculatePremium { get; set; }

    public Contract(T product)
    {
        this.Product = product;
    }
}

You will sometimes end up with parallel inheritance hierarchies as inheritance can be a good representation of the relationship between objects. These relationships can naturally fall into this pattern. However, you should nip it in the bud if the classes start to grow as they can easily become cumbersome. Always consider what changes between the classes and consider moving the responsibility elsewhere.

blog comments powered by Disqus

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
Telerik Developer Evangelist, C# MVP

JustCode

Telerik .NET Ninja

 

INETA Community Speakers Program

 

MVP - Visual C#

 

Friend of RedGate

World Map

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.