http://www.codinghelmet.com/  

Wear a helmet. Even when coding.

howto > reduce-cyclomatic-complexity-working-with-collections

How to Reduce Cyclomatic Complexity: Working With Collections
by Zoran Horvat @zoranh75

One of the typical complications in code is letting the class contain a collection. The problem with such design is that all operations on that piece of state would have to be aware of the collection. Why Collections Are So Complicated?

Getting hold of a collection comes with many irritating questions. Does the collection contain objects or is it empty? Some aggregate functions cannot be calculated on empty collections. For example, maximum is not defined on an empty collection. Some other aggregate functions do not care about this problem: Sum or count functions would both happily return zero on an empty collection.

When a class controls a collection it has so many things to think about. Is it safe to call certain operation? Should the result be modified somehow before proceeding? Should it iterate through the whole collection or take only some elements into account.

What to do With Contained Collections?

Classes that contain collections tend to grow and become overwhelmed by scaffolding code which has nothing to do with class's responsibilities. The only purpose of that code is to make the class work. This code is a good candidate for a refactoring which would relieve the containing class from having to know how to iterate through multiple elements and how to call processing on them.

Natural solution to the problem is to introduce another class with a single purpose of holding the collection. This class would be responsible for iterating through the collection, filtering elements, invoking operations on elements and aggregating the results.

Final goal of this refactoring is to simplify the original class and let it focus on the value it delivers to the domain model. Lesser responsibilities, such as iterating through a list, should be removed and enclosed in a separate class.

Composite Design Pattern

In many cases, operations defined on a single object can also be defined on a collection of such objects. It only takes to define an appropriate aggregate function and to return a single result. This operation would then work uniformly when invoked on a single object and when called on a group of objects.

Main criterion which decides whether the Composite design pattern is applicable in particular case is the interface which exposes the operation. If we can define the same interface on a single element and on the collection, then the Composite pattern can be applied.

Principal Example Leading to Composite

Suppose that we want to hire a painter to paint a house. Painter needs certain number of days to finish work. Now suppose that we want to hire several painters to work together. Time they need to finish when working together is shorter.

We can start from a definition of a painter:

class Painter
{

    private readonly float daysPerHouse;

    public Painter(float daysPerHouse)
    {
        this.daysPerHouse = daysPerHouse;
    }

    public float EstimateDaysToPaint(int houses)
    {
        return houses * daysPerHouse;
    }

}

Painter just paints the houses. When asked to estimate work, the painter multiplies number of houses by his own velocity and returns the estimate.

We could introduce a land owner, a guy who owns several houses:

class LandOwner
{

    private readonly Painter painter;
    private readonly int housesCount;

    public LandOwner(Painter painter, int housesCount)
    {
        this.painter = painter;
        this.housesCount = housesCount;
    }

    public void ManageHouses()
    {
        float daysToPaint = this.painter.EstimateDaysToPaint(this.housesCount);
        Console.WriteLine("Painting houses for {0:0.0} day(s).", daysToPaint);
    }

}

Land owner keeps a reference to a painter. When time comes to manage houses, owner asks the painter to tell how much time he would need to paint all the houses.

Problems begin when it turns out that one painter cannot finish all the work in reasonable time. At that point, land owner hires more painters:

class LandOwner
{

    private readonly IEnumerable<Painter> painters;
    private readonly int housesCount;

    public LandOwner(IEnumerable<Painter> painters, int housesCount)
    {
        this.painters = new List<Painter>(painters);
        this.housesCount = housesCount;
    }
    ...
}

But now, calculating time required to paint all the houses becomes an involved process, as shown in the figure below.

House painters

Now that the land owner has taken responsibility to do this calculation, its implementation becomes slightly more complex:

class LandOwner
{

    private readonly IEnumerable<Painter> painters;
    private readonly int housesCount;

    public LandOwner(IEnumerable<Painter> painters, int housesCount)
    {
        this.painters = new List<Painter>(painters);
        this.housesCount = housesCount;
    }

    private float GetVelocity(Painter painter)
    {
        return painter.EstimateDaysToPaint(1);
    }

    private float GetTotalVelocity()
    {
        float sum = 0;
        foreach (Painter painter in this.painters)
            sum += 1  this.GetVelocity(painter);
        return 1  sum;
    }

    public void ManageHouses()
    {
        float daysToPaint = this.GetTotalVelocity() * this.housesCount;
        Console.WriteLine("Painting houses for {0:0.0} day(s).", daysToPaint);
    }

}

