Measuring code modularity from architectural descriptions
Architectural Description Languages (ADLs) provide structures like components (with ports) and connectors (with roles) with which to describe a software system. Even though these structures represent a high-level modularization of the described system, architectural descriptions are not always useful for helping the system designer to determine the degree of coupling (inter-dependence) among software modules in the implemented system. Most of the formal work on architectural connection represent connectors as high-level abstractions (such as event sequences) that do not directly map to code-level structures. Even though a connector may be represented as a distinct entity in the architectural description, the implementation of the connector may require code fragments spread throughout multiple system components.
The ability to predict the degree of code-level coupling introduced by architectural connectors is important because many software qualities are determined by the dependency structures of the implemented system. For example, if a connector implementation requires code changes throughout several modules, then a program’s maintainability and understandability may be diminished. Or, if the connector’s interaction protocol requires a large amount of inter-component communication (especially data copies) then overall system throughput could be diminished. Unfortunately, it is often hard to determine the code-level modularity characteristics that will result from choosing a particular architectural connector.
One way to mitigate the inability to determine degree of coupling from architectural descriptions is to make code-level impacts of architectural connectors explicit in the architectural description. Some things we’d like to know:
- What code modules are affected when a particular connector is chosen?
- To what degree are the changes localized within the affected modules?
- What are the properties of the required module interfaces in terms of number of parameters, size of communicated data, number of messages passed, degree of data localization, complexity of communication protocol, etc.?
- What external resources does the connector require (e.g., files, network, database, etc.)?
- How many code-level dependencies will be introduced and what is the nature of those dependencies?
- What kind of control structures does the connector require? Is control uni-directional or bi-directional?
It is possible that an abstract connector might be implemented multiple ways. In this case we want to know the things listed above for each of the possible implementations.
A well-known paper by Mehta et al.  notes that architectural connectors may be primitive or complex. Primitive connectors are typically implemented via programming languages (e.g., procedure call, shared memory access) while complex connectors are composites made up of primitive connectors, other composite connectors, or even entire components. Composite connectors are often implemented by libraries or frameworks. Mehta et al. point out that it is important to establish some conceptual framework to support reasoning about the low-level interactions present in composite connectors.
Good modularity can be achieved by reducing the amount of coupling (inter-dependence) among the modules in a system. In some of the earliest work on measuring coupling, Meyers provides six distinct levels . These were later ordered by Page-Jones  according to their effects on certain qualities such as understandability and maintainability. Meyers’ six-level scheme was extended by Offutt et. al to include several new levels of coupling and directionality . I have reproduced that below. In the definitions each variable use is classified as a computation-use (C-use) in which a variable is used in an assignment or output statement, a predicate-use (P-use) in which a variable is used in a predicate statement, or an indirect-use (I-use) in which a variable is a C-use that affects some later predicate in the module. More recently, a slew of coupling metrics have been introduced that relate specifically to object-oriented systems. We’ll ignore those for the time being because most of the scientific code I work with is procedural in nature (go Fortran!).
|Coupling Type||Coupling Level||Directionality||Definition|
|Independent Coupling||0||Commutative||A does not call B and B does not call A, and there are no common variable references or common references to external media between A and B|
|Call Coupling||1||Commutative||A calls B or B calls A but there are no parameters, common variable references or common references to external media between A and B|
|Scalar Data Coupling||2||Bidirectional||A scalar variable in A is passed as an actual parameter to B and it has a C-use by no P-use or I-use|
|Stamp Data Coupling||3||Bidirectional||A record in A is passed as an actual parameter to B and it has a C-use but no P-use or I-use|
|Scalar Control Coupling||4||Bidirectional||A scalar variable in A is passed as an actual parameter to B and it has a P-use|
|Stamp Control Coupling||5||Bidirectional||A record in A is passed as an actual parameter to B and it has a P-use|
|Scalar Data/Control Coupling||6||Bidirectional||A scalar variable in A is passed as an actual parameter to B and it has an I-use but no P-use|
|Stamp Data/Control Coupling||7||Bidirectional||A record in A is passed as an actual parameter to B and it has an I-use but no P-use|
|External Coupling||8||Commutative||A and B communicate through an external medium such as a file.|
|Non-Local Coupling||9||Commutative||A and B share references to the same non-local variable; a non-local variable is visible to a subset of the modules in the system.|
|Global Coupling||10||Commutative||A and B share reference to the same global variable; a global variable is visible to the entire system|
|Tramp Coupling||11||Bidirectional||A formal parameter in A is passed to B as an actual parameter, B subsequently passes the corresponding formal parameter to another procedure without B having accessed or changed the variable.|
The higher degrees of coupling are undesirable in software systems because they indicate that modules have a lot of dependencies (or a few particularly ugly dependencies). Too many dependencies can reduce program understanding and cause maintenance headaches down the line. So, what we’d like to do is give a system architect some idea of the level of coupling introduced when choosing connectors without having to build the whole system first.
Most of the work on measuring coupling came about before the software architecture community honed in on the idea of connectors as loci of interaction that should be give first-class status. But, there is a clear relationship between the coupling levels defined above and primitive connectors. Most of the coupling levels imply a simple interaction mechanism: the procedure call. For example, coupling level 1 is a procedure call in which no parameters are passed. This corresponds with the Mehta Procedure Call connector type with certain constraints on the connector dimensions: no parameters, explicit invocation, single entry point, etc. (Mehta defines connector dimensions as “architecturally relevant details” of the connector.) The global coupling level (10) in which two modules share a reference to the same global variable corresponds to the Data Access connector with a “transient” availability (i.e., variable is located in memory, most likely on the heap). Data Access with “persistent” availability (e.g., file or database) would correspond to coupling level 8, external coupling. The analytic power of linking connectors with coupling levels allows a system designer to make informed decisions about how different connectors will affect inter-module dependencies. For example, based on to the Offut levels defined above, changing a Data Access connector from a file-based communication mechanism to a database mechanism reduces the level of coupling.
Things will likely get more interesting when we consider composite connectors. For example, if we know the coupling behavior introduced by the primitive connectors that make up a composite connector, how can we leverage that knowledge to say something about the level of coupling introduced by the composite connector?
More recently, some work has been done on bridging the gap between architectural descriptions and implementations. One approach is to add architectural abstractions to a programming language. This is the approach of ArchJava which, for example, adds component, port, and connect keywords to the Java language. The idea is to give programmers the ability to communicate the architecture by explicitly identifying architectural details right there in the code. Although this approach is shown to be effective for ensuring communication integrity (i.e., only architecturally connected components should communicate directly), I think this approach suffers from something like the observer effect. The observer effect says that when you observe a phenomenon, the observation procedure itself often ends up effecting the behavior of the phenomenon. The Wikipedia example is a good one: when you measure the air pressure of your tires, you probably let a little air out while taking the measurement. The tie in with ArchJava is that in specifying architectural connection concretely in a programming language (the observation), some decision has to be made a priori about how to implement the connectors (the effect). In the case of ArchJava, a port is essentially a container for method signatures. So, quite literally, all architectural connectors implemented with ArchJava are object-oriented method calls! This approach is therefore not useful for describing existing systems in which we want to illuminate the component-to-component interaction mechanisms that are already in place.
Stay tuned… more to come on this topic.
 Nikunj R. Mehta, Nenad Medvidovic, and Sandeep Phadke. “Towards a taxonomy of software connectors.” Proceedings of the 22nd international conference on Software engineering, 2000.
 G. Myers, Reliable Software Through Composite Design. New York: Mason and Lipscomb Publishers, 1974.
 M. Page-Jones, The Practical Guide to Structured Systems Design, 2nd ed. New York: Yourdon Press, 1988.
 A. J. Offutt, M. J. Harrold, and P. Kolte, “A Software Metric System for Module Coupling,” Journal of Systems and Software, vol. 20, pp. 259-308, 1993.