Software Engineering
Coupling

Course Map



Agenda


Primitive Types, Classes, Interfaces, and Generics

OO programming allows you to lessen coupling on a function argument.


A  type (primitive type, abstract class, class, interface, generic type) is a set of values and a set of operations that act on those values. The type tells you what you can do with its values.

Primitive types (not objects): for integers, floating-point numbers, bytes, characters, and boolean values.
Java has eight primitive types.

Classes define structures (objects):
    internal state information
    methods
    exportable constants
    etc.
A class defines the methods that  can access and manipulate internal state information of its objects.

An interface is like a class but has only declarations of methods.
Interfaces reduce these constraints even more explicitly separating interface from implementation, opening up the function's application to a larger set of classes.

A generic type is a type that is parameterized by one or more type variables. A generic type is instantiated when actual types are substituted for the type variables. For example ArrayList<E> is a generic type and, and ArrayList<String> is an instantiation.
Latent typing makes the interfaces implicit.

Smalltalk has classes but no primitive types.

C++ has templates (generics) and  tightly couples the primitive type hierarchy with the class hierarchy.

Java has classes, primitive types (for performance), and generics.
For primitive types there are object wrappers so you can use just classes.


Relationships between Objects

Note that in general interfaces, classes, and instances of classes are objects.

Relationship types:

Type

Relation between

Association

Object instances

Inheritance

Interfaces and classes

Polymorphism Objects and object references/interfaces and classes
Parametric Polymorphism Objects and object references/interfaces and classes

Domain level: Association

Process level: Uses

Carrier level: Containment


Associations and Inheritance


Inheritance and Polymorphism


Different Ways to Implement Associations (Uses)

How does the sender access the receiver?

1. Containment

class Sender {
   Receiver receiver;
   
   public void method() {
      receiver.sendMessage();
   }
}

2. Argument of a method

class Sender {
   public void method(Receiver receiver) {
      receiver.sendAMessage();
   }
}

3. Ask someone else

class Sender {
   public void method() {
      Receiver receiver= someoneElse.getReceiver();
      receiver.sendAMessage();
   }
}

4. Creation

class Sender {
   public void method() {
      Receiver receiver= new Receiver();
      receiver.sendAMessage();
   }
}

5. Global


Heuristics for the Uses Relationship

  1. Minimize the number of classes with another class collaborates
  2. Minimize the number of message sends between a class and its collaborator
  3. Minimize the number of different messages a class sends to another class.
  4. Minimize the product of the number of methods in a class and the number of different messages they send.


System Complexity

Decomposable system

A nearly decomposable system

Design Goal


Parnas (72) KWIC (Simple key word in context) experiment
 

Parnas compared two different implementations:

1. Modules based on steps needed to perform task

2. Modules based on "design decisions"

List

Each module should hide a design decision

All ways of decomposing an application are not equal


Quality of Objects

Not all objects are created Equal!

Metrics for quality

Cost
Primary goal of decomposition into modules is reduction of software cost

Context
Specific goals of module decomposition

Coupling
Strength of interaction between objects in system

Cohesion
Degree to which the tasks performed by a single module are functionally related


Coupling

Measure of the interdependence among modules

"Unnecessary object coupling needlessly decreases the reusability of the coupled objects"

"Unnecessary object coupling also increases the chances of system corruption when changes are made to one or more of the coupled objects"


Types of Modular Coupling in order of desirability


Modular Coupling - Data Coupling

Output from one module is the input to another

Using parameter lists to pass items between routines


Common Object Occurrence:

Object a passes object x to object b

Object x and b are coupled

A change to x's interface may require a change to b

Example

class Receiver
     {
     public void message( MyType x )
          {
          // code goes here
          x.doSomethingForMe( Object data );
          // more code
          }
     }

Modular Coupling - Data Coupling

Major Problem

Object a passes object x to object b

x is a compound object

Object b must extract component object y out of x

b, x, internal representation of x, and y are coupled


Example: Sorting student records, by ID, by Name

class StudentRecord
     {
     String name;
     Long id;
     
     public String getName()
          { return name }

     // etc.
     }

SortedList cs5331 = new SortedList();
StudentRecord newStudent;
//etc.
cs5331.add ( newStudent );

Solution 1:  Bad News

class SortedList
     {
     Object[] sortedElements = new Object[ properSize ];

     public void add( StudentRecord x )
          {
          // coded not shown
          Long id-a = x.getID();
          Long id-b = sortedElements[ i ].getID();
          if ( id-a < id-b )
               // do something
          else
               // do something else
          }
     }

Solution 2: Send message to object to compare self

class SortedList
     {
     Object[] sortedElements = new Object[ properSize ];

     public void add( StudentRecord x )
          {
          // coded not shown
          if ( x.lessThan( sortedElements[ i ] ) )
               // do something
          else
               // do something else
          }
     }

Solution 3: Interfaces or "required operations"

interface Comparable
     {
     public boolean lessThan( Object compareMe ); 
     public boolean greaterThan( Object compareMe ); 
     public boolean equal( Object compareMe ); 
     }


class StudentRecord implements Comparable
     {
     public boolean lessThan( Object compareMe )
          {
          // compare student record to object compareMe
          }
     }


class SortedList
     {
     Object[] sortedElements = new Object[ properSize ];

     public void add( Comparable x )
          {
          // coded not shown
          if ( x.lessThan( sortedElements[ i ] )
               // do something
          else
               // do something else
          }
     }

Solution 4: Parametric Polymorphism (Generics, Parameterized Types)

Generics is essentially the ability to have type parameters on your type.
JavaTM 2 Platform Standard Edition 5.0 API Specification

interface Comparable
     {
     public boolean lessThan( Object compareMe ); 
     public boolean greaterThan( Object compareMe ); 
     public boolean equal( Object compareMe ); 
     }
class SortedList <X extends Comparable>
     {
     List<X> sortedElements = new ArrayList<X>( properSize );

     public void add( X x )
          {
          // coded not shown
          if ( x.lessThan( sortedElements[ i ] ) )
               // do something
          else
               // do something else
          }
     }

Modular Coupling - Control Coupling

Passing control flags between modules so that one module controls the sequencing of the processing steps in another module

Common Object Occurrences:

class Lamp {
     public static final ON = 0;
     public void setLamp( int setting ) {
          if ( setting == ON )
               //turn light on
          else if ( setting == 1 )
               // turn light off
          else if ( setting == 2 )
               // blink
     }
}
Lamp reading = new Lamp();
reading.setLamp( Lamp.ON );
reading.setLamp)( 2 );


Control Coupling - Cure 1: Decompose the operation into multiple primitive operations

