Cover page images (keyboard)

Coupling & Cohesion

Sue Evans

Press space bar for next slide

Components

A component, at least for sake of this discussion, is a routine or module. In Python a component might be an object / class.

Routines and Modules

Modularity and Abstraction

Information Hiding

Good Design

Characteristics of Good Design

High-quality design leads to quality products

  1. Ease of understanding
  2. Ease of implementation
  3. Ease of testing
  4. Ease of modification
  5. Correct translation from requirements

Component Independence

Abstraction and Information Hiding allow us to examine ways that components are related.

Strive to make components as independent as possible

Component Coupling

Component coupling is the degree to which components are dependent on one another.

How components are coupled

The Goal is to Minimize Coupling

Types of Coupling

Example of Content Coupling:

Python is such a strongly structured language, that it doesn't have goto or it's associated label, so content coupling isn't possible in this language. So the following code doesn't run, but you will get the idea of what content coupling is from it.

def Func1(a):			

   print "In Func1"
   a += 2				
   goto F2A				
					
   return a				



def Func2():

   print "In Func2"

F2A:

   print "At Func2A"

Example of Common Coupling :

Use and Modification of a Global Variable

globalVar = 0

def Function1(a):			

   if a > 0:
      globalVar += 1
      a = 0

   return a


def Function2():

   if globalVar > 0:
      globalVar = 42
   else:
      globalVar = -1

Example of Control Coupling :

Uses a flag to determine what the function will do.

This example is from a game of "Naval Battle", where the craft are indicated on the board by a letter in their name. When the user makes a guess that is a miss, an 'O' is placed on the board in that position. When the user has a hit, the character of the boat is changed from its original letter to an 'X'. When the last original letter for a craft has been hit, a message is printed indicating which craft was hit.

Here's a board in play.

linuxserver1.cs.umbc.edu[138] python naval.py
    0   1   2   3   4   5   6   7   8   9
  -----------------------------------------
0 | O |   |   |   | O |   |   | O |   |   |
  -----------------------------------------
1 |   | O |   |   |   |   |   |   | X | X |
  -----------------------------------------
2 |   |   |   |   |   |   |   |   |   |   |
  -----------------------------------------
3 |   |   |   |   |   |   |   |   |   |   |
  -----------------------------------------
4 |   |   | X | X | O |   |   | O |   |   |
  -----------------------------------------
5 |   |   |   |   |   |   |   |   |   |   |
  -----------------------------------------
6 |   |   |   |   |   |   |   |   |   |   |
  -----------------------------------------
7 |   |   |   |   | O |   |   |   |   |   |
  -----------------------------------------
8 |   |   |   |   |   |   |   |   |   |   |
  -----------------------------------------
9 |   |   |   |   |   |   |   |   |   |   |
  -----------------------------------------
linuxserver1.cs.umbc.edu[139]

Here's the same game after play is done. After 30 moves, the destroyer, cruiser and battleship were sunk. The aircraft carrier was found and partially destroyed, but the sub was not found. We should show the player where the remaining craft were, as well as the shots taken.

    0   1   2   3   4   5   6   7   8   9
  -----------------------------------------
0 | O |   |   |   | O | A |   | O |   |   |
  -----------------------------------------
1 |   | O |   |   |   | A |   |   | X | X |
  -----------------------------------------
2 |   |   |   | O |   | A |   |   |   | O |
  -----------------------------------------
3 |   |   |   |   |   | X |   |   |   | O |
  -----------------------------------------
4 | O | X | X | X | O | X |   | O |   | O |
  -----------------------------------------
5 |   |   |   |   |   |   |   |   |   | O |
  -----------------------------------------
6 |   | O |   |   |   |   |   |   |   | O |
  -----------------------------------------
7 | S |   |   |   | O |   | X | X | X | X |
  -----------------------------------------
8 | S | O |   |   |   |   |   |   |   |   |
  -----------------------------------------
9 | S |   | O |   |   | O |   |   |   | O |
  -----------------------------------------
linuxserver1.cs.umbc.edu[140]

A flag called done is passed into the function. If the game is over, the flag done will be True and the board will be printed differently than if the game is not done.

A single function shouldn't have a dual purpose. We should have written two different functions, one to print the board during the game and a different one to show the player where the ships were if he lost.

def printBoard (board, rows, cols, done):

   print "    0   1   2   3   4   5   6   7   8   9"
   print "  -----------------------------------------"
   
   for i in range(rows):
      print i,
      for j in range(cols):
         if done:
             print "| %c" % (board[i][j]),
         else:
             if board[i][j] == ' ' or board[i][j] == 'O' or board[i][j] == 'X':
                 print "| %c" % (board[i][j]),
             else:
                 print "|  ",
      print "|"
      print "  -----------------------------------------"      

