Virtual Developer Workshop: Containerized Development with Docker
The MFC framework is a powerful approach but using it impacts the design of the application, it's very intrusive and we have to be careful of how we use it. Let's analyze MFC8 with CppDepend to discover its code quality and design.
CppDepend is a tool that simplifies managing a complex C++ code base. Architects and developers can analyze code structure, specify design rules, and plan massive refactoring, do effective code reviews and master evolution by comparing different versions of the code. CppDepend supports the Code Query Language (CQL) for maximum flexibility.
Basically CppDepend considers your code as a database and you can write some CQL statements to query and check some assertions on this database. As a consequence, CQL is similar to SQL and supports the SELECT TOP FROM WHERE ORDER BY pattern.
With CppDepend we analyze the quality of implementation and also the design of MFC. A quality of implementation is important for developer who debug inside MFC,indeed not all things work as we like and sometimes we have to look inside the library code so if it's not well implemented it complicate the task when debugging. A design of MFC is very important for developers because it impact the design of the application because it's very intrusive.
MFC General Information's:
Let's execute the following CQL request:
WARN IF Count > 0 IN SELECT FIELDS WHERE !NameLike "^m_" AND !IsGlobal
The blue squares represent the result of the query, so almost 50% fields don't begin with m_".
And what about methods naming:
WARN IF Count > 0 IN SELECT METHODS WHERE !NameLike "^[A-Z]" AND !(IsClassConstructor OR IsConstructor) AND !IsInTierProject AND !IsGlobal AND !NameLike "^~" AND !NameLike "^operator"
There are just some few methods that not begin with Upper case.
Cyclomatic Complexity:Cyclomatic complexity is a popular procedural software metric equal to the number of decisions that can be taken in a procedure.
We can also consider that a method is complex if NbLinesOfCode,NbParameters or NbBariables are great than a defined values.
So 706 methods are candidates to refactoring, but the request can be changed, it's depending on the choice of the complexity criteria for each team.
Almost all classes are commented so developers can have an idea of what a method does particularly when debugging inside MFC.
Let's see if all complex methods are commented.
SELECT METHODS WHERE (NbLinesOfCode > 100 OR CyclomaticComplexity > 20 OR NbParameters > 5 OR NbVariables > 8 ) AND NbLinesOfComment ==0
There are just few complex methods not commented.
No existence of namespaces:
The namespace is an important concept to design application, it isolates functionalities under a module and provides a logical grouping, it can also make a library simple to use. Unfortunately MFC don't contain any namespace in spite of the existence of different functionality (GUI, OLE, Database, Containers ...).
Global functions and variables:
MFC contains 786 global functions and 338 global variables, its lot for an object oriented framework.
Inheritance:SELECT TYPES WHERE NbBaseClass >0
Almost all class has at least one base class, it cause a high coupling between classes.
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.
WARN IF Count > 0 IN SELECT TYPES WHERE LCOMHS > 0.95 AND NbFields > 10 AND NbMethods >10 AND !IsGlobal ORDER BY LCOMHS DESC
31 types from 529 are considered non cohesive.
Dependency between Classes:The option Direct & Indirect Weight of use the Dependency Structure Matrix is the perfect tool to let users know where the code structure is tangled with dependencies cycles. The whole dependency matrix shows that almost all MFC types are coupled directly or indirectly.
The efferent coupling for a particular type is the number of types it directly depends on. Types with high efferent coupling are more complex than others,CppDepend propose a search panel, it's like a wizard that help you construct query easily.
Let's search for Types where efferent coupling is more than 30.
MFC separates data management into these two classes. The document stores the data and manages printing the data and coordinates updating multiple views of the data. The view displays the data and manages user interaction with it, including selection and editing.
In dependency graph CDocument and CView are mutually dependant and usually the model don't have to know the View and it must be independent of any external framework.
The model has to be as simple as possible with simple types and without any unnecessary coupling.
The following CQL request demonstrate that CDocument is highly coupled with GUI classes:
SELECT TYPES WHERE IsDirectlyUsedBy "CDocument"
The goal is to reuse the same model in different projects (Console, Gui, WebService, ...) and if our model is the CDocument we can't reuse easily in other projects.
Recommendation:Never use CDocument as model but use it just as controller to refresh views.
MFC Automation Server :
CDocument can be used also as COM Component, but CDocument is coupled with CView and using CDocument as COM component can occur some unexpected limitations and problems. When we instantiate a CDocument as COM Component a handle of view is created due to the coupling Doc/View so the number of instances to create is limited by the number of window handles that can be created, and the problem occur particularly if the instances are created in a no desktop session, in this case the number of handle that can be created is very limited by default.
Avoid using CDocument as COM component, keep it simple and use ATL instead it's more simple and flexible.
Use or don't use MFC?
MFC is well implemented but not really object oriented and the classes are high coupled, so be careful when using it and avoid any unnecessary coupling,for example prefer using ATL for COM component and STL for containers.
It's preferable to avoid as possible the coupling of your model with Doc/View concept, it make your design less flexible and add more complexity.
The goal is to spend more time in developing the business layer and isolate any technical layer will be very advantageous:
So keep it simple and isolate the impact of any technical framework used, for that be careful for any unnecessary coupling.