Patterns: Factory and Decorator, a powerfull combination
IntroductionWhile working on a system that distributes passwords throgh different communication channles (email, SMS, snailmail etc) I noticed that the sloppy programmer (hired consultant ofcourse) had written the system with no audit logging what-so-ever.
Since the system had a fair general design, I figured out that adding logging, transparently to clients, without having to change the implementation should possible.
The old designAn interface called Distributor, and a set of implementations are the main point of interest. The Distributor interface declares one method called distribute. All concrete distributors implement this method.
A DistributionFactory class creates implementation clases based on an input parameter holding customer information. The business rules are not really important in this case so I've removed the argument to the constructor for simplicity
Exploring alternatives The easiest way out here is of course to just go ahed and implement audit logging in all the Distributor implementations. Of course, this would be a violation of the DRY principle.
At one point I was tempted to create a Template. This would require me to create an abstract base class for all distributors with an abstract method called handleDistribution (or something similar)
I would then have had to modify all distributors and rename the method that implemented the functionality to
handleDistribution, and of course implement the distribute (in the abstract base class) so that it would first call handledistribution, and then do logging later
The total impact of this would have been
- No Change in the factory class, that's great!
- Code changes in all distributors. Modify to extends the parent class. Not so great!
- Rename of one method in every distributor. Not so great!
- Create one new abstract base class with the template code. Not so great!
Using a Decorator
A real elegant solution is introducing a Decorator. The Decorator class conforms to the exising Distributor interface. The Decorator in this case is called LoggingDistributor
The LoggingDistributor takes a Distributor as an argument in the constructor, and impelments the distribute method by delegating the method calls to that actual instance. After
the delagation, it logs the event.
Conclusion
I was very satisified with this design, since it enabled me to change the system with minimal impact on the existing code. Only the Factory class is modified, and only one class added.
I think this is a great example, outside the GUI world, on how the Decorator and Factory pattern together can be very usefull in adding functionality to an existing system causing a minimal amound of modification.
source code available here