CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2002 Coupling |
||
---|---|---|
© 2002, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 21-Mar-02 |
Object-Oriented Design Heuristics, Riel, Addison-Wesley, 1996
Quality of Objects
Decomposing systems into smaller pieces aids software development
Parnas (72) KWIC (Simple key word in context) experiment
Parnas compared two different implementations
Parnas's Criteria
Primary goal of decomposition into modules is reduction of software cost
Specific goals of module decomposition
Metrics for quality
Coupling
Coupling
Relationships between Objects
Type of Relations:
Type |
Relation
between
|
Uses |
(Object) |
Containment |
(Object) |
Inheritance |
(Class) |
Association |
(Object) |
Different Ways to Implement Uses
How does the sender access the receiver?
1. Containment
class Sender { Receiver here; public void method() { here.sendAMessage(); } }
class Sender { public void method(Receiver here) { here.sendAMessage(); } }3. Ask someone else
class Sender { public void method() { Receiver here = someoneElse.getReceiver(); here.sendAMessage(); } }
class Sender { public void method() { Receiver here = new Receiver(); here.sendAMessage(); } }
Heuristics for the Uses Relationship
4.1 Minimize the number of classes with another class collaborates
4.2 Minimize the number of message sends between a class and its collaborator
4.3 Minimize the number of different messages a class sends to another class.
4.4 Minimize the product of the number of methods in a class and the number of different messages they send.
Which is more complex?
Decomposable system
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 CouplingIn order of desirability
Data Coupling (weakest – most desirable)
Control Coupling
Global Data Coupling
Internal Data Coupling (strongest – least desirable)
Content Coupling (Unrated)
Modular Coupling Data Coupling
Output from one module is the input to another
Using parameter lists to pass items between routines
Common Object Occurrence:
class ObjectBClass{ public void message( ObjectXClass X ){ // code goes here X.doSomethingForMe( Object data ); // more code } }
Modular CouplingData Coupling
Major Problem
Example: Sorting student records, by ID, by Name
class StudentRecord { Name lastName; Name firstName; long ID; public Name getLastName() { return lastName; } // etc. } SortedList cs535 = new SortedList(); StudentRecord newStudent; //etc. cs535.add ( newStudent );
Solution 1 Bad News
class SortedList { Object[] sortedElements = new Object[ properSize ]; public void add( StudentRecord X ) { // coded not shown Name a = X.getLastName(); Name b = sortedElements[ K ].getLastName(); if ( a.lessThan( b ) ) // do something else // do something else } } SortList>>add: aStudentRecord Blah a := aStudentRecord lastName. b := sortedElements at: k. blahSolution 2 Send message to object to compare self to another StudentRecord Object
class SortedList{ Object[] sortedElements = new Object[ properSize ]; public void add( StudentRecord X ) { // coded not shown if ( X.lessthan( sortedElements[ K ] ) ) // do something else // do something else } } class StudentRecord{ private Name lastName; private long ID; public boolean lessThan( Object compareMe ) { return lastName.lessThan( compareMe.lastName ); } etc. } SortList>>add: aStudentRecord Blah aStudentRecord < sortedElements last ifTrue: [ more blah ] ifFalse: [ blah blah ] blahSolution 3 Program to an Interface or "required operations"
interface Comparable { public boolean lessThan( Object compareMe ); public boolean greaterThan( Object compareMe ); public boolean equal( Object compareMe ); } class StudentRecord implements Comparable { private Name lastName; private long ID; public boolean lessThan( Object compareMe ) { return lastName.lessThan( ((Name)compareMe).lastName ); } } class SortedList { Object[] sortedElements = new Object[ properSize ]; public void add( Comparable X ) { // coded not shown if ( X.lessthan( sortedElements[ K ] ) // do something else // do something else } } SortList>>add: anObject anObject < sortedElements last ifTrue: [ more blah ] ifFalse: [ blah blah ] blahSolution 4 Strategy Pattern & Blocks
| sortedStudents | sortedStudents := SortedCollection sortBlock: [:x :y | x lastName < y lastName]. blah sortedStudents add: roger; add: pete; add: sam.sortedStudents sortBlock: [:x :y | x grade < y grade ]
Solution 4 Strategy Pattern & Function Pointers
Code is neither legal C/C++ nor Java. The idea is to pass in a function pointer to the SortList object, which it uses to compare the objects in the list.
typedef int (*compareFun ) ( StudentRecord, StudentRecord ); class SortedList { StudentRecord[] sortedElements = new StudentRecord[ properSize ]; int (*compare ) ( StudentRecord, StudentRecord ); public setCompare( compairFun newCompare ) { compare = newCompare; } public void add( StudentRecord X ) { // coded not shown if ( compare( X, sortedElements[ K ] ) ) // code not shown } } int compareID( StudentRecord a, StudentRecord b ) { // code not shown } int compareName( StudentRecord a, StudentRecord b ) { // code not shown } SortedList myList = new SortedList(); myList.setCompair( compareID );
Functor PatternFunctions as Objects
Functors are functions that behave like objects
They serve the role of a function, but can be created, passed as parameters, and manipulated like objects
A functor is a class with a single member function
Function Pointers in JavaComparator in Java 2 (JDK 1.2)
Methods in Comparator Interface int compare(Object o1, Object o2)
Comparator Example import java.util. Comparator; class Student { String name; int id; public Student( String newName, int id ) { name = newName; this.id = id; } public String toString() { return name + ":" + id; } } final class StudentNameComparator implements Comparator { public int compare( Object leftOp, Object rightOp ) { String leftName = ((Student) leftOp).name; String rightName = ((Student) rightOp).name; return leftName.compareTo( rightName ); } public boolean equals( Object comparator ) { return comparator instanceof StudentNameComparator; } }
//Comparator Example Continued final class StudentIdComparator implements Comparator { static final int LESS_THAN = -1; static final int GREATER_THAN = 1; static final int EQUAL = 0; public int compare( Object leftOp, Object rightOp ) { long leftId = ((Student) leftOp).id; long rightId = ((Student) rightOp).id; if ( leftId < rightId ) return LESS_THAN; else if ( leftId > rightId ) return GREATER_THAN; else return EQUAL; } public boolean equals( Object comparator ) { return comparator instanceof StudentIdComparator; } }
//Comparator Example Continued import java.util.*; public class Test { public static void main(String args[]) { Student[] cs596 = { new Student( "Li", 1 ), new Student( "Swen", 2 ), new Student( "Chan", 3 ) }; //Sort the array Arrays.sort( cs596, new StudentNameComparator() ); for ( int k = 0; k < cs596.length; k++ ) System.out.print( cs596[k].toString() + ", " ); System.out.println( ); List cs596List = new ArrayList( ); cs596List.add( new Student( "Li", 1 ) ); cs596List.add( new Student( "Swen", 2 ) ); cs596List.add( new Student( "Chan", 3 ) ); System.out.println( "Unsorted list " + cs596List ); //Sort the list Collections.sort( cs596List, new StudentNameComparator() ); System.out.println( "Sorted list " + cs596List ); //TreeSets are aways sorted TreeSet cs596Set = new TreeSet( new StudentNameComparator() ); cs596Set.add( new Student( "Li", 1 ) ); cs596Set.add( new Student( "Swen", 2 ) ); cs596Set.add( new Student( "Chan", 3 ) ); System.out.println( "Sorted Set " + cs596Set ); } }
//Comparator Example ContinuedOutput Chan:3, Li:1, Swen:2, Unsorted list [Li:1, Swen:2, Chan:3] Sorted list [Chan:3, Li:1, Swen:2] Sorted Set [Chan:3, Li:1, Swen:2]
Sorting With Different Keys import java.util.*; public class MultipleSorts { public static void main(String args[]) { List cs596List = new ArrayList( ); cs596List.add( new Student( "Li", 1 ) ); cs596List.add( new Student( "Swen", 2 ) ); cs596List.add( new Student( "Chan", 3 ) ); Collections.sort( cs596List, new StudentNameComparator() ); System.out.println( "Name Sorted list " + cs596List ); Collections.sort( cs596List, new StudentIdComparator() ); System.out.println( "Id Sorted list " + cs596List ); TreeSet cs596Set = new TreeSet( new StudentNameComparator()); cs596Set.addAll( cs596List ); System.out.println( "Name Sorted Set " + cs596Set ); TreeSet cs596IdSet = new TreeSet( new StudentIdComparator() ); cs596IdSet.addAll( cs596List ); System.out.println( "Id Sorted Set " + cs596IdSet ); } }Output Name Sorted list [Chan:1, Li:2, Swen:1] Id Sorted list [Chan:1, Swen:1, Li:2] Name Sorted Set [Chan:1, Li:2, Swen:1] Id Sorted Set [Chan:1, Li:2]
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 );Cure:
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();
Is this Control Coupling?
BankAccount>>withdrawal: aFloat balance := balance – aFloat.
What about?
BankAccount>>withdrawal: aFloat balance < aFloat ifTrue: [ self bounceThisCheck ] ifFalse: [balance := balance – aFloat]
Control Coupling
Common Object Occurrences:
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 )Cure: Use 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:
Modular Coupling Lexical Content Coupling
Some or all of the contents of one module are included in the contents of another
Common Object Occurrence :
Object Coupling
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
Assumptions that one object makes about a category of other objects are isolated and used as parameters to instantiate the original object.
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.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 in a slider bar }
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 int count() {return count;} 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:
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 elsewhere
Constructors
Operations that construct a new, or altered version of an object
class Calendar { public void getMonth( from where, or what) { blah } }
class Calendar { public static Calendar fromString( String date ) { blah} }
Primitive Objects
Primitive objects are objects that are both:
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 all conceptually the same
list of names - each item is a member of the same general category of object – a name
Iterator
Allows the user to visit all the nodes in a homogeneous composite object and to perform some user-supplied operation at each node
Both Java and C++ support iterators
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(); Iterator gradeList = grades.iterator(); while ( gradeList.hasNext() ){ listItem = gradeList.next(); print ( listItem ); }
Java Enumeration/Iterator
Methods |
||
Enumeration |
Iterator |
ListIterator |
hasMoreElements() |
hasNext() |
hasNext() |
nextElement() |
next() |
next() |
|
remove() |
remove() |
|
|
nextIndex() |
|
|
hasPrevious() |
|
|
previous() |
|
|
previousIndex() |
|
|
add() |
|
|
set() |
Iterators and Coupling
Array int[] list for (int k = 0; k < list.length; k ++ ) System.out.println( list[k] );Vector Vector list for (int k =0; k < list.size(); k++ ) Sytem.out.println( list.elementAt( k ) );Binary Search Tree BinarySeachTree list Node current = list.root(); Stack previous = new Stack(); Previous.push( current ); while (current != null ) { a lot of code here }
Java Collection Classes
There are synchronized, unsynchronized, modifiable unmodifiable versions of each collection/map
One can set the modifiable and synchronized property separately
What about Arrays?
One can convert an array of objects to a list
String[] example = new String[10]; List listBackedByArray = Arrays.asList( example );
Changes to the array(list) are reflected in the list(array)
Less Coupling with Iterators
Collection list; Iterator elements = list.iterator(); while (elements.hasNext() ) { System.out.println( elements.next() ); }
In this code list could be any type of collection, so is more flexible. It is not coupled to a particular type of collection.
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
Accessing StateC++ 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 private state or private operations of class B
Class A and B are not related via inheritance
Main causes: