- If the person reading the code is aware of design patterns and their concept and is able to identify design patterns in practical use (not just the book examples) then they really do reduce the complexity.
- In the first approach, if you suddenly have more classes or code to go through to solve what seems to be a simple problem at first. If you’re not aware of the benefits of design patterns, hacked solutions always look better.
- Design patterns often lead to additional levels of abstraction around a problem, and if not handled correctly then too much abstraction can lead to complexity.
- However, since design patterns provide a common vocabulary to communicate ideas they also reduce complexity and increase maintainability.
- There’s an infamous disease known as “Small Boy With A Pattern Syndrome” that usually strikes someone who has recently read the GoF book for the first time and suddenly sees patterns everywhere. That can add complexity and unnecessary abstraction.
- In the short term, design patterns will often increase the complexity of the code. They add extra abstractions in places they might not be strictly necessary. However, in the long term they reduce complexity because future enhancements and changes fit into the patterns in a simple way. Without the patterns, these changes would be much more intrusive and complexity would likely be much higher. Take for example a decorator pattern. The first time you use it, it will add complexity because now you have to define an interface for the object and create another class to add the decoration. This could likely be done with a simple property and be done with. However, when you get to 5 or 20 decorations, the complexity with a decorator is much less than with properties.
- To be specific, many patterns increase accidental complexity of a design by introducing new structures (interfaces, methods, etc.) that weren’t present in the design before the pattern was applied.
- Let’s use Visitor as an example.
Visitor is a way of separating operations from an object structure on which they operate. Before the solution with Visitor, the operations are hard-coded into each Element of the object structure. The challenge for the developer is that adding new operations involves modifying the code in the various elements.
After the application of the Visitor pattern, there is an additional class hierarchy of visitors, which encapsulate the operations. The flow of control in the solution is definitely more complex, and will be harder to debug (anyone who has implemented Visitor and tried to follow the program flow of double-dispatched calls with accept/visit will know this).
Understanding and maintaining Visitor functions in terms of cohesive units is less complicated than the alternative of coding functions into each of the Elements in the fixed structure that is visited. This is the benefit of the pattern.
It’s difficult to say quantitatively how much increase there is in accidental complexity or how much easier it is to add new operations. I certainly don’t agree with answers that make a blanket statement saying in the long-term, complexity is reduced with applying a pattern. It’s not like your design “forgets” the double-dispatch added by Visitor’s approach, just because you have code which more easily allows operations to be added. The complexity is a price (or tax) you pay to get the benefit in maintainability.
Patterns still have to be applied
Regardless of one’s supposed familiarity with patterns, any given pattern must be applied to a solution. That application is going to be different every time (Martin Fowler said patterns are only half-baked solutions). Developers will always have to understand what classes are playing what roles in the existing design, which is subject to the essential complexity (the application problem’s complexity) that is often non-trivial.
In the best case, understanding a design pattern applied in an application that’s already complex may be trivial, but it’s not 0 effort:
- Patterns aren’t always applied the same way. There are many variants of patterns — Proxy comes to mind. I’m not sure that everyone agrees about how any given pattern should be applied.
- Introducing one pattern (e.g., Strategy to encapsulate algorithms) often leads to other patterns to manage things properly (e.g., Factory to instantiate the concrete Strategies).
- Introducing a pattern often leads to more responsibilities. Object cleanup when a Factory is used is not trivial (and also not documented in GoF). How many know about the so-called Lapsed-listener problem?
- What happens if there is a change in the assumptions made about the need for the pattern (e.g., there is no longer a need to have multiple encapsulated algorithms provided by the Strategy pattern)? It’s going to be extra work to remove the pattern later. If you don’t remove it, new developers could be duped by its presence when they come on board. Patterns are intertwined between the classes playing the roles in the pattern. Removal is not trivial.
Erich Gamma gave an anecdote at ECOOP 2006 that designers in one case decided to remove the Abstract Factory pattern from a commercial multi-platform GUI widget framework (the classic Abstract Factory example!). It turned out that the multiple-levels of indirection (polymorphic calls) in complex GUIs was a significant performance hit in the client code. Customers complained about GUIs being sluggish, and the “optimization” was to remove the indirections. In this case, performance trumped maintainability; the pattern was only making the coders happy, not the end users.
Revolving door metaphor
Using buildings as a metaphor, let’s consider a revolving door as a building design pattern. The following image comes from Wikipedia:
You can “see” the additional complexity in such a door. The benefits of revolving doors are in energy savings. They attempt to solve the problem where there are people frequently going in and out of a building, and opening/closing a standard door allows too much air to be exchanged between the inside the outside of the building each time.
It probably wouldn’t make sense to install a revolving door as the entrance of a two-bedroom house, because there is not enough traffic to justify the additional complexity. The revolving door would still work in a two-bedroom house. The benefits in terms of energy savings would be small (and might actually be worse because of size and air-tightness relative to a conventional door). The door would surely cost more and would take up more space than a traditional door.
- Design Patterns work like algorithms for Object Oriented Programming. Shows you how to put together objects in a meaningful relationship that performs a particular task. So I would say yes they reduce complexity by allowing you to understand the design of the software better. The same way with algorithms in procedural programs. If I told you that X was using a Linked List with a Bubble Sort it would be a lot easier to following along with what the programmer was doing.
The Effect of Design Patterns on Object-Oriented Metrics and Software Error-Proneness
- In the literature, design patterns are mainly known as reusable solutions for different problem contexts. But, application of design patterns has different effects on software. One of them is expected to be the effect on software error-proneness.
- The effects of design patterns on software are measurable and therefore interpretable.
- Design pattern implementations and refactorings in software are highly recommended. Also, design patterns have positive effects on design, and increase reusability, analyzability, and so on.
- But, design patterns may introduce side effects like increase in error-proneness.
- Conclusion from a thesis from Barış Aydınöz : software error-proneness related OO metrics have been determined and reviewed. Moreover, design patterns that affect these metrics have been found out, and their effect on metrics have been shown empirically on individual experiments. 13 different cases have been refactored with design patterns, and fluctuations on metrics have been evaluated mainly from the perspective of the software errorproneness. Also, in this thesis, [GOF98] patterns have been researched and the effects of these patterns on OO metrics have been measured with refactorings…. …software error-proneness related OO metrics have a significant impact on software development cycle. Since, by using coupling and inheritance metrics, very accurate models can be derived to guess error-prone classes in software [BDPW98-2]. These models will obviously help developers to reduce software testing phase of the development cycle. Design patterns, as stated before, are elegant and reusable solutions to common occurring design problems. But, this study showed that design patterns may have many effects on software quality.
- In conclusion, in this thesis study, the relations between software error-proneness associated metrics and design patterns have been investigated. Also, this study emphasizes the importance of design patterns, the lack of standard metric analysis tools, and the lack of standard ways for hatching design patterns.
- Other article : https://accu.org/index.php/journals/1622