Software Architecture and Design Introduction

What is Design?

  • Planning how to build a system
  • Plan
    • Components / modules
    • Their connections
    • their interactions
    • how they address requirements
      • Functional
      • Quality
  • Two-levels
    • Architecture design = high-level design
    • Detailed design
  • Uses:
    • Manage complexity
    • how to implement and maintain the system
    • divide work
    • for writing tests

Engineering design process

  1. Establish customer needs (requirements)
  2. Establish design criteria
  3. Design Loop: Synthesis (generate design ideas) | Analysis (wrt. criteria)
  4. Design optimization. Go back to 3. if necessary
  5. Validate (Go back to 1 or 2 or 3 or 4)
  6. Coomunicate

Properties of Design

User-facing Qualities

  • Functionality
  • Performance
  • Security, Safety
  • Usability

Developer-facing qualities (Modularity)

  • Maintainability
  • Extensibility
  • Testibility

Maintenance

  • Repair
    • Removing known faults
      • Corrective maintenance
    • Fault detection and removal
      • Preventative maintenance
  • Modifications
    • Adapting to environment change
    • Externality Functionality

Modularity

  • Module
    • unit of decomposition with
      • An interface
      • An implementation
  • Interface
    • Interface is a contract between modules and its environment (Interface = Contract)
      • Module provided inferface to Environment (whats the signature that contains the inputs?)
        • Provided functons Guaranteed propertes
      • Environment required inferface to Module (whats the required target/ output?)
        • Required functons Assumed propertes
img img
  • Implementation
    • hidden details of how things done:
      • Algorithms, data structures, mechanisms

Designing a method

1
2
3
4
5
6
7
8
9
10
def find( arr: list, target: int) -> int: # Signature, mostly Syntactic (module name, parameters, parameter types, return types)
"""
Semantics:
Basically the documentations as comments that contain:
* what the module / function solve
* e.g. "searches arr for element == target"
* performance characteristics, e.g. O[|arr|]
* preconditions, e.g. arr must be sorted
* error codes, e.g. reutrn -1 if not found
"""
  • Syntactic = how to invoke it (module name, parameters, parameter types, return types)
  • Semantic = what it does (what the module / function solve , performance characteristics, preconditions, error codes)

Example

Consider the following interface specification.

1
2
Signature: void print(Buffer) 
Maximum memory use: 2 Mbyte

Does this interface specification contain any syntactic or semantic?

  • The specification is syntactic and semantic.
  • syntactic : print as module name, void as return type, Buffer as parameter
  • semantic : Maximum memory use: 2 Mbyte as performance characteristics

Simple Requirements to Module Mapping

In a good design, specific module can be adjusted to satsfy its corresponding requirement without affectng other requirements.

img

Good designs should reduce crosscuttng and tangling of key requirements.

Quality requirements are difficult to impossible to localize.

Requirements that are more likely to change are more important to localize.

  • We refractor / redesign the problem when crosscuttng or tangling happen.

Crosscutting

  • 1 Requirement impacting many modules
  • Non-functional (quality) requirements tend to result in cross-cutting
    • Quality requirements require multiple changes e.g. increase performance among code
img

Tangling

  • Too many requirements impacting one module
  • Non-functional (quality) requirements also tend to result in Tangling
    • Different Quality requirements require changes on the same code (e.g. performance, code readability)
img

Cohesion and Coupling

Cohesion = measure of coherence among the pieces in a module

Coupling = measure of connectedness among modules

  • We want high cohesion and low coupling

img

Cohesion

Good Cohesion

We want semantically coherent modules. Each module should have a clear purpose (responsibilities) in the system. Each key function (especially if it is likely to change) is localized to a single module rather than scattered over multiple ones.

  • Functional cohersion
    • parts of code that are repsonsible for same functionality;
      • are interacting heavily with each other
      • are on the same topic