class Lamp {
     public void on() {//turn light on }
     public void off() {//turn light off }
     public void blink() {//blink }
}


Lamp reading = new Lamp();
reading.on();
reading.blink();
 

Modular Coupling - Control Coupling

Common Object Occurrences:
Example: Returning error codes
class Test {
     public int printFile( File toPrint ) {
          if ( toPrint is corrupted )
               return CORRUPTFLAG;
          //blah blah blah
     }
}


Test when = new Test();
int result =  when.printFile( popQuiz );
if ( result == CORRUPTFLAG )
     //blah
else if ( result == -243 )

Control Coupling - Cure 2: Using Exceptions

class Test 
{
     public int printFile( File toPrint ) throws
                               PrintExeception
     {
          if ( toPrint is corrupted )
               throws new PrintExeception();
          blah blah blah
     }
}

try
{
Test when = new Test(); when.printFile( popQuiz ); } catch ( PrintException printError ) { do something }

Modular Coupling - Global Data Coupling

Two or more modules share the same global data structures

Common Object Occurrence:

A method in one object makes a specific reference to a specific external object

A method in one object makes a specific reference to a specific external object, and to one or more specific methods in the interface to that external object

A component of an object-oriented system has a public interface which consists of items whose values remain constant throughout execution, and whose underlying structures/implementations are hidden

A component of an object-oriented system has a public interface which consists of items whose values remain constant throughout execution, and whose underlying structures/implementations are not hidden

A component of an object-oriented system has a public interface which consists of items whose values do not remain constant throughout execution, and whose underlying structures/implementations are hidden

A component of an object-oriented system has a public interface which consists of items whose values do not remain constant throughout execution, and whose underlying structures/implementations are not hidden


Internal Data Coupling


One module directly modifies local data of another module

Common Object Occurrence:

C++ Friends


Modular Coupling - Lexical Content Coupling

Some or all of the contents of one module are included in the contents of another
Common Object Occurrence:

C/C++ header files



Decrease coupling by:

Restrict what goes in header file

C++ header files should contain only class interface specifications


Object Coupling

Object Coupling diagram

Coupling measures the strength of the physical relationships among the items that comprise an object

Cohesion measures the logical relationship among the items that comprise an object

Interface coupling is the coupling between an object and all objects external to it. Interface coupling is the most desirable form of object coupling. Internal coupling is coupling among the items that make up an object.


Object Coupling - Interface Coupling

Interface coupling occurs when one object refers to another specific object, and the original object makes direct references to one or more items in the specific object's public interface

Includes module coupling already covered

Weakest form of object coupling, but has wide variation

Sub-topics

Object abstraction decoupling
Selector decoupling
Constructor decoupling
Iterator decoupling


Object Abstraction Decoupling


Assumptions that one object makes about a category of other objects are isolated

Example: List items

C++ Example
class LinkedListCell
     {
     int cellItem;
     LinkedListCell* next;

     // code can now use fact that cellItem is an int
     if ( cellItem == 5 ) print( "We Win" );
     }

template <class type>
class LinkedListCell#2
     {
     type cellItem;
     LinkedListCell* next;

     // code does not know the type, it is just a cell item,
     // it becomes an abstraction
     }


Java Example

class LinkedListCellA
     {
     int cellItem;          
     LinkedListCell* next;

     if ( cellItem == 5 ) print( "We Win" );
     }

class LinkedListCellB
     {
     Object cellItem;          
     LinkedListCell* next;

     if ( cellItem.equals( "hi" ) ) print( "We Win" );
     }

interface ListItem
     {
     public operation1();
public operation2();
     }

class LinkedListCellC
     {
     ListItem cellItem;          
     LinkedListCell* next;

     if ( cellItem.operation1() ) print( "We Win" );
     }

Selector Decoupling


Example
: Counter object

class Counter
     {

     int count = 0;

     public void increment()      {  count++; }
     public void reset()      { count = 0; }
     public void display()      {
         // code to display the counter
     }

Display of Counter
Display of Counter

"display" couples the counter object to a particular output type

The counter class can not be used in other setting due to this coupling

Better Counter Class

class Counter
     {
     int count = 0;

     public void increment()      {  count++; }
     public void reset()      { count = 0; }
     public String toString() {return String.valueOf( count );}
     }

Primitive Methods

A primitive method is any method that cannot be implemented simply, efficiently, and reliably without knowledge of the underlying implementation of the object

Primitive methods are:

functionally cohesive, they perform a single specific function

small, seldom exceed five "lines of code"

A composite method is any method constructed from two or more primitive methods ­ sometimes from different objects

Types of Primitive Operations

Selectors (get operations)

Constructors (not the same as class constructors)

Iterators


Selectors

Selectors
are encapsulated operations which return state information about their encapsulated object and do not alter the state of their encapsulated object

Replacing

     public void display()      {
          // code to display the counter
     }

with

public String toString() {return String.valueOf( count );}

is an example of Selector decoupling.

By replacing a composite method (display) with a primitive method the Counter class is decoupled from the display device

This makes the Counter class far more useful

It also moves the responsibility of displaying the counter else where


Constructors

Operations that construct a new, or altered version of an object

class Calendar
     {

     public void getMonth( from where, or what)
          {
          }
     }

class Calendar
     {

     public static Calendar fromString( String date )
          {
          }
     }

Primitive Objects

Primitive objects are objects that are both:


Primitive objects don't count in coupling with other objects

"
An object that refers to itself and to primitive objects is considered for all intents and purposes, totally decoupled from other object"


Composite Object

Object conceptually composed of two or more objects

Heterogeneous Composite Object

Object conceptually composed from objects which are not all conceptually the same

class Date
     {
     int year;
     int month;
     int day;
     }

Homogeneous Composite Object

Object conceptually composed from objects which are not all conceptually the same

list of names - each item is a member of the same general category of object


Iterator

Allows the user to visit all the nodes in a homogeneous composite object and to perform some user-supplied operation at each node

     List grades = new List();

     // add some grades

     while ( grades.nextpointer != null )
          {
          grades = grades.nextpointer^;
          print( grades.examName )
          }

or

     for ( int k = 0; k < grades.length(); k++ )
          print( grades.getExamNameAt( k ) );


Passive Iterator

class List
     {
     Object[] listElements = new Object[ size ];

     public void do( Function userOperation )
          {
          for ( int k = 0; k < listElements.length(); k++ )
               userOperation( listElements[ k ] );
          }
     }

In Main


     List grades = new List();

     aFunction = ( item ){ print( item ) };

     grades.do ( aFunction );


Active Iterator

List grades = new List();

Enumeration gradeList = grades.elements();

while ( gradeList.hasMoreElements() )
     {
     listItem = gradeList.nextElement();
     print ( listItem );
     }

Java Enumerations

public interface Enumeration {
    /**
     * Returns true if the enumeration contains more elements;
false
     * if its empty.
     */
    boolean hasMoreElements();

    /**
     * Returns the next element of the enumeration. Calls to this
     * method will enumerate successive elements.
     */
    Object nextElement();

Inside Internal Object Coupling

Coupling between state and operations of an object

The big issue: Accessing state


Changing the structure of the state of an object requires changing all operations that access the state including operations in subclasses

Solution: Access state via access operations

C++ implementation
Provide private functions to access and change each data member

Simple Cases:

One function to access the value of the date member

One function to change the value of the data member

Only these two functions can access the data member


Accessing State
C++ Example

class Counter
{
public:
     void increment(void);

private:
     int  value;
    
     void  setValue(int newValue);

     int  getValue(void);
};

void Counter::increment(void)    //Increase counter by one
{
     setValue(getValue() + 1);
};

void Counter::setValue(int newValue)
{
     value = newValue;
};

int Counter::getValue
{
     return value;
};

Outside Internal Coupling from Underneath

Coupling between a class and subclass involving private state and private operations


Major Issues:


Outside Internal Coupling from the Side


Class A accesses the private state or private operations of class B

Class A and B are not related via inheritance


Main causes:

Using nonobject-oriented languages

Special language "features"
C++ friends


Donald Knuth

"First create a solution using sound software engineering techniques, then if needed, introduce small violations of good software engineering principles for efficiency's sake."


References

  1. Object Coupling and Object Cohesion, chapter 7 of Essays on Object-Oriented Software Engineering, Vol 1, Berard, Prentice-Hall, 1993
  2. Object-Oriented Design Heuristics, Riel, Addison-Wesley, 1996
  3. On the Criteria To Be Used in Decomposing Systems into Modules, D. L. Parnas, http://www.acm.org/classics/may96/
  4. UML Distilled, Martin Fowler, 3rd ed., Addison Wesley, 2003