Software Design

Stages of design

The design process

Design phases

Design strategies

Design quality

A maintainable design can be adapted to modify existing functions and add new functionality. The design must therefore be understandable and changes should be local in effect. The design components should be cohesive which means that all parts of the component should have close logical relationship. They should be loosely coupled which means they should not be tightly integrated. Coupling is a measure of the independence of the components. The looser the coupling, the easiest it is to adapt the design as the effect of changes are localized.

Design quality metrics may be used to assess if a design is a “good” design.




Cohesion


Cohesion levels

  1. //Implementation of r(x) = 5x + 3
  2. a(x, y) = x + y ' Groups Addition
  3. b(x, y) = x * y ' Groups Multiplications
  4. r(x) = a(b(5, x), 3)
  5. //Functional because the parts "a" and "b" contribute well defined tasks.

A cohesive object is one in which a single entity is represented and all the operations of this entity are included with the object.

If a class in an Object-oriented system inherit attributes and operations from a superclass the cohesion of that class is reduced. This is no longer possible to consider that object class as self-containing unit.


Cohesion as a design attribute.

In object-oriented programming, if the methods that serve a class tend to be similar in many aspects, then the class is said to have high cohesion. In a highly cohesive system, code readability and reusability is increased, while complexity is kept manageable.

Cohesion is increased if:

Advantages of high cohesion (or “strong cohesion”) are:

While in principle a module can have perfect cohesion by only consisting of a single, atomic element – having a single function, for example – in practice complex tasks are not expressible by a single, simple element. Thus a single-element module has an element that either is too complicated, in order to accomplish task, or is too narrow, and thus tightly coupled to other modules. Thus cohesion is balanced with both unit complexity and coupling.

Coupling

a) High cohesion, Low coupling
b) Low cohesion, High coupling

Cyclomatic Complexity - https://en.wikipedia.org/wiki/Cyclomatic_complexity

This is a measure of the control complexity of a program. This control complexity may be related to program understandability.

McCabe's Cyclomatic Complexity (V(G) also known as CC)

Introduced by Thomas McCabe in 1976, cyclomatic complexity measures the number of linearly-independent paths through a program module. This measure provides a single ordinal number that can be compared to the complexity of other programs. Cyclomatic complexity is often referred to simply as program complexity or as McCabe's complexity.

Methods with a high complexity tend to be more difficult to understand and maintain. In general the more complex the methods of an application are, the more difficult it is to test the application, which adversely affects the application's reliability. V(G) counts the number of branches in the body of the method defined as:

This metric is computed as follows:

If V(G) is larger than 10, consider to split the method up. The more complex the program the harder it is to test and comprehend. This metric can additionally be interpreted as the cost of producing test cases for the code. The following is an example of how RefactorIT computes V(G):

... // in the beginning: V(G) = 1
// +2 conditions, V(G) = 3:
if ((i > 13) || (i < 15)) {
  System.out.println("Hello, there!");
  // +3 conditions, V(G) = 6:
  while ((i > 0) || ((i > 100) && (i < 999))) {
    ...
  }
}
// +1 condition, V(G) = 7
i = (i == 10) ? 0 : 1;
switch(a) {
  case 1: // +1, V(G) = 8
    break;
  case 2: // +1, V(G) = 9
  case 3: // +1, V(G) = 10
    break;
  default:
    throw new Runtim


Weighted Methods per Class - The WMC metric is the sum of the complexities of all class methods. It is an indicator of how much effort is required to develop and maintain a particular class. RefactorIT sums the V(G) (cyclomatic complexity) of all declared methods and constructors of class to calculate the WMC. A class with a low WMC usually points to greater polymorphism. A class with a high WMC indicates that the class is complex (application specific) and therefore harder to reuse and maintain. The lower limit for WMC in RefactorIT is default 1 because a class should consist of at least one function and the upper default limit is 50.


Depth in Tree (DIT)

This metric calculates how far down a class is declared in the inheritance hierarchy, where the DIT is the length from the class to the root of the inheritance tree. In Java, all classes have java.lang.Object as their ultimate super class, which is defined to have a depth of 0. So a class that immediately extends java.lang.Object has a metric value of 1. Any of its subclasses will have a value of 2 and so on. DIT is defined in RefactorIT for classes and interfaces as follows:

The deeper a class is in the hierarchy, the greater the number of methods and state variables it is likely to inherit, which makes it more difficult to predict its behaviour. It becomes more specialised and it can be hard to understand a system with many inheritance layers. However, there is a greater potential reuse of inherited methods.

A DIT value of 0 indicates a root while a value of 2 and 3 indicates a higher degree of reuse. If there is a majority of DIT values bellow 2, it may represent poor exploitation of the advantages of OO design and inheritance. RefactorIT recommends a maximum DIT value of 5 since deeper trees constitute greater design complexity as more methods and classes are involved.


Number of Children in Tree (NOC)

Also known as : Number of Direct Subclasses of a Class, this metric measures the number of direct subclasses of a class. The size of NOC approximately indicates how an application reuses itself. It is assumed that the more children a class has, the more responsibility there is on the maintainer of the class not to break the children's behaviour. As a result, it is harder to modify the class and requires more testing.

The upper recommended limit for a class in RefactorIT is 10 and the lower limit is 0. If NOC exceeds 10 children for a class, this may indicate a misuse of subclassing.

Coupling and inheritance

Understandability

Cohesion. Can the component be understood on its own?

Naming. Are meaningful names used?

Documentation. Is the design well-documented?

Complexity. Are complex algorithms used?

Adaptability

  1. Its components are loosely coupled

  2. It is well-documented and the documentation is up to date

  3. There is an obvious correspondence between design levels (design visibility)

  4. Each component is a self-contained entity (tightly cohesive)

Adaptability and inheritance

Key points

Name:       

Description:

Abstractness (A)

This metric counts the ratio of abstract classes and interfaces for a package

Afferent Coupling (Ca)

This metric counts the number of classes from other packages that depend on classes in the analysed package

Comment Lines of Code (CLOC)

CLOC counts all lines that contain regular comments and Javadoc comments

Cyclic Dependencies (CYC)

This estimates how many cycles in which a package is involved

Cyclomatic Complexity (V(G) aka CC)

V(G) counts the number of code conditions giving an indication of how complex the program is

Density of Comments (DC = CLOC / LOC)

This determines a density value for how commented the code is

Dependency Inversion Principle (DIP)

The DIP metric calculates the ratio of dependencies that have abstract classes or interfaces as a target

Depth in Tree (DIT)

This is the distance from the class to the root of the inheritance tree

Direct Cyclic Dependencies (DCYC)

Direct cyclic dependencies counts every mutual dependency between packages

Distance from the Main Sequence (D)

The perpendicular distance of a package from the main sequence

Efferent Coupling (Ce)

This metric is a measure for the number of types of the analysed package which depend upon types from other packages

Encapsulation Principle (EP)

This calculates the ratio of classes that are used outside of a package

Executable Statements (EXEC)

This metric counts the number of executable statements

Instability (I = Ce / (Ca + Ce))

Check to see how stable/unstable your packages are designed

Limited Size Principle (LSP)

This is the number of direct subpackages of a package

Modularization Quality (MQ)

The difference between the average inter- and intra-connectivity of the packages

Non-Comment Lines of Code (NCLOC, aka NCSL and ELOC)

This counts all the lines that do not contain comments or blank lines

Number of Abstract Types (NOTa)

This metric counts the number of abstract classes and interfaces

Number of Children in Tree (NOC)

This metric measures the number of direct subclasses of a class

Number of Concrete Types (NOTc)

This metric counts the number of concrete classes

Number of Exported Types (NOTe)

This metric counts the number of classes and interfaces exported outside a package

Number of Parameters (NP)

This metric counts the number of parameters for a method or a constructor

Number of Types (NOT)

This metric counts the number of classes and interfaces

Response for Class (RFC)

This metric counts the number of distinct methods and constructors invoked by a class

Total Lines of Code (LOC, aka SLOC and ELOC)

The number of lines for a class including blank lines and comments

Weighted Methods per Class (WMC)

This calculates the sum of cyclomatic complexity of methods for a class

Number Of Fields (NOF)

This calculates the number of fields declared in method (in local and anonymous classes declared in this method)

Number Of Attributes (NOA)

This calculates the number of fields declared in class or interface


Please, find a formula here - Dependency.htm

References

This metric documentation is based on various sources. Definitions and information can be found at the following locations: