The majority of developers have already heard about the design patterns, GOF(Gang Of Four) patterns are the most popularized, and each developer has his way to learn them , we can enumerate:
- Reading a book.
- From web sites.
- From a collegue.
- Doing a training.
Regardless of the method chose, we can learn by heart the patterns and spent hours to memorize their UML diagrams, but sometimes when we need to use them in a real project, it becomes more problematic.
What’s very important is not to know exactly the pattern names and how to implement them, but what’s more relevant is the motivation behind each pattern, it’s from motivations that we invent the patterns.
As Bjarne Stroustrup points out, “C++ is a multi-paradigmed language.” It supports many different styles of programs, or paradigms, and object-oriented programming is only one of these. Some of the others are structured programming, and generic programming. In the last few years C++ experts like Andrei Alexandrescu, Scott Meyers and Herb Sutter promotes the uses of the generic programming and they qualify it as Modern C++ Design.
In the C++ world some developers are anti OOP, others hate the generic programming and some of them mix between them.
Here are some assertions from the STL creator Alexander Stepanov:
“I find OOP technically unsound.”
“I find OOP philosophically unsound.”
“I find OOP methodologically wrong.”
What’s really the big issue of the OOP approach?
Maybe the big drawback of using the OOP approach is that you introduce easilly a high coupling between your classes and methods. Think about the inheritence which is overused in OOP, it introduces a very high coupling. However even the composition introduces a high coupling. To discover that let’s compare the implementation of a class calculating a tax using OOP and generic programming.
What the CTaxCalculator class implementation tell us exactly?
I’m the class CTaxCalculator, I know how to calculate the tax but I collaborate only with classes of ICalculator kind. I refuse to colaborate with any other not ICalculator class even if it can help me to calculate the Tax.
The CGenericTaxCalculator class in the other side know how to calculate the tax , and for that it can collaborate with any type capable to calculate the tax, and it’s not aware about its kind.
The GOF patterns to the rescue:
Do you asked yourself why almost all the GOF patterns introduce the abstract classes and the interfaces? The short answer is to fight the high coupling introduced by the OOP approach.
The goal of the GOF patterns is not only to improve the design quality of your code base, but also to resolve some OOP drawbacks.
The Abstract factory case study:
Let’s discover the benefits of using the abstact factory pattern and let’s start by its common definition :
The Abstract Factory Pattern provides an interface for creating families of related objects without specifying their concrete classes. The Abstract Pattern makes sure that the client does not know anything about the objects being created. The pattern hides the implementation of the product definitions and their class names and the only way the client can create a product is through the factory.
But what’s the role of this pattern in a concrete code base, it’s worth to use it? or it just complicate the code more than it makes it more readable and maintainable.
In this post we will try to discover visually its benefits using the dependency structure matrix and the dependency graph. For that we analyze the following example where we have a code source before and after the refactoring to introduce the abstract factory pattern.
CppDepend provides an interesting feature to compare between two version, in our case we compare the project before and after the refactoring and here’s the result.
After introducing the pattern three classes were added(Factory,MotifFactory an WindowsFactory), they are characterized by the Bold Style in the class browser.
There are also some changes concerning the dependencies between the classes as we can see in the dependency matrix.
Until now the result is that we have more lines of code and more classes when introducing the pattern.
The Pattern Benefits
Where the WindowsButton class is created?
To answer to this question let’s execute the following CQL request to quey the code base:
SELECT METHODS WHERE CreateA “WindowsButton” ORDER BY DepthOfCreateA
And here are the methods creating it before the refactoring:
And the ones creating it after introducing the pattern:
Before the refactoring, the class is created in many places, but after the refactoring the creation is isolated in WindowsFactory.
The responsibility of the creation is now isolated and it impact the class cohesion, so we have a high cohesion than before. Indeed the logic of the creation is now managed by a specific class, and it become easy to change this logic if we need to.
What about the coupling?
Using the DSM can help to detect the coupling between the projects and the classes.
If you look at the intersection between the main() method and the MotifButton class we observe a red “-“sign, what indicate tha we lose a dependency between main() and the other classes WindowsButton and MotifButton. However a dependency was introduced between the main() method and the factory classes.
Let’s use the dependency graph to discover what happens to the dependencies:
Before the refactoring:
The FactoyGlobalMembers which represent the global methods including the main() method uses directly the Gui widgets (Button, Menu, …)
And after the refactoring:
The global methods are now isolated form the concretes classes used, we introuce a low coupling so we can add another family of classes without changing the client using them, only the factory will be changed.
The abstract factory pattern is not only a pattern which is good to know when you have an interview. But it improves the cohesion and the coupling of your classes.
The GOF patterns are very useful to fight the high coupling introduced by some OOP mechanisms and the improve the design quality of your code base to make it more readable and maintainable.