Modules should represent important abstractions, such as important domain concepts. They often gather related operations around data they manipulate, such as classes and abstract data types.

Good design provides clear rules on where to put new piece of code during evolution. A project may develop it own design principles to guide system decomposition.

Bad Cohesion

  • Forms of bad cohesion
    • accidental / coincidental cohesion => put things randomly together
      • too few modules
      • Coincidental cohesion is when code is inserted into a random module. This can occur during system maintenance of the system lacks clear design or the maintainer is unaware of the design.
    • “god” modules
      • too much functionality
      • at a level of abstraction
      • They tend to contain large amounts of code and have large and complex interfaces. For example, a class with 50 or more operations is likely trying to do too much.
        Such classes may also exhibit non-communicating behaviors, that is, groups of functions accessing different and disjoint sets of attributes. God classes/modules are candidates that can be broken up.
    • Temporal cohesion
      • grouping code by execution time
        • e.g. all initialization code of a system in one module
      • Temporal cohesion gathers all those operations that are executed at a similar point in time into a module. As an example, consider a product performing the following major steps: initialization, get user input, run calculations, perform appropriate output, cleanup. Temporal cohesion would lead to five modules, namely, initialize, input, calculate, output and cleanup. This division will most probably lead to code duplication across the modules, e.g., each module may have code that manipulates one of the major data structures used in the program.
    • Control Flow cohesion
      • occurs when code is merged into a single module to share a common control flow
      • This cohesion is the result of misguided elimination of duplication and it results hard to understand modules with complicated logic. Also, the operations are coupled in an undesirable way: each variant is likely to evolve in independent ways; having them together complicates evolution. Such code should be refactored to separate the different operations.

Example of an bad Control Flow conhesion.

The example represents a “template” implementation of a number of quite different operations that share some basic course of action. Variation is achieved through “control flag” parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void function(flag1 , flag2 ... , flagN)
{
...// variable declarations
...// code common to all cases
if (flag1 == 1) // different code for different cases
...
else if (flag1 == 2)
...
else if (flag1 == n)
...
end if
... // code common to all cases
if (param == 1) // different code for different cases
...
else if (param1 == 5)
...
end if
...
}

Coupling

Coupling is induced by different types of dependencies among modules,

(From Best (Low Coupling) to Worst (High Coupling))

  • No Coupling
  • Data Coupling
    • module A provides data to module B;
  • Stamp Coupling
    • passing too much data (pass unecessary data)
  • Control Coupling
    • module A controls the execution (such as control flow inside) of module B;
  • Location Coupling
    • module A is aware of the location (local or remote) of the target module B;
  • Content Coupling
    • module A includes code from module B, e.g., preprocessor includes, inheritance of implementation from superclass.

Low Coupling (good)

Data coupling is when module A depends on module B by expecting only the data that it needs. No coupling is best; data coupling is second-best. An example of data dependency is the coupling among filters in pipes-and-filters architectures.

High Coupling (bad)

Hidden coupling should be avoided by making it explicit in interfaces. Hidden coupling can be caused, for example, by global variables and by reflection.

  • Mutable global variables should be avoided.

Another negative scenario is if all modules in a program have access to a common database in both read and write mode, even if write mode is not required in all cases.

The use of reflection should be controlled. Reflection is a powerful tool, but it can lead to hidden dependencies and hard to understand code. For example, the target of reflective calls can be anything. Thus, limit the potential targets and document them.

Implementation inheritance introduces strong coupling, which also lacks a clear interface. Prefer inheriting interface vs. implementation. Implementation inheritance suffers from the “fragile base class” problem, where changes to a base class can potentially break many subclasses.

Control coupling should be avoided. It occurs when one module controls the execution flow of another. Thus occurs, for example, when one module passes an execution flag to another module. Instead to passing a flag, consider using polymorphism.

Stamp coupling should be avoided. It occurs when more data is passed to a module than necessary. As an example, consider passing an entire employee record into a function that prints a mailing label for that employee. The function needs name and address but no salary, SIN number, etc. Stamp coupling creates unnecessary dependency and increased security risk.