Example of Stamp Coupling :

Stamp Coupling occurs when too much information is passed to a function.

As a rule of thumb, never pass a data structure containing many fields to a module that only needs a few.

The distinction between data and stamp coupling is not relevant in object-oriented systems.

This example is written in C, which is a procedural language. Here we are passing an entire RECTANGLE structure to each of the functions, even though the function really doesn't need to see or modify all of the members. Written this way, the function must know the names of the members and is also allowed to change any of the members. Each of these functions really should have been written to take primitive types, not the entire RECTANGLE.

typedef struct rectangle
{	
   int length;
   int width; 
   int area;
   int perimeter;
   int color;
   double diagonal;
   char symbol;
} RECTANGLE;

RECTANGLE CalcArea      (RECTANGLE r);
RECTANGLE CalcPerimeter (RECTANGLE r);
RECTANGLE CalcDiagonal  (RECTANGLE r);

int main()
{
   RECTANGLE rect1;
    
   rect1.length = 7;
   rect1.width  = 6;
   rect1.color   = RED;
   rect1.symbol = '#';

   rect1 = CalcArea (rect1);
   rect1 = CalcPerimeter (rect1);
   rect1 = CalcDiagonal (rect1);

   printf("For width: %d and length %d\n",
          rect1.width, rect1.length);
   printf("The area, perimeter and diagonal are\n");
   printf("%d %d %f\n", rect1.area, rect1.perimeter,
          rect1.diagonal);

   return 0;
}

RECTANGLE CalcArea (RECTANGLE r)
{
   r.area = r.width * r.length;

   return r;
}


RECTANGLE CalcPerimeter (RECTANGLE r)
{
   r.perimeter = 2 * r.width + 2 * r.length;

   return r;
}


RECTANGLE CalcDiagonal (RECTANGLE r)
{
   r.diagonal = sqrt (r.width * r.width
                      + r.length * r.length);

   return r;
}

Example of Data Coupling :

This example shows a better way to write the previous program. Here we will be passing and returning only primitive data types. They are all that is really needed by the functions and now the functions are more general, too.


#define BLACK 0
#define RED   1
#define BLUE  2

typedef struct rectangle
{
   int length;
   int width; 
   int area;
   int perimeter;
   int color;
   double diagonal;
   char symbol;
} RECTANGLE;

int    CalcArea      (int width, int length);
int    CalcPerimeter (int width, int length);
double CalcDiagonal  (int width, int length);

int main()
{
   RECTANGLE rect1;

   rect1.length = 7;
   rect1.width  = 6;
   rect1.color  = RED;
   rect1.symbol = '#';

   rect1.area = CalcArea(rect1.width, rect1.length);
   rect1.perimeter = CalcPerimeter(rect1.width, rect1.length);
   rect1.diagonal = CalcDiagonal(rect1.width, rect1.length);

   printf("For width: %d and length %d\n",
          rect1.width, rect1.length);
   printf("The area, perimeter and diagonal are\n");
   printf("%d %d %f\n", rect1.area, rect1.perimeter,
          rect1.diagonal);

   return 0;
}

int CalcArea (int width, int length)
{
   int area;

   area = width * length;

   return area;
}


int CalcPerimeter (int width, int length)
{
   int perimeter;

   perimeter = 2 * width + 2 * length;

   return perimeter;
}


double CalcDiagonal (int width, int length)
{
   double diagonal;

   diagonal = sqrt (width * width
                    + length * length);

   return diagonal;
}

Cohesion

Cohesion is the degree of interrelatedness of internal parts. All of the elements of a component are directed towards and are essential for the same task, ("A function should do one thing, and only one thing"). We should try to MAXIMIZE Cohesion.

Degrees of Cohesion

How much cohesion?

Functional cohesion should be achieved where ever possible.
Communicational and sequential cohesion represent a satisfactory fallback level.
Logical, temporal, and procedural cohesion should be reserved for application specific, non-reusable code.
Coincidental cohesion should not be used at all.

Example of Coincidental Cohesion

All of the following in one module:

Example of Logical Cohesion

    Display time and date on the screen
    Prompt user for user-id
    Look up user-id in USER list
    If found:
	Prompt for password
	Validate password
	Process course enrollment request
    Else if not found:
	do error processing

Example of Temporal Cohesion

Related only by the time they need to be done, i.e. at the beginning of the program

    Initialize two arrays
    Initialize several variables
    Seed the random number generator
    Print a greeting

Example of Functional Cohesion

Each in a separate module: