The majority of developers have already heard about 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 or a magazine.
- From web sites.
- From a collegue.
- Doing a training.
Regardless of the method choosed, 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 exacly pattern names and how to implement them as described in the documentation, but what’s more relevant is the motivitations behind each pattern, because it’s from motivations that we invent the patterns.
And to master better pattern motivations, the interesting way is to study them from a real project. it’s the goal of this article , we will try to discover an open source project using them heavily.
Analysis of Rigs of Rods
Rigs of Rods (“RoR”) is an open source multi-simulation game which uses soft-body physics to simulate the motion and deformation of vehicles. The game is built using a specific soft-body physics engine called Beam, which simulates a network of interconnected nodes (forming the chassis and the wheels) and gives the ability to simulate deformable objects. With this engine, vehicles and their loads flex and deform as stresses are applied. Crashing into walls or terrain can permanently deform a vehicle.
Here we go to dicover some GOF design patterns used, for that we used CppDepend to analyse RoR, and CQLinq will be helpful to request the code base.
Singleton
The singleton is the most popular and the most used one. RoR uses generic singleton to avoid repeating the same code for each singleton class, and define two variants: a singleton that create the instance , and another where the instance is assigned.
Let’s search for all RoR singletons using the following CQLinq query.
from t in Types where t.DeriveFrom(“RoRSingletonNoCreation“) || t.DeriveFrom(“RoRSingleton“)
select t
Motivation:
Let’s take the example of InputEngine singleton, RoR needs to store information about keyboard, mouse, and joyticks, which are detected in the initialisation by the InputEngine class, all classes needs the same data of the input devices, and no need to create more than one instance, so the primary motivation is to “Create one instance of InputEngine class“.
To acheive that we can declare it as global variable or define it as singleton, however using singleton became controversial, and not all architects and designers recommend it, here’s an article talking about the singleton controversy.
Factory Method
There is no mistery about factories, their goal is simple: Create instances, and a simple factory containing a CreateInstance method could achieve this goal, however RoR use “Factory Method” pattern for all its factories instead of using a simple factory.
Motivation:
To understand better this pattern let’s describe the scenario where RoR use this pattern:
- RoR use the graphics engine OGRE, and some classes of OGRE needs to instantiate ParticleEmitter class.
- RoR defines and uses another class BoxEmitter that inherits from ParticleEmitter, and wants that OGRE uses this new class as ParticleEmitter.
- OGRE doesn’t know anything about RoR.
The question is how OGRE will know how to instantiate this new class BowEmitter from RoR and use it?
Here come the role of “Factory Method” pattern:
OGRE has an abstract class ParticleEmitterFactory who has the CreateEmitter method, and to acheive his job , OGRE needs to a concrete factory, RoR defines a new factory BoxEmitterFactory inheriting from ParticleEmitterFactory and overloads CreateEmitter method.
RoR gives to OGRE this factory using ParticleSystemManager::addEmitterFactory (ParticleEmitterFactory * factory). And each time OGRE needs an instance of ParticleEmitter, the BoxEmitterFactory is invoked to create it.
The most important motivation is the low coupling, indeed OGRE doesnt know anything about RoR and it can instatiate classes from it.
Using a simple factory is interesting to isolate the logic instantiation , but using “Factory Method” is more suitable to enforce low coupling.
Template Method
Template method defines the skeleton of Algorithm in a method, differing some steps to subclasses. Template method lets subclasses redefine some steps of an algorithm without changing the algorithm’s structure.
The objective is to ensure that algorithm’s structure stays unchanged, while subclasses provide some part of implementation.
Let’s use CQLinq to detect all classes using template method pattern, For that we can search for methods in a parent class where it use a pure virtual methods, and has subclassses implementing these virtual classes.
from t in Types where t.IsAbstract && t.Methods.Where(a=> a.NbLinesOfCode>0 && a.MethodsCalled.Where(b=>b.IsPureVirtual && b.ParentType==t).Count()>0).Count()>0 select t
Motivation:
Let’s take as example IRCWrapper, the method process implement template method pattern, it invoke the pure virtual method processIRCEvent.
LobbyGui is a class that needs to process IRC events,it inherits from IRCWrapper, the logic of processing IRC events received is centralized in the IRCWrapper::process, however for each event received the lobbygui must treat it, for that reason, the process method invoke processIRCEvent which is overloaded by the LobbyGui class.
With this pattern, we can easilly change the implementation of algorithms without changing the skelton, it enforce low coupling, because the client could reference only the abstract class instead of concrete ones.
Strategy
There are common situations when classes differ only in their behavior. For this cases is a good idea to isolate the algorithms in separate classes in order to have the ability to select different algorithms at runtime.
Let’s use CQLinq to detect all classes using strategy pattern, For this purpose we can search for abstract classes, having multiple derived classes, and where the client reference abstract class instead of concrete implementations.
from t in Types where t.IsAbstract && t.DirectDerivedTypes.Count()>1 && !t.IsThirdParty
let tt=t.DirectDerivedTypes
from db in tt where db.Methods.Where(a=>a.NbMethodsCallingMe!=0 && !a.IsStatic).Count()==0
select new {db,t}
Motivation:
The camera could have multiple behaviors: fixed, free, static or isometric, and this behavior could be changed dynamically. and other behaviors could be added in the future.
The CameraManager use the behavior abstraction IBehavior, here’s all CameraManger methods using IBehavior class.
As we can observe, there is a method named switchBehavior to change the behavior dynamically.
This pattern enforce low coupling, CameraManger doesnt know the concrete behaviors, and also enfore high cohesion, because each specific behavior is implemented in a isolated class.
State
State pattern is similar to the Strategy Design Pattern from an architectural point of view, and for this reason for the previous CQLinq request when we searched for strategy pattern, we found also state classes.
But the goal is different, the Strategy pattern represents an algorithm that uses one or more IStrategy implementations.and there’s no correlation between these different behavior, however for State pattern we pass from one state to another to acheive the final objective, so there’s a cohesion between the different states.
Here’s all state classes inheriting from the abstract class AppState
Like Strategy pattern, only the abstract class is reference by other classes, here’s all methods using AppState.
As we can observe, AppStateManager contains many methods to manage the state lifecycle.
Motivation:
Like Strategy patern, this pattern enforce low coupling, AppStateManger doesnt know the concrete states, and also enforce high cohesion, because each traitement is isolated in it’s corresponding state.
Facade
A facade is an object that provides a simplified interface to a larger body of code, such as a class library. And to detected facades used the simple way is to search for external code used.
Here’s all namespaces used by ROR project:
let’s take as example the Caelum and search for classes using it from ROR
from m in Methods where m.IsUsing (“Caelum“)
select new { m }
Only SkyManager use directly Caelum namespace, so it represent the Caelum facade.
What about OIS namespace:
from m in Methods where m.IsUsing (“OIS“)
select new { m }
mainly InputManager use OIS namespace.
Motivation
If we use an external library and this highly coupled with our code, ie many classes use directly this library, it will be very difficult to change the external library, however if a facade is used, only its implementation will be changed if we want to change the external library.
So this pattern enforce low coupling with external libraries.
Domain Driven Design approach
The Domain Driven Design that is a software design approach, based on the two premises:
-That complex domain designs should be based on a model, and
-That for most software projects, the primary focus should be on the domain and domain logic (as opposed to the particular technology used to implement the system).
In other words the heart of the DDD is Model and the first thing to do when starting development is drawing the model. Model and design you create should shape each other. Model should represent knowledge of the business.
RoR use this approch and isolate all data into structures, and to check that, let’s search for all ROR structures:
from t in Types where t.IsStructure && t.Methods.Where(a=>!a.IsGeneratedByCompiler).Count()==0 select t
The treemap view show us all the code impacted by this query, the blue rectangles represent the result of the CQLinq query
Conclusion
Using design patterns has many advantages, but without understanding their motivations is very difficult to implement them, and as we discovered in this article motivations like low coupling and high cohesion are present in almost all design patterns, for this reason it’s recommended to discover also patterns focused more on motivations like GRASP to complete our understanding of GOF. Here’s the Irrlicht case study To discover more GRASP patterns.
Filed under:
C++,
CodeProject