CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2001 Module Coupling & Cohesion |
||
---|---|---|
© 2001, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 06-Feb-01 |
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
Each module's structure should be simple enough to understood fully
Concepts for quality
Coupling
Coupling
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 Receiver { public void message( MyType X ) { // code goes here X.doSomethingForMe( Object data ); // more code } }
Modular CouplingData Coupling
Major Problem
Example: Sorting student records, by ID, by Name
How does the SortedList method add() add the new student record object and resort the list? To do this it needs to access the ID (name) fields of StudentRecord!
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
Here the add method actually accesses the StudentRecord method to get the ID. What is wrong with that? Why is this 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 } }Solution 2 Send message to object to compare self to another StudentRecord Object
How is this any better that solution 1? Is it any better? How does it differ?
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. }Solution 3 Interfaces or "required operations"
Notice how the SortedList is no longer coupled to the StudentRecord class. It can be used to sort any list of objects of the same class than implement Comparable.
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 } }Solution 4 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
Note 1: Functors violate the idea that a class is an abstraction with operations and state. Beginners should avoid using the Functor pattern, as they can lead to bad habits. The functor pattern is used here only as a last resort.
Note 2: The Command pattern is similar to the Functor pattern, but contains operations and state.
Note 3: For more information about patterns see: http://www.eli.sdsu.edu/courses/spring98/cs635/notes/patternIntro/patternIntro.html .For more information about Functor and Command pattern see: http://www.eli.sdsu.edu/courses/spring98/cs635/notes/command/command.html
Function Pointers in JavaComparator in Java 2 (JDK 1.2) In Java 2, the Comparator interface defines an interface for objects that act like functions pointers to compare objects.
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]Solution 5 Function Pointers using generic types
typedef int (*compairFun ) ( <type>, <type> );
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();
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
How does this reduce coupling?
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 :
Cohesion
"Cohesion is the degree to which the tasks performed by a single module are functionally related."
Types of Module CohesionFrom Worst to Best
Coincidental (worst)
Logical
Temporal
Procedural
Communication
Sequential
Functional (best)
Module Cohesion Coincidental
Little or no constructive relationship among the elements of the module
Common Object Occurrence:
class Rous { public static int findPattern( String text, String pattern) { // blah} public static int average( Vector numbers ) { // blah} public static OutputStream openFile( String fileName ) { // blah} }
Module Cohesion Logical
Module performs a set of related functions, one of which is selected via function parameter when calling the module
Similar to control coupling
Cure:
public void sample( int flag ) { switch ( flag ) { case ON: // bunch of on stuff break; case OFF: // bunch of off stuff break; case CLOSE: // bunch of close stuff break; case COLOR: // bunch of color stuff break; } }
Module Cohesion Temporal
Elements are grouped into a module because they are all processed within the same limited time period
Common example:
"Initialization" modules that provide default values for objects
"End of Job" modules that clean up
procedure initializeData() { font = "times"; windowSize = "200,400"; foo.name = "Not Set"; foo.size = 12; foo.location = "/usr/local/lib/java"; }
Cure: Each object should have a constructor and destructor
class foo { public foo() { foo.name = "Not Set"; foo.size = 12; foo.location = "/usr/local/lib/java"; } }
Sample Configuration File
[Macintosh] |
[General] |
EquationWindow=146,171,406,661 |
Zoom=200 |
SpacingWindow=0,0,0,0 |
CustomZoom=150 |
|
ShowAll=0 |
|
Version=2.01 |
[Spacing] |
OptimalPrinter=1 |
LineSpacing=150% |
MinRect=0 |
MatrixRowSpacing=150% |
ForceOpen=0 |
MatrixColSpacing=100% |
ToolbarDocked=1 |
SuperscriptHeight=45% |
ToolbarShown=1 |
SubscriptDepth=25% |
ToolbarDockPos=1 |
LimHeight=25% |
|
LimDepth=100% |
[Fonts] |
LimLineSpacing=100% |
Text=Times |
NumerHeight=35% |
Function=Times |
DenomDepth=100% |
Variable=Times,I |
FractBarOver=1pt |
LCGreek=Symbol,I |
FractBarThick=0.5pt |
UCGreek=Symbol |
SubFractBarThick=0.25pt |
Symbol=Symbol |
FenceOver=1pt |
Vector=Times,B |
SpacingFactor=100% |
Number=Times |
MinGap=8% |
|
RadicalGap=2pt |
[Sizes] |
EmbellGap=1.5pt |
Full=12pt |
PrimeHeight=45% |
Script=7pt |
|
ScriptScript=5pt |
|
Symbol=18pt |
|
SubSymbol=12pt |
Module Cohesion Procedural
Associates processing elements on the basis of their procedural or algorithmic relationships
Procedural modules are application specific
In context the module seems reasonable
Removed from the context these modules seem strange and very hard to understand
Module Cohesion Communication
Operations of a module all operate upon the same input data set and/or produce the same output data
Cure:
Module Cohesion Sequential
Sequential association the type in which the output data from one processing element serve as input data for the next processing element
A module that performs multiple sequential functions where the sequential relationship among all of the functions is implied by the problems or application statement and where there is a data relationship among all of the functions
Cure:
Module Cohesion Functional
If the operations of a module can be collectively described as a single specific function in a coherent way, the module has functional cohesion
If not, the module has lower type of cohesion
In an object-oriented system:
Module Cohesion Informational Strength
Myers states:
"The purpose of an informational-strength module is to hide some concept, data structure, or resource within a single module.
An informational-strength module has the following definition: