Software Design Principes

Introduction

  • General guidelines and best practices

  • Goal: improve maintainability, scalability, and efficiency

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Foreword

In this lecture, we use the terms Module and Operation to design programming language constructs that contain some behavior and some structural properties, and routines, respectively.

Modules may be Classes in object-oriented programming, Units in procedural programming, or Components in component-oriented programming.

Operations may be Methods in object-oriented programming, or Procedure and Functions in procedural programming.

Design Principle 1: Abstraction

  • Complexity control by the amplification of the essential features and the suppression of the less important details.

  • Types of abstraction: functional, structural, control, etc.

assembly machine
Figure 1. Assembly is an abstraction of the Machine Language

Java as an Abstraction of its Bytecode

Java Source Code
public class Length {
	protected final double length;

	public Length(double d) {
		this.length = d;
	}
}
Corresponding Bytecode
public class fr.unantes.info.units.Length {
  protected final double length;

  public fr.unantes.info.units.Length(double);
    Code:
       0: aload_0
       1: invokespecial #1 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: dload_1
       6: putfield      #2 // Field length:D
       9: return
}

UML as an Abstraction of a Complex Application

  • Abstraction allows designers to postpone design decisions.

A Game Sofware as a UML Component
Figure 2. A Game Sofware as a UML Component

Design Principle 2: Decomposition

  • Complexity control by the decomposition of problems into sub-problems.

  • Divide and conquer approach, common to all design techniques.

Example

A Game Software decomposed into three Components
Figure 3. A Game Software decomposed into three Components

Design Principle 3: Information Concealment

  • A type of Data Abstraction

  • AKA Information Hiding, Information Dissimulation

  • Important mean to achieve abstraction

  • Design decisions that are likely to change should be hidden behind interfaces.

A UML Component behind an Interface
Figure 4. A UML Component behind an Interface
The Play Interface
Figure 5. The Play Interface

Hiding Decisions Behind Interfaces

  • Design decisions that are likely to change should be hidden behind interfaces.

Diagram

Hiding Components Behind Interfaces

  • Components should communicate only through well-defined interfaces.

  • Interface should be specified by as little information as possible.

  • If component internal decisions change, clients should not be affected.



Components communicate through an Interface
Figure 6. Components communicate through an Interface

Information Concealment Types

  • Data representation

    • Data types, etc.

  • Algorithms

    • Data search and sorting

  • Input and output formats

    • Machine dependencies, e.g., byte-ordering, character codes

  • Lower-level interfaces

    • Ordering of low-level operations

  • Policy/Mechanism separation

    • Multiple policies for one mechanism.

    • Same policy for multiple mechanisms.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 4: Increase Cohesion

Cohesion is a measure of the degree to which the elements of a module are functionally related.



  • Cohesion should be increased where possible.

  • Types of cohesion: functional, layer, de communicational, sequential, procedural, temporal, coincidental.

Keeping Together

  • All the code that performs a particular task (functional cohesion).

  • All the code that provides or uses a set of related services (layer cohesion).

  • All code that access or modify certain data (communicational cohesion).

  • A sequence of operations, in which one operation provides input to the next (sequential cohesion).

  • A set of operations that are used one after another (procedural cohesion).

  • Operations that are performed at the same execution phase (temporal cohesion).

  • Operations which cannot logically be placed in other cohesive units (coincidental cohesion).

Advantages of high cohesion

  • Better readability and understandability: High cohesion results in clear, focused modules with a single, well-defined purpose, making it easier for developers to understand and maintain the code.

  • Better error isolation: High cohesion reduces the chances of a change in one part of a module affecting other parts, making it easier to locate errors.

  • Better reliability: High cohesion leads to modules that are less prone to errors and that function more consistently, leading to an overall improvement in the software’s reliability.

Disadvantages of low cohesion

  • Increases code duplication: Low cohesion may lead to code duplication, as related elements are split into separate modules.

  • Reduces functionality: Low cohesion may lead to modules lacking a clear purpose, containing unrelated elements, reducing functionality, and making maintenance harder.

  • Reduces understandability: Low cohesion can make it difficult for developers to comprehend the purpose and behavior of a module, leading to errors and a lack of clarity.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 5: Reduce Coupling

Coupling is the measure of the degree of interdependence between the modules. A good software will have low coupling.
  • Coupling should be reduced where possible.

  • Interdependencies impacts maintainability, testability, and simplicity.

software coupling
Figure 7. Coupling between Modules

