By John Lakos

C0 Introduction

Excessive compile-time dependencies: single global errorcode.h. Use local enumeration.

Quality measure:
-Testability: not an afterthought
-Functionality: functional specification
-Usability: customer feedback
-Maintainability: extensible

  1. Preliminaries
    Translation unit: .c + all recursively included header files

    Internal linkage: a name is local to its translation unit

    Inline function has internal linkage, because it’s either replaced by actual code, or compiled as a static function.

    Typedef good for complex function argument: typedef int (Person::*PCPMFDI)(double) const; PCPMFDI is a pointer to a const Person member function taking a double argument and returning an int.

  2. Ground Rules
    Use redundunt include guards in header files for large project. In this example, if thatfileb.h is included in thatfilea.h, the preprocessor doesn’t need to open thatfileb.h to find out.
    #ifndef THISFILE_H
    #define THISFILE_H

    #ifndef THATFILEA_H
    #include “thatfilea.h”

    #ifndef THATFILEB_H
    #include “thatfileb.h”


  3. Components
    When using other’s code that may change, avoid relying on one header file to include another (include header file in .c).

    Avoid long-distance friendship across components. Treat friendship as implementation detail. Friend class/operator should be in the same header file of the befriended, so that client cannot simply define a class of his own (with internal linkage) that has exactly same declaraction to gain friendship and thus access to private members.

  4. Physical Hierarchy
    A physical dependency graph that can be assigned unique level numbers is levelizable (it doesn’t have cyclic dependency, the dependency graph is a Directed Acyclic Graph DAG). Levelizable design is easier to develop/test in isolation and incrementally.

    The level of a component is the length of the longest path in dependency graph from it to the set of external/library components.

    Hierarchical testing means testing individual components at each level of the physical hierarchy. It requires a separate test driver for each component.

    Incremental testing means testing only the functionality actually and directly implemented within the component under test. Direct implementation means the function is not just a stub for a lower level function.

    White-box testing ensures that we solve a problem correctly. Black-box testing ensures that we solve the correct problem.

    Cumulative Component Dependency = \sum_{all components C_i} (# of components to test C_i incrementally, including C_i itself)
    CCD_{totally independent components}(N) = N
    CCD_{Balanced Binary Tree}(N) = (N+1) * (log2(N+1) – 1) + 1
    CCD_{linearly dependent components}(N) = \sum1N(i) = N(N+1)/2
    CCD_{Cyclically Dependent Graph}(N) = N * N

    Average Component Dependency = CCD(N)/N (N is # of components)

    Normalized CCD = CCD(N) / CCD_{Balanced Binary Tree}(N)

  5. Levelization
    A subsystem is levelizable if it compiles and the graph implied by #include of the individual components (including .c files) is acyclic.

    Component A dominates B if A is at a higher level and depends physically on B. Dependency can be added to a system without creating cycle as long as the dependee does not dominate dependent.

      Techniques for reducing dependency cycle

    • Escalation: move interdependent functionality from components to static members in a potentially new higher-level component (may be just a struct) that depends on all original components.
    • Demotion: move interdependent functionality from components to a potentially new lower-level (shared) component. It can reduce CCD even when there’s no cyclic dependency, by reducing unnecessary transitory dependencies. For example A->B->C, D, where A actually only uses an enum in B. Demotion means A->E, B->C, D, E so that A no longer depends on B or C and D.
    • Opaque pointers
      A component uses a type in name only if compiling the component does not require the definition of the type.

      A pointer is opaque if the definition of the type to which it points to is not included in the current translation unit.

      Suppose A is a container of B. If B holds a pointer to A and has a function that calls A’s method, A and B become mutually dependent. Instead we can make B::A* opaque by providing a getter for A*.

    • Dumb data: generalization of opaque pointer, where an object holds something it doesn’t know. For example, use an integer index instead of an iterator to represent an object in a sequence. It can be used to break in-name-only dependency, but it doesn’t have type safety and encapsulation that opaque pointer has.
    • Redundancy: duplicate small amount of code may reduce dependency and outweigh reuse.
    • Callbacks: a function supplied by a client to allow a (usually) lower-level component to perform something that requires a (usually) higher-level context. Use as a last resort.
    • Manager class: a high-level class that owns and coordinates lower-level objects, where the lower-level classes do not dominate each other and are mutually dependent.
    • Factoring: extract independent implementation details and demote them to a lower level helps reducing (but not eliminating) the cost of cyclic dependency.

      A type has value semantics if a copy constructor and (usually) an assignment operator are inherently (semantically) valid operations for it.

    • Escalating encapsulation: encapsulation a type means hiding its use, not hiding the type itself (declaration and interface). Use a wrapper class (re facade pattern) to restrict the subset of classes exposed to clients in the interface of the overall subsystem, which results in that the encapsulation is escalted to the wrapper class.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s