Exercise Questions


Revisit Module, Interface, Implementations

Module

  • Module is a unit of decomposition with interface and implementation

Interface

  • Interface is a contract between modules and its environment
    • Provided inferface = called by the environment
    • Required inferface = called by the module
img

Implementation

  • Implementation is the hidden details of how things done.
    • Algorithms, data structures, mechanisms

About operation specs

  • Syntactic = how to invoke it (module name, parameters, parameter types, return types)
  • Semantic = what it does (what the module / function solve, performance characteristics, preconditions, error codes)

What is a module?

Module is a unit of decomposition with interface and implementation

or

A Module is Unit of system decomposition with a clear purpose and an interface

One important role of modules is to allow understanding a system piece by piece. Name two other roles modules can play in software development?

  • Dividing work among developers
  • Unit of reuse (use a module several times in one system or across several systems)
  • Unit of evolution (encapsulate change)
  • Unit of compilation (in a programming language)

What is an interface?

An interface is a contract between module and environment

Consider a Java implementation of a module below. It consists of two interfaces and a class containing the module implementation. Which interface, A or B, in the Java code fragment below is a required interface of the module?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface A 
{...}
public interface B
{...}
public class C implements A
{
private B b;
// Called by the module

public C (B b)
{ this.b=b; }

public void do()
{ ...
b.do();
... }
}

Provided inferface = called by the environment

Required inferface = called by the module

B is the required interface of the module

Consider the following three specs of an operation:

  1. Spec A: The operation Status open(FileHandle f) takes a parameter of type FileHandle and returns a value of type Status.
  2. Spec B: The operation Status open(FileHandle f) takes a parameter of type FileHandle and returns a value of type Status. Status equal 1 indicates success, in which case the file handle is in the state “open”.
  3. Spec C: The operation Status open(FileHandle f) takes a parameter of type FileHandle and returns a value of type Status. A call to open(f) has to return no late than 1ms.

Please classify each of the three specs as either “syntactic only” or “syntactic and semantic”.

  • Spec A is …

Syntactic only. Because it only has the signature, parameter, and the return type. No semantic.

  • Spec B is …

Syntactic and semantic exists. Exit code is explained. It also explained what the operation do.

  • Spec C is …

Syntactic and semantic exists. Performance characteristics are mentioned.

What type of requirements tend to result in cross-cutting?

Cross-cutting happen when 1 requirement impacting a lot of modules.

This is usually happened in Non-functional (quality) requirements.

Why? compare to the functional requirements, where you can meet a new requirement by modifying the specific corresponding module, or create a new one for it, e.g., EmailVerification module. Quality requirements are more like global requirements, like all APIs needs to be authenticated or the responding delay must be within 5 seconds. It’s hard to have a module like AuthenticateAllApi or FastRespond, and it is hard to achieve them without modifying many modules or using some crafty mechanisms.

Consider the following interface specification and judge if it contains syntactic and semantic.

1
2
Signature: void print(Buffer) 
Maximum memory use: 2 Mbyte

The specification is syntactic and semantic.

Syntactic: return type (void), module name (print), parameter (Buffer)

Semantic: Performance charactistics (maximum memory use)

The interface construct in Java captures the syntax of an interface. Give two examples of semantic aspects not covered by the Java interface construct. For each of them, provide an idea of how it could be captured.

Example 1: Behavior

Way to capture: can be captured using assertions (pre and post conditions), state machines, tests and/or documentation

Example 2: Performance

Way to capture: can be captured through documentation, e.g., response time

Example 3: Reliability

Way to capture: can be captured in documentation

ShoppingCart is a Java class implementing a shopping cart component. One of its fields has the type TaxCalculatorI, which is a Java interface. Which of these statements is correct?

(i) TaxCalculatorI is a provided interface of ShoppingCart.

