Investigating Strategies For Functional Decomposition
- by Liam McLennan
Introducing Functional Decomposition Before I begin I must apologise. I think I am using the term ‘functional decomposition’ loosely, and probably incorrectly. For the purpose of this article I use functional decomposition to mean the recursive splitting of a large problem into increasingly smaller ones, so that the one large problem may be solved by solving a set of smaller problems. The justification for functional decomposition is that the decomposed problem is more easily solved. As software developers we recognise that the smaller pieces are more easily tested, since they do less and are more cohesive. Functional decomposition is important to all scientific pursuits. Once we understand natural selection we can start to look for humanities ancestral species, once we understand the big bang we can trace our expanding universe back to its origin. Isaac Newton acknowledged the compositional nature of his scientific achievements: If I have seen further than others, it is by standing upon the shoulders of giants The Two Strategies For Functional Decomposition of Computer Programs Private Methods When I was working on my undergraduate degree I was taught to functionally decompose problems by using private methods. Consider the problem of painting a house. The obvious solution is to solve the problem as a single unit: public void PaintAHouse()
{
// all the things required to paint a house
...
}
We decompose the problem by breaking it into parts:
public void PaintAHouse()
{
PaintUndercoat();
PaintTopcoat();
}
private void PaintUndercoat()
{
// everything required to paint the undercoat
}
private void PaintTopcoat()
{
// everything required to paint the topcoat
}
The problem can be recursively decomposed until a sufficiently granular level of detail is reached:
public void PaintAHouse()
{
PaintUndercoat();
PaintTopcoat();
}
private void PaintUndercoat()
{
prepareSurface();
fetchUndercoat();
paintUndercoat();
}
private void PaintTopcoat()
{
fetchPaint();
paintTopcoat();
}
According to Wikipedia, at least one computer programmer has referred to this process as “the art of subroutining”.
The practical issues that I have encountered when using private methods for decomposition are:
To preserve the top level API all of the steps must be private. This means that they can’t easily be tested.
The private methods often have little cohesion except that they form part of the same solution.
Decomposing to Classes
The alternative is to decompose large problems into multiple classes, effectively using a class instead of each private method. The API delegates to related classes, so the API is not polluted by the sub-steps of the problem, and the steps can be easily tested because they are each in their own highly cohesive class. Additionally, I think that this technique facilitates better adherence to the Single Responsibility Principle, since each class can be decomposed until it has precisely one responsibility. Revisiting my previous example using class composition:
public class HousePainter
{
private undercoatPainter = new UndercoatPainter();
private topcoatPainter = new TopcoatPainter();
public void PaintAHouse()
{
undercoatPainter.Paint();
topcoatPainter.Paint();
}
}
Summary
When decomposing a problem there is more than one way to represent the sub-problems. Using private methods keeps the logic in one place and prevents a proliferation of classes (thereby following the four rules of simple design) but the class decomposition is more easily testable and more compatible with the Single Responsibility Principle.