Software Design
Deriving a solution which satisfies software requirements
Look at the problem from different angles to discover the design requirements
Identify one or more solutions
Stages of design
Problem understanding - Look at the problem from different angles to discover the design requirements
Identify one or more solutions - Evaluate possible solutions and choose the most appropriate depending on the designer's experience and available resources
Describe solution abstractions - Use graphical, formal or other descriptive notations to describe the components of the design
Repeat process for each identified abstraction until the design is expressed in primitive terms
The design process
Any design may be modeled as a directed graph made up of entities with attributes which participate in relationships
The system should be described at several different levels of abstraction
Design takes place in overlapping stages. It is artificial to separate it into distinct phases but some separation is usually necessary
Design phases
Architectural design Identify sub-systems
Abstract specification Specify sub-systems
Interface design Describe sub-system interfaces
Component design Decompose sub-systems into components
Data structure design Design data structures to hold problem data
Algorithm design Design algorithms for problem functions
Design phases
Architectural design - Identify sub-systems
Abstract specification - Specify sub-systems
Interface design - Describe sub-system interfaces
Component design - Decompose sub-systems into components
Data structure design - Design data structures to hold problem data
Algorithm design - Design algorithms for problem functions
Design strategies
Functional design - The system is designed from a functional viewpoint. The system state is centralized and shared between the functions operating on that state
Object-oriented design - The system is viewed as a collection of interacting objects. The system state is de-centralized and each object manages its own state. Objects may be instances of an object class and communicate by exchanging methods
Design quality
Design quality is an elusive concept. Quality depends on specific organizational priorities.
A 'good' design may be the most efficient, the cheapest, the most maintainable, the most reliable, etc.
The attributes discussed here are concerned with the maintainability of the design
Quality characteristics are equally applicable to function-oriented and object-oriented designs
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
A measure of how well a component 'fits together'.
A component should implement a single logical entity or function
Cohesion is a desirable design component attribute as when a change has to be made, it is localized in a single cohesive component
Various levels of cohesion have been identified
Cohesion levels
Coincidental cohesion (weak) - Parts of a component are not related but simply bundled together into a single component
Logical association (weak) -Components which perform similar functions are grouped /e.g. input, error handling; grouping all mouse and keyboard input handling routines/
Temporal cohesion (weak) - Components which are activated at the same time are grouped /start up, shutdown/
Procedural cohesion (weak) -The elements in a component make up a single control sequence
Communicational cohesion (medium) - All the elements of a component operate on the same input or produce the same output
Sequential cohesion (medium) - The output for one part of a component is the input to another part
Functional cohesion (strong) - Each part of a component is necessary for the execution of a single function
Object cohesion (strong) - Each operation provides functionality which allows object attributes to be modified or inspected
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.
Not well-defined. Often difficult to classify cohesion
Inheriting attributes from super-classes weakens cohesion
To understand a component, the super-classes as well as the component class must be examined
Object class browsers assist with this process
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:
The functionalities embedded in a class, accessed through its methods, have much in common.
Methods carry out a small number of related activities, by avoiding coarsely grained or unrelated sets of data.
Advantages of high cohesion (or “strong cohesion”) are:
Reduced module complexity (they are simpler, having fewer operations).
Increased system maintainability, because logical changes in the domain affect fewer modules, and because changes in one module require fewer changes in other modules.
Increased module reusability, because application developers will find the component they need more easily among the cohesive set of operations provided by the module.
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 measure of the strength of the inter-connections between system components
Loose coupling means component changes are unlikely to affect other components
Shared variables or control information exchange lead to tight coupling
Loose coupling can be achieved by state decentralization (as in objects) and component communication via parameters or message passing
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.
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:
if statements
conditions such as && and ||
for statements
while statements
This metric is computed as follows:
Each function has a base complexity of 1
Each atomic condition adds 1
Each case block of switch adds 1
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.
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:
All interface types have a depth of 1
The class java.lang.Object has a depth of 0
All other classes have a depth of 1 plus the depth of their super class
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.
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
Object-oriented systems are loosely coupled because there is no shared state and objects communicate using message passing
However, an object class is coupled to its super-classes. Changes made to the attributes or operations in a super-class propagate to all sub-classes. Such changes must be carefully controlled
Understandability
Related to several component characteristics
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?
Informally, high complexity means many relationships between different parts of the design. hence it is hard to understand
Most design quality metrics are oriented towards complexity measurement. They are of limited use
Adaptability
A design is adaptable if:
Its components are loosely coupled
It is well-documented and the documentation is up to date
There is an obvious correspondence between design levels (design visibility)
Each component is a self-contained entity (tightly cohesive)
To adapt a design, it must be possible to trace the links between design components so that change consequences can be analyzed
Adaptability and inheritance
Inheritance dramatically improves adaptability.
Components may be adapted without change by deriving a sub-class and modifying that derived class
However, as the depth of the inheritance hierarchy increases, it becomes increasingly complex. It must be periodically reviewed and restructured
Key points
Design is a creative process
Design activities include architectural design, system specification, component design, data structure design and algorithm design
Functional decomposition considers the system as a set of functional units
Object-oriented decomposition considers the system as a set of objects
Decisions on parallelism should usually be detailed design decisions
Name: |
Description: |
---|---|
This metric counts the ratio of abstract classes and interfaces for a package |
|
This metric counts the number of classes from other packages that depend on classes in the analysed package |
|
CLOC counts all lines that contain regular comments and Javadoc comments |
|
This estimates how many cycles in which a package is involved |
|
V(G) counts the number of code conditions giving an indication of how complex the program is |
|
This determines a density value for how commented the code is |
|
The DIP metric calculates the ratio of dependencies that have abstract classes or interfaces as a target |
|
This is the distance from the class to the root of the inheritance tree |
|
Direct cyclic dependencies counts every mutual dependency between packages |
|
The perpendicular distance of a package from the main sequence |
|
This metric is a measure for the number of types of the analysed package which depend upon types from other packages |
|
This calculates the ratio of classes that are used outside of a package |
|
This metric counts the number of executable statements |
|
Check to see how stable/unstable your packages are designed |
|
This is the number of direct subpackages of a package |
|
The difference between the average inter- and intra-connectivity of the packages |
|
This counts all the lines that do not contain comments or blank lines |
|
This metric counts the number of abstract classes and interfaces |
|
This metric measures the number of direct subclasses of a class |
|
This metric counts the number of concrete classes |
|
This metric counts the number of classes and interfaces exported outside a package |
|
This metric counts the number of parameters for a method or a constructor |
|
This metric counts the number of classes and interfaces |
|
This metric counts the number of distinct methods and constructors invoked by a class |
|
The number of lines for a class including blank lines and comments |
|
This calculates the sum of cyclomatic complexity of methods for a class |
|
This calculates the number of fields declared in method (in local and anonymous classes declared in this method) |
|
This calculates the number of fields declared in class or interface |
This metric documentation is based on various sources. Definitions and information can be found at the following locations:
https://satc.gsfc.nasa.gov/metrics/codemetrics/oo/thresholds/index.html
https://www.ingrid.org/jajakarta/turbine/en/turbine/maven/reference/metrics.html
https://www.objectmentor.com/publications/granularity.pdf
https://www.objectmentor.com/publications/dip.pdf
https://www.objectmentor.com/resources/articles/Principles_and_Patterns.PDF
https://yunus.hun.edu.tr/~sencer/oom.html
https://mdp.ivv.nasa.gov/oo_metrics.html
https://www.scism.sbu.ac.uk/law/Section5/chap5/s5c5p22.html
https://builder.com.com/5102-6386-5035294.html
https://javacentral.compuware.com/products/optimaladvisor/documentation/v3.1/2037-1-15-18-8.html
https://javacentral.compuware.com/pasta/concepts/packageDesign.html
https://www.sei.cmu.edu/str/descriptions/cyclomatic_body.html
https://www.cin.ufpe.br/~inspector/relacionados/Object-oriente%20Model%20Metrics%20Document.htm
https://www.fawcette.com/archives/premier/mgznarch/vbpj/1999/09sep99/cs0999.pdf
https://www.onjava.com/pub/a/onjava/2004/01/21/jdepend.html
https://metrics.sourceforge.net/
https://serg.cs.drexel.edu/projects/bunch/
https://mvnrepository.com/artifact/com.codahale.metrics