Tuesday, June 9, 2009

Inside MFC

The Microsoft Foundation Class Library is an application framework for programming in Microsoft Windows. Written in C++, MFC provides much of the code necessary for managing windows, menus, and dialog boxes; performing basic input/output; storing collections of data objects; and so on.

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:


The dependency Graph shows that MFC uses 149 methods from ATL and 1014 from Windows API, and there’s general Information’s about MFC:


Code Implementation:

Naming Rules:

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.

SELECT METHODS WHERE (NbLinesOfCode > 100 OR CyclomaticComplexity > 20 OR NbParameters > 5 OR NbVariables > 8 )




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.

Comments:

SELECT METHODS WHERE NbLinesOfComment > 0



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.

Design:

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.

Types 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.

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 screenshot below showsthat the 2 classes CDocument and CCmdUI are involved in a cycle of minimum length 5.


The whole dependency matrix shows that almost all MFC types are coupled directly or indirectly.




Coupling:

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.





Doc/View Concept:

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.

Recommendation:

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:

· Human resources department don’t need to spend a lot of time to search for MFC gurus; maybe just few MFC developers will be sufficient.

· Developers don’t add a complexity of a framework to the complexity of what’s they implement.

· Reusing easily the business layer in other context (Web Services, Console,…).

So keep it simple and isolate the impact of any technical framework used, for that be careful for any unnecessary coupling.

Keep it Simple Concept make C++ much easier

When the languages like Java or C# has been used by companies, a language like C++ became for many developers and mangers a complex and a bad language to avoid.

History of computer engineering can maybe explain this C++ vision:

Before internet explosion in 1990s, developing with C++ was very difficult due to the inexistence of technical documentation and feedback to resolve easily technical problems, so the team focused on technical layer.

We can understand why in this period the managers follow the technical approach and we looked for a C++ Guru to join the development Team.

After internet explosion we can found a many C++ documentation and feedback for technical layer but unfortunately the vision was not changed, and new developers spend lot of time searching for technical tricks.

The good way is to focus more time in the business layer which is the added value of a project and a technical layer must be just an infrastructure and we don’t have to spend a lot of time in it.

Imagine that we have to begin an application using COM Components, let’s see how two managers will realize this project.

C++ manager focused on technical layer

The first manager will first try to search for developers that master COM components it will take more time and energy but it’s obligatory for him to implement a project as he expect .

He will begin with a COM implementation and since the beginning of the project he add a complexity to it and also add useless dependency , even if the team contains just COM Gurus an overhead time will be spend in the COM Layer.

So finally this kind of manager will participate in an unpopularity of C++, every basic C++ developer will say “C++ is very complex”.

"Keep it simple" manager

The second manager named “Keep it Simple” try to look for just one developer who master COM technology and the rest of the team are just a basic C++ developers.

This manager tell to his team to forget COM and just implement POCO objects ,simple classes that use a simple types to implement a business layer.

And just one developer who master COM technology develop a wrappers around the POCO objects.

So finally the “Keep it Simple” manager win a lot of time to look for developers and to implement the project and aproximatively the whole project is developed with a basic C++ concept, and isolate the COM technology complexity and limit dependency of a project .

With this approach every developer will say “WOW developing project that use COM technology is very simple”.

So try to avoid useless dependency; it can multiply the development time of a project, and a tool like cppdepend can help you to detect unecessary dependency .