An Approach to Effectively Applying Software Design Patterns
You’ve thought long and hard about whether your design really needed that design pattern you chose and you then proceeded to think increasingly harder about the validity of the pattern you chose given the context you’re applying it in. Now it has come time for the final question. How do I effectively apply this design pattern? I say effectively because even a well chosen pattern can be poorly implemented.
Question the design pattern’s reason for inclusion
This is just a quick check to indeed make sure you have chosen the pattern wisely and are sure it is going to fit into your design nicely. The worst thing you can do is force a pattern into your design when it has no reason being there.
Source some information on the pattern if you are not particularly well versed in the particular pattern you have chosen. Get an idea of its intentions and its consequences and try and picture the types of problems it might be well suited to. Is your problem in the list? If so, read on. Otherwise, back to How to Choose a Design Pattern That Fits.
What changes will the design pattern produce?
You need to make sure you have fully considered the implications of augmenting your design with the chosen pattern. Sometimes whilst you can see the pattern will solve an immediate need you have in your design, it can also produce (what may have been at the time) seemingly unobvious side-effects. So take a few minutes to note down all the changes you can see taking effect.
State the reason for inclusion
This list is where you put all the benefits the pattern will produce. This might be increased flexibility, better long term maintenance, increased simplicity, better structure etc.
Observe the impact
This list is where you note down the impact points on the rest of the system. So ask yourself what will be impacted by the change. The implementation might mean adding a lot of classes (relative to the rest of the system), creating a new abstraction, modifying old ones etc. Does it change the way other parts of the system will perform any of their tasks? The goal here is to make sure you are aware of, and have considered the implications the design change will have on the rest of your existing design.
Example:
You have a problem that you considered using a Strategy Pattern for (to factor out the part that differs) where each strategy uses a Visitor (to allow operations of functionality to be added to the visitable objects independent of the object). What if there is only a handful of classes actually doing anything. Is it worth doubling the amount of classes? Or even, is it worth applying more classes in the pattern than exist in the rest of the design? These are all questions you need to be asking yourself when looking at the impact list.
The aforementioned example of a large increase in classes brought about by the design pattern may be subtly reminding you that the pattern usage is just overkill. If you have more classes in the pattern than you do in the rest of the system you might be being a little too ambitious. This is a classic case of too much flexibility. What happens if you never need to use this flexibility? You just have a bigger and larger framework for a small system that may never have needed it.
Choose appropriate names to model the pattern
When you start creating the appropriate abstractions, classes and interfaces don’t use the generic names used by the Pattern. Use names that better fit the domain you are working in but also describe the pattern such that when you look at certain domain objects, you can tell how they fit together. If for example you were using a Strategy pattern and you were implementing a various types of checking you might use a name like CheckStrategy instead of just Strategy. Instead of using a generic Context class that has many strategies, consider using a more meaningful name like CheckingContext or CheckerContext.
Choose application specific names to model operations
Similarly, you want to choose appropriate names for the operations they model. In the aforementioned checking example you might consider a check method rather than something generic like execute.
If you were too look at a context object and strategy interface and discovered the strategy interface had an execute method you would gain little understanding of what is being modeled. About the only piece of information you may glean is the type of pattern employed. Whereas something like a CheckerContext, CheckStrategy and check method is much more descriptive. You should be able to infer it is a strategy pattern for supporting different types of checking scenarios.
In short, be aware of the full implications a design pattern will impose on the rest of your design before you include it; this means listing benefits and gains to ensure you have the complete picture. When implementing abstractions, classes and interfaces choose meaningful names that match the application domain. Similarly for the methods the classes and interface realise. By understanding how the design pattern fits into your pre-exisiting design and tailoring it to this design, you minimise any unforeseen problems cropping up in the future.

Leave a Reply