Computer science on the other is a rather young academic discipline. This is probably a reason why we don't have to deal with old luggage such as learning some old language such as Latin to learn the field.
At least this was the case until about 1994 when Robert C. Martin of Object Mentor introduced three design quality metrics in his paper OO Design Quality Metrics and when doing this decided that it was time to have some Latin in computer science as well.
- Ca : Afferent Couplings : The number of classes outside this component that depend upon classes within this component
- Ce : Efferent Couplings : The number of classes inside this component that depend upon classes outside this component
- I : Instability : (Ce ÷ (Ca+Ce)) : This metric has the range [0,1]. I=0 indicates a maximally stable category. I=1 indicates a maximally instable component.
Is the ferry coming or is it leaving?
Instability is regular english, but "Afferent and Efferent Couplings"? I can easily remember that its about incoming and outgoing couplings, but I suppose incoming and outgoing would have been to simple.If you try to google them you will see that the most common use of afferent and efferent are as medical terms. Wikipedia defines afferent as: "Afferent is an anatomical term" and "Efferent is an anatomical term". Same story at dictionary.reference.com, no references to classes and dependencies, just medicine and anatomy.
Well as I said, doctors are used to this Latin business, I am not, but I try to remember which is which by trying to remember the little Latin I do know and what these two words are composed of.
Afferent is ad- + ferre and efferent is ex- + ferre, which means roughly something along the lines of "carry to" and "carry away from". There obviously is some rule in latin that ad- before an f, becomes af-, and ex- before an f becomes ef-.
The origins of the word ferry is also ferre.
So we only have to remember that Af- is Ad-, and then we have to know that Ad means towards.
Ad-ferre - the ferry is coming
We have afferent coupling when the direction of the dependencies is going inwards, or towards our component.
Ex-ferre - the ferry is leaving, exiting the port
We have efferent couplings when the direction of the dependencies is going away from our component. When we are depending on something.
The Main Sequence and the Stable-Abstraction Principle (SAP)
So now that we remember what Ce, Ca and I means we can go on to the next step and look into what we can do once we know how to calculate the Instability of a component and what it means.
Well actually I have only mentioned that the Instability is related to afferent and efferent couplings, but the general idea is that this Instability number tells us something about how hard it is to change the component.
This is not to say that every component should be hard nor that all should be easy to change.
Martin writes in his 1994 paper about the main sequence, and he later refines this into the Stable-Abstraction Principle which states:
A component should be as abstract at it is stable
Although possible it is not practical to count and calculate these numbers as we are writing our code. Fortunately tools such as nDepend exist to do this job for us.
nDepend can be downloaded free of charge for use in Open Source projects and trial version is available for commercial use.
To check where your code is with respect to the Stable-Abstraction Principle and a lot of other metrics, download nDepend and start it. Then go File->New Project
Then add the assemblies you want to analyze, the easiest option is to simply select a Visual Studio solution and nDepend will locate the assemblies for you.
After you have selected the assemblies, simply hit F5 and nDepend will generate a detailed report for you which among a lot of details contains a diagram which is called "Assemblies Abstractness vs. Instability" which is exactly the same as Martins main sequence diagrams.
The Stable Dependencies Principle (SDP)
In a later article on Stability Martin introduces the Stable Dependencies Principle:
THE DEPENDENCIES BETWEEN PACKAGES IN A DESIGN SHOULD
BE IN THE DIRECTION OF THE STABILITY OF THE PACKAGES. A
PACKAGE SHOULD ONLY DEPEND UPON PACKAGES THAT ARE
MORE STABLE THAT IT IS.
In other words the components we depend on should be harder to change than the component we are working on. We want to reduce the likelihood of needing to change our component because one of our dependencies change.
So we should check our code for this as well, this is supposed to be something that nDepend will tell us directly out-of-the-box, at least this is what the nDepend documentation says, but I haven't been able to find it.
Anyway there are ways that we can tailor the information that nDepend does provide exactly as we want.
Anyway there are ways that we can tailor the information that nDepend does provide exactly as we want.
nDepend provides us with a query language called CQL that we can use to get information about our code. It is in a syntax that bears some resemblance to SQL, but just don't be fooled into believing it is SQL, it can do a lot, but not nearly as much as you can do with data and SQL.
If the standard report doesn't tell you what you are looking for, there are a lot of other pre-made queries you can run and you can write your own.
To get a list of the Instability values (I) of all your assemblies you can run the following CQL:
// <Name>Olavs Instability of
assemblies</Name>
WARN IF Count > 0 IN SELECT ASSEMBLIES ORDER BY Instability ASC
What we can make use of is that nDepend will output a number of XML files when it is run and we can run it from the command.
So when we have made a nDepend project, added the solution (assemblies) and added the above CQL query, then nDepend will produce XML files that we can use to do a Stable Dependencies Principle check ourselves.
To run from the command line:
Start a command prompt in the folder where nDepend is installed and execute: nDepend.Console.exe <path to the ndepend project file>
The XML output files will be in a folder called NDependOut in the same folder as the nDepend project file.
The two we are interested in are AssembliesDependencies.xml and CQLResult.xml:
The AssembliesDependencies.xml lists for each assembly the assemblies the assembly depends on and all the assemblies that depends on the assembly.
This is an example of a part of an AssembliesDependencies.xml file:
<Dependencies_For Assembly="MyProject.UI v1.0.0.0">
<DependsOn>
<DependsOn_Name>mscorlib v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Domain v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Service.DataContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web.Abstractions v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.ServiceModel v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Configuration v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Mapping v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Services.DataContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Preview.Model v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Core v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Services.ServiceContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web.Extensions v4.0.0.0</DependsOn_Name>
</DependsOn>
<ReferencedBy>
<ReferencedBy_Name>MyProject.Web v1.0.0.0</ReferencedBy_Name>
<ReferencedBy_Name>MyProject.CalculationService v1.0.0.0</ReferencedBy_Name>
</ReferencedBy>
</Dependencies_For>
<Dependencies_For Assembly="MyProject.Web v1.0.0.0">
<DependsOn>
<DependsOn_Name>MyProject.UI v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Domain v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Mapping v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web v4.0.0.0</DependsOn_Name>
<DependsOn_Name>mscorlib v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Service.DataContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web.Extensions v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Core v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.ServiceModel v4.0.0.0</DependsOn_Name>
The other file that we are interested in is the CQLResult.xml file. If you remember that we added a query for the Instabilities of assemblies and that we named it: "Olavs Instabilities.....". The result of this query will now appear in the CQLResult.xml file. There will be a lot of other results as well, but we can search for our query by the name we gave it in the comment of the query.
This is an example of the "Olavs Instabilities of assemblies" section:
<Query Status="Warn" Name="Olavs Instability of assemblies" NbNodeMatched="33" NbNodeTested="33" SelectionViewFileName="SelectionView_-266889740.png" KindOfNode=" assemblies">
<QueryHtml><font color='#008000'>//&#0160;&lt;Name&gt;</font><b style="color:#008000;background-color:#E6FFE6">Olavs&#0160;Instability&#0160;of&#0160;assemblies</b><font color='#008000'>&lt;/Name&gt;<br/></font><font color='#0000FF'>WARN</font>&#0160;<font color='#0000FF'>IF</font>&#0160;<font color='#000064'>Count</font>&#0160;<font color='#000000'>&gt;</font>&#0160;<b style="color:#000000;background-color:#FFFF99">0</b>&#0160;<font color='#0000FF'>IN</font>&#0160;<font color='#0000FF'>SELECT</font>&#0160;<font color='#0000FF'>ASSEMBLIES</font>&#0160;<font color='#0000FF'>ORDER</font>&#0160;<font color='#0000FF'>BY</font>&#0160;<font color='#000064'>Instability</font>&#0160;<font color='#0000FF'>ASC</font></QueryHtml>
<Columns>
<Column>assemblies</Column>
<Column>Instability</Column>
</Columns>
<Rows>
<Row Name="System.ServiceModel" FullName="System.ServiceModel">
<Val>N/A</Val>
</Row>
<Row Name="System.Web.Abstractions" FullName="System.Web.Abstractions">
<Val>N/A</Val>
</Row>
<Row Name="MyProject.Domain" FullName="MyProjectDK.DealerPortal.Domain">
<Val>0.21081</Val>
</Row>
<Row Name="MyProject.CalculationService.Data" FullName="MyProjectDK.DealerPortal.CalculationService.Data">
<Val>0.66667</Val>
</Row>
<Row Name="MyProject.Mapping" FullName="MyProjectDK.DealerPortal.Mapping">
<Val>0.71875</Val>
</Row>
<Row Name="MyProject.Service.DataContracts" FullName="MyProjectDK.DealerPortal.Service.DataContracts">
<Val>0.78723</Val>
</Row>
<Row Name="MyProject.UI" FullName="MyProjectDK.DealerPortal.UI">
<Val>0.7907</Val>
</Row>
<Row Name="MyProject.Repositories" FullName="MyProjectDK.DealerPortal.Repositories">
<Val>0.96296</Val>
</Row>
<Row Name="MyProject.Preview.Model" FullName="MyProjectDK.DealerPortal.Preview.Model">
<Val>0.96907</Val>
</Row>
ad-This is an example of a part of an AssembliesDependencies.xml file:
<Dependencies_For Assembly="MyProject.UI v1.0.0.0">
<DependsOn>
<DependsOn_Name>mscorlib v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Domain v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Service.DataContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web.Abstractions v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.ServiceModel v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Configuration v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Mapping v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Services.DataContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Preview.Model v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Core v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Services.ServiceContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web.Extensions v4.0.0.0</DependsOn_Name>
</DependsOn>
<ReferencedBy>
<ReferencedBy_Name>MyProject.Web v1.0.0.0</ReferencedBy_Name>
<ReferencedBy_Name>MyProject.CalculationService v1.0.0.0</ReferencedBy_Name>
</ReferencedBy>
</Dependencies_For>
<Dependencies_For Assembly="MyProject.Web v1.0.0.0">
<DependsOn>
<DependsOn_Name>MyProject.UI v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Domain v1.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Mapping v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web v4.0.0.0</DependsOn_Name>
<DependsOn_Name>mscorlib v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System v4.0.0.0</DependsOn_Name>
<DependsOn_Name>MyProject.Service.DataContracts v1.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Web.Extensions v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.Core v4.0.0.0</DependsOn_Name>
<DependsOn_Name>System.ServiceModel v4.0.0.0</DependsOn_Name>
The other file that we are interested in is the CQLResult.xml file. If you remember that we added a query for the Instabilities of assemblies and that we named it: "Olavs Instabilities.....". The result of this query will now appear in the CQLResult.xml file. There will be a lot of other results as well, but we can search for our query by the name we gave it in the comment of the query.
This is an example of the "Olavs Instabilities of assemblies" section:
<Query Status="Warn" Name="Olavs Instability of assemblies" NbNodeMatched="33" NbNodeTested="33" SelectionViewFileName="SelectionView_-266889740.png" KindOfNode=" assemblies">
<QueryHtml><font color='#008000'>//&#0160;&lt;Name&gt;</font><b style="color:#008000;background-color:#E6FFE6">Olavs&#0160;Instability&#0160;of&#0160;assemblies</b><font color='#008000'>&lt;/Name&gt;<br/></font><font color='#0000FF'>WARN</font>&#0160;<font color='#0000FF'>IF</font>&#0160;<font color='#000064'>Count</font>&#0160;<font color='#000000'>&gt;</font>&#0160;<b style="color:#000000;background-color:#FFFF99">0</b>&#0160;<font color='#0000FF'>IN</font>&#0160;<font color='#0000FF'>SELECT</font>&#0160;<font color='#0000FF'>ASSEMBLIES</font>&#0160;<font color='#0000FF'>ORDER</font>&#0160;<font color='#0000FF'>BY</font>&#0160;<font color='#000064'>Instability</font>&#0160;<font color='#0000FF'>ASC</font></QueryHtml>
<Columns>
<Column>assemblies</Column>
<Column>Instability</Column>
</Columns>
<Rows>
<Row Name="System.ServiceModel" FullName="System.ServiceModel">
<Val>N/A</Val>
</Row>
<Row Name="System.Web.Abstractions" FullName="System.Web.Abstractions">
<Val>N/A</Val>
</Row>
<Row Name="MyProject.Domain" FullName="MyProjectDK.DealerPortal.Domain">
<Val>0.21081</Val>
</Row>
<Row Name="MyProject.CalculationService.Data" FullName="MyProjectDK.DealerPortal.CalculationService.Data">
<Val>0.66667</Val>
</Row>
<Row Name="MyProject.Mapping" FullName="MyProjectDK.DealerPortal.Mapping">
<Val>0.71875</Val>
</Row>
<Row Name="MyProject.Service.DataContracts" FullName="MyProjectDK.DealerPortal.Service.DataContracts">
<Val>0.78723</Val>
</Row>
<Row Name="MyProject.UI" FullName="MyProjectDK.DealerPortal.UI">
<Val>0.7907</Val>
</Row>
<Row Name="MyProject.Repositories" FullName="MyProjectDK.DealerPortal.Repositories">
<Val>0.96296</Val>
</Row>
<Row Name="MyProject.Preview.Model" FullName="MyProjectDK.DealerPortal.Preview.Model">
<Val>0.96907</Val>
</Row>
So now we have two lists (xml files), the first one has all the dependency information for the assemblies and the second has the calculated Instability values for each of the assemblies.
Now we only have to combine these two lists to check if we have any assemblies that breaks the Stable dependencies principle.
I have written a small console application that will do just this. The source code for it is available at https://spdchecker.codeplex.com/
The application is exuted from a command prompt as this:
SPDChecker <path to CQLResult.xml> <path to AssembliesDependencies.xml>
And the result looks something like this:
Should you try it and should it find something in your assemblies, please be advised that even if my warning text may indicate that this is a major crisis, remember what Robert C. Martin himself wrote about his metrics:
However, a metric is not a god; it is merely a measurement against an arbitrary standard. It is certainly possible that the standard chosen in this paper is appropriate only for certain applications and is not appropriate for others. It may also be that there are far better metrics that can be used to measure the quality of a design.
Thus, I would deeply regret it if anybody suddenly decided that all their designs must unconditionally be conformant to “The Martin Metrics”. I hope that designers will experiment with them, find out what is good and what is bad about them, and then communicate their findings to the rest of us.I agree 100% with Mr. Martin on this.
ex-
efferent
afferent
http://www.objectmentor.com/resources/articles/stability.pdf
http://www.objectmentor.com/resources/articles/oodmetrc.pdf
http://www.ndepend.com/NDependDownload.aspx
There is also a very good explanation of these two principles (SDP/SAP) in Robert C. Martins book: "Agile Principles, Patterns and Practices in C#", Prentice Hall, pages 426-435