This implementation is a bit complicated but it works. What it cannot do is this. Suppose that one of the "painters" is the painting company which employs other painters. Even worse, painting company could employ another company along with its own painters. Land owner would then face multi-level hierarchy of painters. It would be very difficult for him to estimate time required to paint houses under such circumstances.

Implementing the Composite

Organizing the house painters could be made easy if only we could pull out the public interface of a painter:

interface IPainter
{
    float EstimateDaysToPaint(int houses);
}

This is the first way towards the composite painter. Land owner steps back from controlling a collection of painters and now controls only one abstract painter:

class LandOwner
{

    private readonly IPainter painter;
    private readonly int housesCount;

    public LandOwner(IPainter painter, int housesCount)
    {
        this.painter = painter;
        this.housesCount = housesCount;
    }

    public void ManageHouses()
    {
        float daysToPaint = this.painter.EstimateDaysToPaint(this.housesCount);
        Console.WriteLine("Painting houses for {0:0.0} day(s).", daysToPaint);
    }

}

This solution differs in only one letter compared to the first LandOwner class implementation shown above – this time, land owner just keeps reference to an abstract painter. What goes on under this abstract interface is of no interest to the land owner.

On the other hand, Painter class would remain the same as it is now, only it will implement the IPainter interface:

class Painter: IPainter
{
    ...
}

And here comes the real benefit. We are ready to define a composite element – concrete implementation of the IPainter interface which contains multiple contained elements implementing the same interface:

class PaintingCompany: IPainter
{

    private readonly IEnumerable<IPainter> painters;

    public PaintingCompany(IEnumerable<IPainter> painters)
    {
        this.painters = new List<IPainter>(painters);
    }

    private float GetVelocity(Painter painter)
    {
        return painter.EstimateDaysToPaint(1);
    }

    private float GetTotalVelocity()
    {
        float sum = 0;
        foreach (Painter painter in this.painters)
            sum += 1  this.GetVelocity(painter);
        return 1  sum;
    }

    public float EstimateDaysToPaint(int houses)
    {
        return this.GetTotalVelocity() * houses;
    }

}

Here is the implementation of the painting company. Remember the code from the LandOwner class when it used to control multiple painters? Well, that code has moved into this class. The difference is that painting company is now managing a number of abstract painters. Some of them could be real people, some others could be other painting companies subcontracted to help this company to complete work.

Conclusion

In this article we have seen one example of the Composite design pattern. Applying the Composite pattern has simplified the way in which classes are dealing with collections of objects. Working with the collection complicates code and adds a lot of thinking to the equation. When collection is conveniently wrapped into a class which only takes care of working with the collection, rather than working with its elements in terms of domain logic, we can greatly simplify the solution.

See also:

Published: Jun 16, 2015

ZORAN HORVAT

Zoran is software architect dedicated to clean design and CTO in a growing software company. Since 2014 Zoran is an author at Pluralsight where he is preparing a series of courses on object-oriented and functional design, design patterns, writing unit and integration tests and applying methods to improve code design and long-term maintainability.

Follow him on Twitter @zoranh75 to receive updates and links to new articles.

Watch Zoran's video courses at pluralsight.com (requires registration):

Making Your C# Code More Object-Oriented

This course will help leverage your conceptual understanding to produce proper object-oriented code, where objects will completely replace procedural code for the sake of flexibility and maintainability. More...

Advanced Defensive Programming Techniques

This course will lead you step by step through the process of developing defensive design practices, which can substitute common defensive coding, for the better of software design and implementation. More...

Tactical Design Patterns in .NET: Creating Objects

This course sheds light on issues that arise when implementing creational design patterns and then provides practical solutions that will make our code easier to write and more stable when running. More...

Tactical Design Patterns in .NET: Managing Responsibilities

Applying a design pattern to a real-world problem is not as straight-forward as literature implicitly tells us. It is a more engaged process. This course gives an insight to tactical decisions we need to make when applying design patterns that have to do with separating and implementing class responsibilities. More...

Tactical Design Patterns in .NET: Control Flow

Improve your skills in writing simpler and safer code by applying coding practices and design patterns that are affecting control flow. More...

Writing Highly Maintainable Unit Tests

This course will teach you how to develop maintainable and sustainable tests as your production code grows and develops. More...

Improving Testability Through Design

This course tackles the issues of designing a complex application so that it can be covered with high quality tests. More...

Share this article

webmasters