Why POCO is well implemented and designed?

“The POCO C++ Libraries are a collection of open source class libraries for developing network-centric, portable applications in C++. POCO stands for POrtable COmponents. The libraries cover functionality such as threads, thread synchronization, file system access, streams, shared libraries and class loading, sockets and network protocols (HTTP, FTP, SMTP, etc.), and include an HTTP server, as well as an XML parser with SAX2 and DOM interfaces and SQL database access. The modular and efficient design and implementation makes the POCO C++ Libraries well suited for embedded development.”
This kind of statement “The modular and efficient design and implementation” is often used to describe libraries even if they are bad designed.

I decided to verify if POCO is well designed and implemented as expected, for that I analyse it with CppDepend.

Here’s the result of the analysis

and here’s POCO general informations:

The first remark is that POCO is well commented.

Implementation

Number of line of code

Methods with many number of line of code are not easy to maintain and understand, let’s search for methods with more than 60 lines.

SELECT METHODS WHERE NbLinesOfCode > 60 ORDER BY NbLinesOfCode DESC

Less than 1% of methods has more than 60 lines.

Cyclomatic complexity

Cyclomatic complexity is a popular procedural software metric equal to the number of decisions that can be taken in a procedure.

Let’s execute the following CQL request to detect methods to refactor.

SELECT METHODS WHERE CyclomaticComplexity > 20 ORDER BY CyclomaticComplexity DESC

So only 1% of methods can be considered as complex.

Which methods are complex and not enough commented?

SELECT METHODS WHERE CyclomaticComplexity > 20 AND PercentageComment

Methods with many variables

Methods where NbVariables is higher than 8 are hard to understand and maintain. Methods where NbVariables is higher than 15 are extremely complex and should be split in smaller methods (except if they are automatically generated by a tool).

SELECT METHODS WHERE NbVariables > 15 ORDER BY NbVariables DESC

only 8 methods has too many variables.

Types with many methods and fields

Let’s sarch for types with many methods, for that we can execute the following CQL request

SELECT TYPES WHERE NbMethods > 30 AND !IsGlobal ORDER BY NbMethods DESC

Only 3% of types has many methods.

And we can do the same search for fields

SELECT TYPES WHERE NbFields > 20 AND !IsGlobal ORDER BY NbFields DESC

Less than 1% of types has many fields.

We can say that POCO is well implemented,few methods are considered complex,the types are simple with few methods and fields and it’s well commented.

DESIGN

Abstract vs instability
The “Abstractness vs Instability” graph can be useful to detect projects that will be difficult to maintain or evolve.
This following post describe the utility of this graph and how to exploit it to improve the design.

For POCO here’s the “Abstractness vs Instability” graph:

Only Fondation is inside the zone of pain , it’s normal because it’s very used by other projects.

inheritance

Multiple inheritane increase complexity ,and we have to use it carefully.

Let’s search for class with many base classes.

SELECT TYPES WHERE NbBaseClass >1

The blue rectangles represent the result.

only few classes derived from more than one class.

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 ORDER BY LCOMHS DESC

only 1% of types are considered as no cohesive.

Efferent coupling

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.

Let’s execute the following CQL request

SELECT TYPES WHERE TypeCe > 50 AND !IsGlobal ORDER BY TypeCe DESC

And the result is empty so no class has many responsabilities.

Types most used

It’s very interesting to know which types are most used,for that we can use the TypeRank metric.

TypeRank values are computed by applying the Google PageRank algorithm on the graph of types’ dependencies. A homothety of center 0.15 is applied to make it so that the average of TypeRank is 1.

Types with high TypeRank should be more carefully tested because bugs in such types will likely be more catastrophic.

Let’s search for types most used and complex.

SELECT TYPES WHERE TypeRank >10 AND CyclomaticComplexity >20

The result is empty so no popular class is complex.

Layering and level metric

This post explain the level metric and how to exploit it to improve design.

