Friday 23 September 2011

In The Red Corner Inheritance, In The Blue Corner Composition, Round 2

My last blog post talked about how to use composition instead of inheritance. There is another design that takes the previous lesson from the last blog one step further. Thanks for this one go to Kevin Thomas.

The Problem
When I was working on CLS, there was a pair of products that we needed to support, NDF and OPR. CLS had a specific design requirement to be a framework for further products to be inserted into the system.

NDF was the first product to be implemented, and had two search screens, one for matched trades and another for unmatched trades. Matched trades allowed you to search against the matching partner, and the date on which the match was made, etc.

The team set about implementing the screens and we ended up with three classes:
  • SearchForm – this class was responsible for collecting the data from the UI and making a query available to the framework to search for the trades. This is the super class for the following two classes.
  • MatchedSearchForm – The matched trades form had some extra fields for the matching partner, and match time. The object provided a customised query for these fields.
  • UnmatchedSearchForm – The unmatched trades form also had extra fields and supplied a customised query for these.

This worked quite well for the NDF release, however when we came to the seconds release the implementation spawned a new classes and renamed the existing classes to:
  • SearchForm
  • OPRMatchedSearchForm
  • NDFMatchedSearchForm
  • OPRUnmatchedSearchForm
  • NDFUnmatchedSearchForm

Just as in the design from the last blog post, there are multiple responsibilities in each of these objects. Specific responsibilities are the NDF search, OPR search, matched search, unmatched search.
This lead to a similar set of problems with the number of classes growing with each product added. 

The Solution
Unlike the previous solution, the interface changes for the storage of data into the forms. So the solution was to use composition to break up the SearchForm into a container that contained a ProductSearchForm and a MatchStateSearchForm. These two were the start of hierarchies that had a single responsibility each. The refactored solution looked like this:


One important point to note is that the criteria generation is now split between two classes, MatchingStatus and Product, and are combined by the SearchForm. We can do this using an object oriented criteria such as Hibernate Criteria. As it was some years ago, I have given a solution which does not quite match the real system.

So you are probably wondering, did we not make the system more complex by doing this, as we have actually increased the number of classes? We have increased the number of classes, however:
  • The number of operations per class has gone down, because each class has only a single responsibility, making each class easier to understand.
  • There are no combinations of responsibilities to understand, or generate weird interaction defects. This was something that was experienced in the real system, and is not particularly apparent from the class diagrams above.
In summary, this was an easier solution to understand and maintain.

The Behaviours
By understanding the responsibilities of each class at a high level, the designer gives themselves the big picture. Once they understand the big picture they can:
  1. Start to look for good aspects of the solution and bad smells.
  2. Once these have been identified, the candidate solutions are generated,
  3. And the pros and cons of each solution can be weighed up to find the best solution, or to combine candidates to generate better candidates.

No comments:

Post a Comment