Types of Coupling

From the weakest (loosest) to the strongest (tightest):
  • Data: when parameter types are primitive types.

  • Stamp (data-structured coupling): when a complete data structure is passed from one module to another.

  • Control: when a parameter controls the behavior of an operation (a what-to-do flag).

types of coupling
Figure 8. Types of Coupling

Types of Coupling

  • External: when a component depends on external artifacts: operating system, hardware, libraries, etc.

  • Common: when modules share global variables. Common coupling can lead to uncontrolled error propagation and unforeseen side-effects, after modifications.

  • Content: when a module modifies the content of another module.

types of coupling
Figure 9. Types of Coupling

Other Types of Coupling

Object-Oriented Types of Coupling
  • Subclass: describes the relationship between a subclass and its superclass. The subclass is connected to its superclass, but not the opposite.

  • Temporal: when operations are bundled into one module because they occur at the same time.

  • Dynamic: when a module uses/imports another at run-time (static coupling metrics lose precision when using dynamic binding and inheritance).

  • Semantic: when the relationship between modules are based on their meaning or purpose, rather than their implementation.

  • Logical: when the relationship between modules is base on the software change history.

Advantages of low coupling

  • Better maintainability: Low coupling reduces the impact of changes in one module on other modules, making it easier to modify or replace individual components without affecting the entire software.

  • Better modularity: Low coupling allows modules to be developed and tested in isolation, improving the modularity and reusability of code.

  • Better scalability: Low coupling facilitates the addition of new modules and the removal of existing ones, making it easier to scale the system as needed.

Disadvantages of high coupling

  • Increases complexity: High coupling increases the interdependence between modules, making the system more complex and difficult to understand.

  • Decreases flexibility: High coupling makes it more difficult to modify or replace individual components without affecting the entire system.

  • Decreases modularity: High coupling makes it more difficult to develop and test modules in isolation, reducing the modularity and reusability of code.

Cohesion and Coupling

cohesion coupling
Figure 10. Cohesion and Coupling

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 6: Reusability

  • Reusability should be increased where possible.

  • Components should be designed to work on different contexts.

  • Generalize design as much as possible:

    • Use Frameworks, Patterns, and UML Collaborations.

    • Design the system to contain hooks.

  • Keep the design as simple as possible

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 7: Reuse

  • Reuse Analysis, Design, and Code

  • Reuse existing artifacts when possible, to take advantage of existing investment.

  • Use Frameworks, Patterns, and UML Collaborations.

  • Complementary to the principle of reusability.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 8: Obsolescence Anticipation

  • Avoid:

    • Immature technologies.

    • Undocumented features.

    • Software and Hardware without long-term support provision.

  • Plan technology changes and adopt technologies that are supported by different vendors.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 9: Portability

  • Develop on and for different platforms.

  • Avoid platform-specific frameworks or libraries.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 10: Testability

  • The internal state of a component should be accessible by external programs.

  • Design components to be used directly by external clients, without user interfaces.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 11: Simplicity

  • The KISS principle [US Navy, 1960]:

    • Keep it simple, stupid!

  • Most systems work best if they are kept simple rather than made complicated

  • Simplicity should be a key goal in design and unnecessary complexity should be avoided.

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

Design Principle 12: Stability

  • A Component (class, module, etc.) must only depend on more stable components

  • The dependency between two components must follow the direction of stability (a component must always depend on a more stable one).

Diagram

Outline

  • Basis

    • Cohesion

    • Couping

  • General principles

SOLID

  • Acronym for Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion

Single responsibility [Robert Martin]

  • A class should have one and only one responsibility (i.e. only one potential change in the software’s specification should be able to affect the specification of the class).

  • The classes are abstractions of the real and not the different roles played by an object.

swiss knife

Open/Closed principle [Bertrand Meyer]

Software entities should be open for extensions but closed for modification.

Method AbstractEList::equals() from EMF
  public boolean equals(Object object) {
	// (...)
    Iterator<?> objects = list.iterator();
    if (useEquals()) {
		// Compare with equals()

    } else {
		// Compare with "=="
	}
  }

Liskov substitution principle [Liskov 1994]

  • Class instances can be used throughout the superclass interface, without notifying its clients.

Diagram

Interface-segregation [Robert Martin 2002]:

  • Many client-specific interfaces are better than one general-purpose interface.

Dependency inversion [Robert Martin 2003]

  • A client should depend upon abstractions and not upon concretions.