Let’s search for the dependency cycles, for that we can execute the following CQL requestSELECT METHODS WHERE !HasLevel AND !IsGlobal

only fews methods has dependency cycle, let’s take for example the Zip project and look to its dependency graph

You can do it for Boost, the difference is that for POCO there’s vcproj that contains all info needed for the analysis, like additional include directory, preprocessing but for Boost we have to configure them, for that you can use ProjectMaker to add source file to analyse and add boost path to additional include path.
I will wrote a post about Boost analysis, and you can do it with the version available at http://www.cppdepend.com.
any feedback is welcome if you find some problems to analyse Boost.

On the large, everything is peaches with Poco’s design. I love it and prefer PoCo (greatly) over anything boost-y or ACE-y. It is just so much simpler to use.

On the detail level there are the usual annoyances.

Example: I have tried to find time to make things compile on AIX 5.3 using the IBM XlC++ compiler. No dice, I even had to strip quite a few things (delegates/function/asynch stuff: mainly the dreaded template stuff) from Foundation to even get it to compile [1].

In the foundation classes, however, many things are dependent, and what’s worse, a thing like Path (or File, I don’t recall a.t.m.) is depending on Environment (for resolution of $VARIABLE references or ~/this type). However, Environment mistakenly depended on Poco::Net for generating machine IDs. So I ended up stubbing it out for my platform :)

I could not get Poco::Net to compile without the full (or at least more) of Foundation, and I couldn’t get enough of Foundation to compile, for spurious dependency on Poco::Net.

Now why would such coupling not appear as problems in CppDepend? I haven’t tried CppDepend but I figure it migh have much to do with the tool possibly missing some of the preprocessing logic (the _WIN32, _UNIX or _POSIX includes e.g.) to spot the coupling.

Now: all of this is not to detract much of Poco; if anything more to detract a little of the CppDepend analysis report. IMHO it used to many blanket statements and little insight. This is okay if it is just to show off the tool. I just felt it needed to be balanced a bit. Real code audit goes a little further than throwing a tool at it and admiring the pretty reports it spills out :)

Not that I’ve never fallen in that trap myself (I recall discovering NDepend… I was like the proverbial kid in the candy store!)

[1] the changes required to make it all compile were not too complex (I guess) but simply prohibitively labour intensive (imagine having to rewrite all the delegation/functor template declarations/instantiations/specializations without the implicit use of default template parameters; I have written a number of helper macros (VIM) but I couldn’t see the end of he tunnel after a full day of work. That means: too much effort

Foundation does not depend on the Net library. If it would, this would be a huge design failure. It does, however, require the socket API in the Environment::nodeId() member function (or, rather its actual implementation in nodeIdImpl()).
The size of the Foundation library is one of the things we’d like to change in POCO 2.0.
As for the IBM compiler: it’s now been more than 10 years that the C++ Standard if out, so I guess its reasonable to expect that at least the compilers from major manufacturers fully support it. It puzzles me a bit though, because I’ve heard of people compiling it successfully with xlC++ 8, with a few patches (which have been included in 1.3.4; may not be in the trunk yet). I don’t have access to an AIX machine, so I cannot verify this.

Ok – Mine was xlC++ 7. More interestingly, the compiler explicitely refuses to compile the code with reference to a specific ANSI standard :) I don’t know who’s right here (xlC or ANSI) but for the life of me I haven’t found the appropriate -langlvl or similar option in IBM’s many man-pages…

I reviewed my patch and I must agree: I had overlooked that the ‘GetAdaptersInfo’ stuff in WIN32 was in fact _not_ Poco::Net code, but Win32 API (Windows 5.0+). I must have done a grep when in a hurry, because the same function appeared in Poco::Net (of course, being called) and I might have quickly drawn the conclusion it was _defined_ there, without actually reading it. I apologize for drawing that premature conclusion and posting it here.

Moreover: this explain why the dependency did not show up in CppDepend. Needless to say, I’m so going to give this tool a test drive once I get the time!