Design Quality

Assessing Decomposition

How do you know if you've created a good design?

Two measures of an effective design decomposition are coupling and cohesion.
 

Coupling

Coupling describes the relative independence among modules. Coupling is a qualitative measure of the degree to which a module is connected to other modules in a software structure.  Coupling depends on the interface complexity between modules, the point at which entry or reference is made to a module, and what data pass across the interface.  We strive for the lowest possible coupling.

The five types of coupling are described below in order from high (bad) to low (good):
 

Content coupling: Sometimes called "pathological coupling" cause it is so tweaked. One module refers to the inside of another module (in any way).  E.g. changing data inside another module, or making an unconditional branch into another module. Fortunately, modern languages have made the latter nearly impossible, and if you always make your instance data private, then you defeat the former as well.

Common (global) coupling: Several modules refer to a global data area. See below.

"Global variables = death"

This is about the worst design you would ever imagine.  It usually results from a functional design approach where the designer became lazy when it came to data design.  All the data just got thrown in a big stew pot, and functional modules dip into whatever they want.

Problems with common coupling:


Control coupling: one module passes a piece of information (a "flag") that is intended to control the internal logic of another module. Should be avoided, as it means the calling module must know how the logic of the called module is organized. This usually is a symptom of deeper design flaws (e.g., improper decomposition).
E.g.  Call ProcessTransaction( WhatToDoFlag)

Stamp coupling: one modules passes another a composite piece of data.  This is okay as long as you really use all the data, but not good if you use only one field out of a large data structure.
E.g., Call MakeMove (ChessBoard, Move) Return NewChessBoard  is okay.
Call CalculateRentalFee(CustomerRecord) Return RentalFee is not, as CustomerRecord has many fields not used by the module.

Data coupling: Two modules communicate by parameters, each parameter is an elementary piece of data. This is normal and usually harmless. However, beware of excessively long parameter lists, and "tramp" data that hitches a ride through various modules without being used.
E.g.  Call CalculateMortgagePayment (Term, InterestRate, AmountBorrowed) Return RepaymentRate
 

Cohesion

Cohesion is a qualitative measure of the functional specialization of a module.  Ideally, each module (or method) should do only one thing.  High cohesion is good.  Moderate cohesion can be nearly as good as high cohesion. Low cohesion is really bad.
 
 
Type of cohesion Quality of module
coincidental ghastly
logical horrible
temporal poor
procedural OK
communicational OK
sequential OK
functional good

Coincidental Cohesion: the module performs a set of tasks that have little or no relation to each other.
Example:

  1. Wash Car
  2. Bake Cake
  3. Walk Dog
  4. Apply to College
Why do programmers do this?
It usually results from improper decomposition, or taking a previously monolithic code and arbitrarily shoveling it into modules.
What's wrong with it?
Very hard to understand and maintain.
Makes modules unnecessarily complex, leading to errors during development and unreliable systems.


Logical Cohesion: the module performs multiple tasks that are related logically or conceptually, or the activities are of the same general kind.
Example

AddorUpdateStudent: the name indicates the module is doing more than a single task.
Example
  1. Output to printer
  2. Output to file
  3. Output to communications port
are related because they are all output routines. To call the module a control flag is usually required, e.g.,
Call OutputRecord (aRecord, whichDevice)

Why do programmers do this?

It seems to save code, since some tasks will share certain parts of the code. Why should there be a separate routine for each device?
What's wrong with it?
Changes in requirements for one task affect code used by all tasks.
The code is often very convoluted, using flags to control logic.


Temporal Cohesion: The module performs a set of tasks that occur at the same time, but otherwise have nothing in common.
Example: The classic example is an initialization module that carries out several unrelated functions, such as Open Files, Reset Totals, Print Headings.

Why do programmers do this?

Everyone does; initialization modules are obvious.
What's wrong with it?
If you later need to Print Headings for a new page, you can't do it without Open Files too.
The module is difficult to reuse.


Procedural Cohesion: The module performs a set of tasks that are different and possibly unrelated except that control flows from one to the next.  Similar to temporal, except that order of processing matters.  The tasks are related by order of execution rather than by any single problem-related function.
Example

FOR index IN 1 TO 100
    Add FirstTable(index) to Total1
    Add SecondTable(index) to Total2
END FOR
finds the total of two tables that are unrelated in the original problem.

Why do programmers do this?

They are trying to save code. In the above example, they write only one loop instead of two.
What's wrong with it?
Difficult to maintain. What happens if SecondTable changes to 120 items?
Communicational Cohesion: the module performs a set of tasks that use the same data.
Example: FindCustomerNameandBalance

Why do programmers do this?

Once you've got the data, it's tempting to just get everything you need from it all at once.
What's wrong with it?
It can lead to maintenance problem, say, if someone wants to find the customer name but not the balance.
Sequential Cohesion: the module performs a set of tasks where the results of one task serve as the input to the next.  A sequentially cohesive module is like an assembly line.
Example:
  1. Read raw record
  2. Format record
  3. Validate record
Why do programmers do this?
Sequential code is easy to follow as it has a linear flow to it.
What's wrong with it?
Usually nothing. It can lead to maintenance problems, say, if someone wants to format a record they got somewhere else. It's not as reusable as a functional module because the tasks are probably not generally useful to have bundled together.


Functional Cohesion: all the elements of the module contribute to completing a single problem-related task.It contains all the code and only the code sufficient to do a specific operation. There are a small number of parameters, and thus low coupling.  The module can be developed in parallel with other modules.
Examples:

Why do programmers do this?
Because they hate rewriting an entire system when there are minor requirements changes.
What's wrong with it?
Nothing! This is the smart way to design your modules.