Irrlicht

Irrlicht is a 3D engine library that use many GRASP concepts, let’s discover with CppDepend the benefits of using this kind of patterns, and to show using of GRASP concepts we will focus on the namespace irr::gui.

GRASP Patterns

Creator

To detect with CppDepend the existence of creator, the easy way is to use a CQLinq query like this:

SELECT METHODS WHERE DepthOfCreateA “irr.gui.CGUISkin” == 1

So the CGUIEnvironement is the only class that create CGUISkin, but what about other GUI elements?
almost all GUIElement are created by CGUIEnvironement class except CGUIButton

SELECT METHODS WHERE DepthOfCreateA “irr.gui.CGUIButton” == 1

As we observe the CGUIButton is created in three different places, maybe there’s a reason for this behavior.
So the CGUIEnvironement class act as creator, but what’s the role of CDefaultGUIElementFactory class?
The dependency graph between CDefaultGUIElementFactory and CGUIEnvironement shows the relation between them:

Adding GUIElement begin with the invocation of CDefaultGUIElementFactory::addGuiElement , and this method invoke the right creation method from CGUIEnvironement, it depends of the type of element to create.
Finally the creation responsability and logic for GUI elements is isolated in two classes.

Controler

The Controler for GUIElements must at least manage event processing,this task is processed by CGuiEnvironement::OnEvent.
Let’s see which methods are used by OnEvent

SELECT METHODS WHERE IsUsedBy “irr.gui.CGUIEnvironment.OnEvent(constSEvent&)

So the event fired is processed by a class that implement IEventReceiver.

Which classes implement this abstract class?

SELECT TYPES WHERE DeriveFrom “irr.IEventReceiverORDER BY DepthOfDeriveFrom

What’s the other responsabilities of CGUIEnvironement?

As we have seen before this class create the concrete classes and also manage event processing, and to discover if it do another responsability we can search for methods used by this class:

SELECT METHODS WHERE IsDirectlyUsedBy “irr.gui.CGUIEnvironment

This class use also classes from irr::io namespace to persist and load into xml files , so maybe this class has many responsabilities and it can impact it’s cohesion, but it still tolerable because this class has all informations needed to persist data so this class follows the “Information Expert” principle of GRASP.

Low Coupling

Low coupling is desirable because a change in one area of an application will require less changes throughout the entire application. In the long run, this could alleviate a lot of time, effort, and cost associated with modifying and adding new features to an application.

Using abstract classes can improve the low coupling and we can evaluate the abstractness of a defined module by the following metric:


A = Na / Nc

Where:
* A = abstractness of a modulke Zero is a completely concrete module. One is a completely abstract module.
* Na = number of abstract classes in the module.
* Nc = number of concrete classes in the module.

The abstractness of Irrlich is equal to 0.1245972, and it contains 125 abstract classes.
And for irr::gui namespaces there’s 28 abstract classes , for each GUI element there’s the equivalent interface.

SELECT TYPES FROM NAMESPACESirr.guiWHERE IsAbstract

CppDepend provide DSM graph, and we can triangularize this matrix to focus under red borders highly dependent classes, and to detect modules.

As we can observe all abstract classes are grouped, so we can consider them as module, so we can isolated them in another namespace or maybe in another project.
And to see the benefit of using abstract class to improve the low coupling, let’s search for classes that use the concrete class CGUISkin.

SELECT METHODS WHERE IsDirectlyUsing “irr.gui.CGUISkin

Only one class know this class, it’s his creator.
And what about IGUISkin:

SELECT METHODS WHERE IsDirectlyUsing “irr.gui.CGUISkin

Unlike CGUISkin the IGUISkin class is visible and used by many classes.
What about coupling between namespaces:

A dependency cycle exist between namespaces, having this dependency can be not problematic but avoiding it enforce loose coupling, but what’s interesting than this dependency cycle is the manner that namespaces interact with each others.
When we choose to work with abstract classes, it’s recommended to interact between functional namespaces using abstract classes rather than concrete classes, except for namespaces that contains utility classes like irr::core.

Is Irrlich follow this rule?
For that let’s see what the namespace irr use as classes and methods.

SELECT METHODS WHERE IsDirectlyUsedBy “irr

As we can see almost all interaction with other namespaces pass by abstract classes, except for irr::video::CVideoModelList and irr::scene::CMeshBuffer.

Let’s discover the origin of the dependency with irr::video::CVideoModelList, for that we can execute the following CQLinq query:

SELECT METHODS OUT OF TYPESirr.video.CVideoModeListWHERE IsUsing “irr.video.CVideoModeList

so the class irr::CIrrDeviceWin32 use it because this class declare a field as video::CVideoModeList instead of video::IVideoModeList.

High Cohesion

The single responsibility principle states that a class should have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. The LCOM takes its values in the range [0-1]. The LCOMHS (HS stands for Henderson-Sellers) takes its values in the range [0-2]. Note that the LCOMHS metric is often considered as more efficient to detect non-cohesive types.
LCOMHS value higher than 1 should be considered alarming.

SELECT TYPES WHERE LCOMHS > 0.95 AND NbFields > 10 AND NbMethods >10 AND !IsGlobal ORDER BY LCOMHS DESC

Only few classes are considered as no cohesive.

Conclusion

Irrlicht use namespaces to modularize the code base and absract classes to improve low coupling, so it makes it very easy to understand and maintain.
It’s a good example to follow if you want to improve your design quality.

OGRE3D internal design

OGRE (Object-Oriented Graphics Rendering Engine) is a scene-oriented, flexible 3D rendering engine written in C++ designed to make it easier and intuitive for developers to produce applications utilising hardware-accelerated 3D graphics. The class library abstracts the details of using the underlying system libraries like Direct3D and OpenGL and provides an interface based on world objects and other high level classes.

