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.
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
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
Decomposable system
A nearly decomposable system
Design Goal
Parnas compared two different implementations:
List
Each module should hide a design decision
All ways of decomposing an application are not equal
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
"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
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 } }
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 );
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 } }
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 } }
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 } }
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 } }
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 );
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();
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 )
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 }
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
Common Object Occurrence:
C++ Friends
C/C++ header files
Decrease coupling by:
Restrict what goes in header file
C++ header files should contain only class interface specifications
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
Example: List items
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 }
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" ); }
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" 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 are:
A composite method is any method constructed from two or more primitive methods sometimes from different objects
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
class Calendar { public void getMonth( from where, or what) { } }
class Calendar { public static Calendar fromString( String date ) { } }
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
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 ) }
for ( int k = 0; k < grades.length(); k++ ) print( grades.getExamNameAt( k ) );
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 );
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();
Coupling between state and operations of an object
The big issue: Accessing state
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
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; };
Major Issues:
Class A and B are not related via inheritance
Main causes:
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."