(ii) TaxCalculatorI is a required interface of ShoppingCart.

Provided inferface = called by the environment

Required inferface = called by the module

Therefore TaxCalculatorI is a required interface of ShoppingCart. Statement (ii) is correct.

Consider the code below, implementing two components, Ermintrude and Oonator3000. Oonator3000 provides the oo() service to Ermintrude. Please modify the class Ermintrude to make its required interface explicit. You can simply cross out the code line(s) that need to be modified and provide the modified code line next to it. Hint: very few modifications are needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface ICow { 
public moo();
}

public interface IOonator {
public oo();
}

class Ermintrude implements ICow {
private Oonator3000 _oonator;
Ermintrude (Oonator3000 oonator) {
_oonator = oonator;
}
moo() {
m();
_oonator.oo();
}
m() {
...}
}

class Oonator3000 implements IOonator {
oo() {
...}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface ICow { 
public moo();
}

public interface IOonator {
public oo();
}

class Ermintrude implements ICow {
private IOonator _oonator;
Ermintrude (IOonator oonator) {
_oonator = oonator;
}
moo() {
m();
_oonator.oo();
}
m() {
...}
}

class Oonator3000 implements IOonator {
oo() {
...}
}

Replace Oonator3000 with IOonator in the Ermintrude class.


(From Best (Low Coupling) to Worst (High Coupling))

  • No Coupling
  • Data Coupling
    • module A provides data to module B;
  • Control Coupling
    • module A controls the execution (such as control flow inside) of module B;
  • Location Coupling
    • module A is aware of the location (local or remote) of the target module B;
  • Stamp Coupling
    • passing too much data
  • Content Coupling
    • module A includes code from module B, e.g., preprocessor includes, inheritance of implementation from superclass.

Consider two modules: Application and File. File provides three operations: open file, read data from file, and close file; data can only be read after opening a file and before closing it. Application reads data from File by following the File interface. Which of these coupling types does the interaction between Application and File establish?

  • (i) stamp
  • (ii) control and data
  • (iii) data only
  • (iv) control only

(ii) control and data.

(Cohesion) The Linux kernel configuration tree gathers all multi-media devices in one group, even though they (mostly) do not interact with each other. What is the likely reason for such grouping?

They are grouped by their function (by topic)

(Cohesion) Please give an example of a god class that may be justified?

  • Math Help Library class
  • Utility class
  • Generated code

(Cohesion/Coupling) Give two different disadvantages of maintaining duplicate code in a system.

  • Could forget to propagate an error fix
  • Twice as much code to read and maintain

(Cohesion/Coupling) What is a potential disadvantage of factoring out common code into a common module?

  • Increased coupling (components or projects sharing code have to coordinate)
  • Increased testing effort (If multiple project share the common code rather than their own copies, changes to the common code driven by one project will trigger retesting all projects)

(Coupling) Suppose that procedure ProcessRequest() calls procedure ProcessElement(Element element, bool isVerbose), where isVerbose determines whether the ProcessElement is executed silently on element or whether each step within ProcessElement is logged. Name the two types of coupling that exist between these two procedures?

Data and control

(Coupling) Which of the three types of coupling, namely control, data, and stamp coupling, is the lowest one?

(From Best (Low Coupling) to Worst (High Coupling))

  • No Coupling
  • Data Coupling
    • module A provides data to module B;
  • Stamp Coupling
    • passing too much data
  • Control Coupling
    • module A controls the execution (such as control flow inside) of module B;
  • Location Coupling
    • module A is aware of the location (local or remote) of the target module B;
  • Content Coupling
    • module A includes code from module B, e.g., preprocessor includes, inheritance of implementation from superclass.

Therefore its data coupling

(Coupling) Name two different disadvantages caused by stamp coupling.

  • Increased security risk (the extra data is now also exposed)
  • Imprecise interface (does not make clear what the real dependency is)