Let’s analyze analyze it with CppDepend to discover its design advantages.

OGRE3D Architecture

The following dependency graph shows the dependencies between the OGRE3D projects:

As we can observe the architecture is based on the plugin concept, its very useful to extend Ogre3D without any changes to the kernel project OgreMain which  provides needed classes to use for a plugin, for example we can see which classes of OgreMain are used by RenderSystem_GL plugin. For that we can execute the following CQLinq request:

SELECT TYPES WHERE IsDirectlyUsedBy “RenderSystem_GL

For a plugin it’s also very useful to know which abstract classes are used by it, it give us an idea about the contracts implemented or used by the plugin.

SELECT TYPES WHERE IsDirectlyUsedBy “RenderSystem_GLAND IsAbstract

Which paradigm is mostly used?

Lets search for global functions to discover if Ogre3d is Procedural oriented?

SELECT METHODS FROM PROJECTS “OgreMain” WHERE IsGlobal AND !NameLike “^operator

How about generic paradigm:

SELECT TYPES WHERE IsTemplate

Only few classes are templated so Ogre3d is mostly object oriented.

Inheritance

Using object oriented approach can implies an overuse of inheritance to exploit polymorphism concept.

Specially for OgreMain that represent the kernel of Ogre3d framework, where many classes are designed to be overloaded by plugin projects.

SELECT TYPES FROM PROJECTS “OgreMainWHERE NbBaseClass >0

But multiple inheritance increase complexity ,and we have to use it carefully.

Let’s search for class with many base classes.

 

Only few classes derived from more than one class.

Abstractness

For a plugin oriented architecture, the host must have many abstract classes to be more flexible and extensible.

Lets search for abstract classes of OgreMain:

SELECT TYPES FROM PROJECTS “OgreMainWHERE IsAbstract

Namespaces layering

CppDepend provides DSM graph, and we can triangularize this matrix to focus under red borders highly dependency cycle.

A dependency cycle exist between Ogre,Ogre::EmitterCommands and Ogre::OverlayElementCommands, having this dependency can be not problematic but avoiding this kind of dependency enforce loose coupling,this interesting post explain the benefit of layering.

Let’s search for the origin of dependency between Ogre and two other namespaces.

SELECT TYPES WHERE IsDirectlyUsedBy “Ogre

The namespace Ogre use all Cmd classes from the other namespaces, and all those classes are used by Ogre::ParticleEmitter as static fields, ParticleEmitter add them to CmdParam dictionary.

Maybe its possible to do it differently to avoid this dependency cycle but its not very problematic.

Type cohesion

The single responsibility principle states that a class should have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. The LCOM takes its values in the range [0-1]. The LCOMHS (HS stands for Henderson-Sellers) takes its values in the range [0-2]. Note that the LCOMHS metric is often considered as more efficient to detect non-cohesive types. LCOMHS value higher than 1 should be considered alarming.

SELECT TYPES WHERE LCOMHS > 0.95 AND NbFields > 10 AND NbMethods >10 AND !IsGlobal

Only few classes are considered as no cohesive.

Design patterns used

Singleton

To ensure a class has only one instance, the better way is to use singleton pattern.

Lets search which classes are singleton:


SELECT TYPES WHERE
DeriveFrom “Ogre.Singleton

As we can observe that almost all manager classes are singleton,in general a manager is a good candidate to be singleton. And we can search for manager classes that not derived from Singleton

SELECT TYPES WHERE !DeriveFrom “Ogre.SingletonAND NameLike “Manager$

Only few managers not derived from Singleton, its normal because those classes can be instantiated many times.

Factory

The factory pattern is very useful to abstract the creation of objects,it enforce low couplig and high cohesion as explained in this post.

but having factory not implies that an instance is created by this factory, because we can instantiate the class directly, and we have to define a rule to be sure that the factory is used for instantiation.

For example about Entity class we can define the following rule to discover each class instantiate it:

SELECT METHODS WHERE DepthOfCreateA “Ogre.Entity” == 1

As we can observe only EntityFactory instantiate the Entity class.

Manager

Manager classe give access to a subsystem, is very useful to modularise the project, and Ogre3d contains many managers, each one represent a different subsystem.


SELECT TYPES WHERE
NameLike “Manager$

Facade

Facade defines a higher-level interface that makes the subsystem easier to use, and we can detect facade for your project by using Efferent Coupling metric. The Efferent Coupling for a particular type is the number of types it directly depends on. Types where TypeCe > 50 are types that depends on too many other types. They are complex and have more than one responsability. They are good candidate for refactoring.

But sometimes in the case of facade, having a high TypeCe can be normal.

Lets search for classes with high TypeCe:

SELECT TYPES ORDER BY TypeCe DESC

Not all of those classes are facade and for each class of them we can maybe find an explanation why TypeCe is high, and maybe some classes can be refactored, and i confess that i don’t master Ogre3d to explain that.

And the facade mostly used is Root class, let’s see what subsystem this class use.

As we can observe Root class use almost all manager classes.

And we can search for manager not accessible by Root by this following CQLinq query:

SELECT TYPES WHERE !IsDirectlyUsedBy “Ogre.RootAND NameLike “Manager$AND !IsAbstract

Observer

The observer is a pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement event handling systems.

Ogre3d use Listener classes to implement the observer pattern.

Let’s search for Listener classes for OgreMain project.

SELECT TYPES FROM PROJECTS “OgreMainWHERE NameLike “Listener$

Conclusion

Ogre3d is very clean as library, very well designed and very well commented.You can easily understand the utility of design patterns used, and its modularity can help you to accelerate the time of learning its capabilities.