'How to Build Better Object Models'
Download
Report
Transcript 'How to Build Better Object Models'
JAVA DESIGN
Building Better Apps and Applets
Peter Coad
Object
International, Inc.
www.oi.com [email protected]
1-919-772-9350 fax -9389
direct line -7734 direct fax -8916
Version Date:
Jan. 2, 1998
1
Purpose and Agenda
Purpose
To
gain insights into better design
Agenda
1.
Design with Composition, Rather than Inheritance.
2. Design with Interfaces.
3. Design with Threads.
4. Design with Notification.
5. Build with Java.
"Building materials profoundly affect design techniques."
2
1. Design with Composition,
Rather than Inheritance
Inheritance extends attributes and
methods.
"What’s
the same; what’s different."
Weak encapsulation with respect to superclasses
Awkward accommodation of change over time
Composition delegates
Message-based
work to other objects.
encapsulation
Gracious accommodation of change over time
3
Inheritance: Good Uses (i)
MomentInterval
dateTime
number
Reservation
4
Purchase
dateTimeExpiration
amount
notifyPendingExpiration
total
public abstract class MomentInterval extends Object { /*code*/ }
public class Reservation extends MomentInterval { /*code*/ }
public class Purchase extends MomentInterval { /*code*/ }
Inheritance: Good Uses (ii)
5
Inheritance: Problems (i)
6
Inheritance: Problems (ii)
7
Inheritance: When to Use It
8
Composition
9
public class Passenger extends Object {
private Vector reservations; /*created by constructor*/ }
Composition and Change (i)
10
Composition and Change (ii)
11
Combo
12
2. Design with Interfaces
A common
set of method signatures
Interfaces let you connect to and message...
An
object in any class that implements that interface
Rather than an object in a specific class.
IN a m e
g e tN a m e
s e tN a m e
public interface IName {
public String getName();
public void setName(String aName); }
interface
Person
IName
13
implementer
public Person extends Object implements IName {
public String getName() {/*code*/}
public void setName(String aName) {/*code*/} }
Why Design with Interfaces?
Abstraction of
common method signatures
Abstract
upwards and avoid method signature
overload.
Interaction substitution
Interact
with objects from one class as if it were an
object from another class.
Part substitution
Unplug
an object from one class and plug in an
object from another class.
14
Interface Strategies (First Set) -Factor Out
a. Strategy: factor-out common method signatures.
b. Strategy: factor-out proxies.
c. Strategy: factor-out by analogy.
d. Strategy: factor-out future expansion.
15
a. Common Method Signatures (i)
Strategy: factor-out common method signatures.
Person
name
address
n
1
number
howMuch
Store
Sale
Customer
number
dateTime
n
1
calcTotal
calcTax
1
n
grandTotal
howMuch
n
1-n
1
1
Item
SaleLineItem
quantity
calcTotal
calcTax
16
1
n
number
description
price
howMany
a. Common Method Signatures (ii)
Customer
Person
name
address
n
1
number
ICount
Sale
n
1
dateTime
ISell
1
n
1-n
Store
ICount
number
howMany
ITotal
ICount
n
ITotal
calcTotal
ITax
1
1
calcTax
Item
SaleLineItem
quantity
ISell
1
n
number
description
price
ISell
ITotal
ITax
ICount
public interface ISell extends ITotal, ITax {}
b. Proxies (i)
Strategy: factor-out proxies.
Person
name
address
INameAddress
1
1
Passenger
type
number
IName
getName
setName
IAddress
getAddress
setAddress
Person
name
address
INameAddress
18
1
1
Passenger
type
number
INameAddress
INameAddress
IName
IAddress
b. Proxies (ii)
INameAddress
NameAddressUI
display
19
n
IName
IAddress
public class NameAddressUI {
private Vector nameAddresses; /*created by constructor*/
public void addNameAddress(INameAddress aNameAddress) {
this.nameAddresses.addElement(aNameAddress); }
public void display() {
Enumeration nameAddressList = this.nameAddresses.elements();
while (nameAddressList.hasMoreElements()) {
String nameAddress = (String) nameAddressList.nextElement();
/*display using nameAddress.getName()*/
/*display using nameAddress.getAddress()*/ } } }
c. By Analogy (i)
Strategy: factor-out by analogy.
FlightDescription
hasAvailableSeat
reserveSeat
cancelSeat
FlightDescription
IDateReserve
20
IDateReserve
available (date)
reserve (date, reserver)
cancel (date, reserver)
c. By Analogy (ii)
DateReserveUI
invokeAvailable
invokeReserve
invokeCancel
n
IDateReserve
available (date)
reserve (date, reserver)
cancel (date, reserver)
FlightDescription
IDateReserve
21
c. By Analogy (iii)
DailyWorkOrder
reserveResources
22
n
IDateReserve
available (date)
reserve (date, reserver)
cancel (date, reserver)
Equipment
Worker
Workspace
IDateReserve
IDateReserve
IDateReserve
public class DailyWorkOrder {
private Vector dateReservables; /*created by constructor*/
public void addDateReserves(IDateReserve aDateReserve) {
this.dateReservables.addElement(aDateReserve); }
public void reserveResources() {
Enumeration dateReservableList = this.dateReservables.elements();
while (dateReservableList.hasMoreElements()) {
IDateReserve dateReservable = (IDateReserve) dateReservablesList.nextElement();
dateReservable.reserve(); } } }
d. Future Expansion (i)
Strategy: factor-out for future expansion.
Zone
IActivate
IActivate
n
activate
deactivate
1
Sensor
IActivate
Coad notation
23
UML notation
d. Future Expansion (ii)
IActivate
Zone
IActivate
n
activate
deactivate
Sensor
IActivate
24
d. Future Expansion (iii)
IActivate
Zone
IActivate
25
activate
deactivate
n
Sensor
Motor
IActivate
IActivate
RobotArm
IActivate
Switch
IActivate
public class Zone {
private Vector activatibles; /*created by constructor*/
public void addActivate(IActivate anActivate) {
this.activatibles.addElement(anActivate);
public void activate() {
Enumeration activatableList = this.activatables.elements();
while (activatableList.hasMoreElements()) {
IActivatable activatable = (IActivate) activatableList.nextElement();
activatable.activate(); } } }
Interface Strategies (Second Set) -Design-In
e. Strategy: design-in, from features to interfaces.
f. Strategy: design-in, from role to interfaces to proxies.
g. Strategy: design-in, from collections to interfaces.
h. Strategy: design-in, from scenarios to interfaces.
i. Strategy: design-in, from intra-class roles to interfaces.
j. Strategy: design-in, from plug-in methods to interfaces.
26
e. Features to Interfaces (i)
Strategy: design-in, from
features to interfaces.
Look
for a common feature, one you need to provide
in different contexts.
Identify a set of common method names that
correspond to that feature.
Add an interface.
Identify implementers.
27
Features:
Total outstanding balances for a borrower
Total outstanding balances for an applicant.
List accounts and limits for a borrower.
List accounts and limits for an applicant.
e. Features to Interfaces (ii)
IAccount
totalOustandingBorrowingBalance
listBorrowingAccountsAndLimits
Applicant
IAccount
28
BorrowingAccount
Borrower
0-1
1
IAccount
0-1
1
getBalance
public interface IAccount {
public double totalOutstandingBorrowingBalance();
public Enumeration listBorrowingAccountsAndLimits(); }
f. Role to Interface to Proxies (i)
Strategy: design-in, from role
to interface to proxies.
Take
a role and turn its method signatures into a roleinspired interface.
Let a party or a role offer that same interface by:
Implementing that interface, and
Delegating the real work to the original role player.
Borrower
29
totalApprovedLimits
totalAvailableLimits
f. Role to Interface to Proxies (ii)
Borrower
IBorrow
totalApprovedLimits
totalAvailableLimits
IBorrow
Party
IBorrow
Person
30
Borrower
Applicant
n
Organization
1
IBorrow
0-1
1
IBorrow
IBorrow
totalApprovedLimits
totalAvailableLimits
f. Role to Interface to Proxies (iii)
31
public interface IBorrow {
public double totalApprovedLimits();
public double totalAvailableLimits();
}
public Borrower extends Object implements IBorrow {
public double totalApprovedLimits() {/*real work*/} }
public double totalAvailableLimits() {/*real work*/}
}
public Applicant extends Object implements IBorrow {
private Borrower borrower; /*add/remove with add/remove
methods*/
public double totalApprovedLimits() {
return this.borrower. totalApprovedLimits (); /*delegate*/}
public double totalAvailableLimits() {
return this.borrower. totalAvailableLimits (); /*delegate*/}
g. Collections and Members to Interfaces (i)
Strategy: design-in, from
collections and members to
interfaces.
Does
your object hold a collection of other objects? If so:
Consider its potential method signatures.
If other collections might offer the same set of method
signatures, then design-in that common interface.
Is
your object a member within a collection? If so:
If that object needs to provide an interface similar to the
collections it is in, then design-in that common interface.
Identify
32
implementers.
g. Collections and Members to Interfaces (ii)
ITotalApprovedLimit
totalApprovedLimit
ICompareAppliedVsApproved
compareAppliedVsApproved
Approval
Application
ITotalApprovedLimit
ICompareAppliedVsApproved
33
n
1
ITotalApprovedLImit
ICompareAppliedVsApproved
h. Scenarios to Interfaces(i)
Strategy: design-in, from scenarios to interfaces.
Look
for similar interaction patterns.
Add an interface-implementer column.
Use this naming convention:
I<what it does> Implementer
Add
an interface: I<what it does>.
Identify implementers.
34
h. Scenarios to Interfaces (ii)
Applicant
Application
assessProfit
assessRisk
assessRisk
Borrower
BorrowingAccount
assessProfit
assessRisk
assessProfit
assessRisk
assessProfit
( ; profit)
( ; profit)
assessProfit
n
assessProfit
assessRisk
n
Name:
Assess profit and risk (i).
Constraints:
( ; risk)
( ; risk)
assessRisk
assessRisk
n
Applicant
assessProfit
assessRisk
IAssessProfitAndRisk Implementer
assessProfit
assessRisk
assessProfit
Name:
Assess profit and risk (ii).
Constraints:
assessProfit
( ; profit)
( ; profit)
assessRisk
( ; risk)
( ; risk)
assessRisk
35
assessRisk
h. Scenarios to Interfaces (iii)
Application
Applicant
n
1
0-1
1
IAssessProfitAndRisk
IAssessRisk
IAssessProfitAndRisk
assessProfit
IAssessRisk
Borrower
IAssessProfitAndRisk
assessRisk
36
IAssessRisk
assessRisk
BorrowingAccount
n
1
IAssessProfitAndRisk
i. Intra-Class Roles to Interfaces (i)
Strategy: design-in, from intra-class roles
to
interfaces.
Identify
roles that objects within a class can play.
Establish an interface for each of those roles.
Identify implementers.
37
i. Intra-Class Roles to Interfaces (ii)
Account
ITransferSource
ITransferDestination
Account [from]
Account [to]
transferFrom
transferTo
transferFrom
transferTo
38
ITransferSource
transferTo
ITransferDestination
transferFrom
Name:
Transfer from one account to another.
Constraints:
(amount, transferTo ; result)
(amount ; result)
j. Plug-In Methods to Interfaces (i)
Strategy: design-in, from plug-in
methods to
interfaces.
Look
for useful functionality you’d like to "plug in.”
Add a plug-point, using an interface.
Identify implementers.
39
j. Plug-In Methods to Interfaces (ii)
Term
validate
Term
validate
1
IValidateTerm
validateTerm
IValidateTerm Implementer
validateTerm
validate
validateTerm
public interface IValidateTerm {
public int validateTerm(); }
Name:
Validate a term.
Constraints:
( ; result)
( ; result)
public class Term extends Object {
private IValidateTerm validater;
public int validate() {
return validater.validateTerm(); }
3. Design with Threads
A thread is
41
a stream of program execution.
Multiple Threads
42
this.mainThread = new Thread (this);
this.mainThread.start();
Why Threads
43
Threads and Shared Values (a)
44
Threads and Shared Values (b)
45
Threads and Synchronized Methods (a)
FlightDescription
ScheduledFlight
reserve
reserve
reserve
Name:
Reserve space.
Constraints:
(passenger, date; reservation)
SYNC
reserve
ENDSYNC
(passenger; reservation)
SYNC
reserve
ENDSYNC
(passenger; reservation)
reserve
public class ScheduledFlight extends Object {
public synchronized Reservation reserve(Passenger aPassenger, Date aDate) {/*code*/} }
46
Threads and Synchronized Methods (b)
At
the start of a sync’d method,
just one thread may enter.
That thread can invoke non-syncs
no waiting.
That thread can invoke syncs in the same object
no waiting.
That thread can invoke syncs in other objects
potential waiting, potential for deadlock.
47
Threads and Synchronized Methods (c)
.
48
Thread Strategies (a)
"Short Synchronized Methods" Strategy
Keep
synchronized methods short and to the point.
"Thread Gatekeeper" Strategy
Use
a thread gatekeeper, an object that permits just
one thread at a time into a collection of crossmessaging objects.
49
Thread Strategies (b)
"Four Thread Designs" Strategy
Single
Prioritized
objects (hi-pri objects, low-pri objects)
Prioritized methods (hi-pri methods; low-pri methods)
Combo
50
4. Design with Notification
51
Observer-Observable (inheritance)
inheritance of a
utility function--gack!
overly restrictive
interface--gack!
Observable
addObserver
deleteObserver
deleteObservers
notifyObservers
Observer
n
update
java.util
SpecializedObservable
status
methodResultingInStateChg
getStatus
52
public interface Observer {
void update (Observable observed; Object argument); }
Observer-Observable (composition sketch)
53
Observer-Observable (composition model)
MyObservable
myValue
IObservable
IMyInterface
ObservableComponent
1 [IObservableComponent]
IObservableComponent
n [IObserver]
MyObserver
IObserver
1 [IMyInterface]
IObservable
addIObserver
deleteIObserver
deleteIObservers
IMyInterface
getMyValue
setMyValue
IObservableComponent
IObservable
notifyIObservers
IObserver
update
public class ObservableComponent extends Object implements IObservableComponent {
private Vector myObservers; /*created by constructor*/
public void notifyObservers() {
Vector copyMyObservers = (Vector) this.myObservers.clone();
Enumeration myObserverList = this.copyMyObservers.elements();
while (myObserverList.hasMoreElements()) {
IObserver observer = (IObserver) myObserverList.nextElement();
observer.update(); } } }
Threaded Observer-Observable (sketch)
55
Threaded Observer-Observable (model)
Thread
ThreadedObservableComponent
MyObservable
myValue
IObservable
IMyInterface
1 [IObservableComponent]
IObservableComponent
Runnable
1
1 [Runnable]
n [IObserver]
start
MyObserver
IObserver
1 [IMyInterface]
IMyInterface
getMyValue
setMyValue
IObservable
addIObserver
deleteIObserver
deleteIObservers
IObservableComponent
IObservable
notifyIObservers
Runnable
run
IObserver
update
public class ThreadedObservableComponent extends Object
implements IObservableComponent,Runnable {
public void notifyObservers() {
this.addToNotificationQueue(observers, observable, changeCode); }
public void run() {
do
// get the next observers-observable-changeCode triad
// and notify the observers
while this.removeFromNoticationQueue(); } }
Event Source–Event Listener (sketch)
57
Event Source–Event Listener (model)
EventObject
EventListener
<none>
java.util
getSource
MyEvent
myData
getMyData
MyEventSource
addMyEventListener
removeMyEventListener
fireMyEvent
n
MyEventListener
EventListener
change
public class MyEventSource extends Object {
private Vector myEventListeners; /*created by constructor*/
public void fireMyEvent() {
MyEvent myEvent = new MyEvent(); /*then initialize it*/
Vector copyMyEventListeners = (Vector) this.myEventListeners.clone();
Enumeration myEventListenerList = copyMyEventListeners.elements();
MyEventListener myEventListener = (MyEventListener) myEventListenerList.nextElement();
myEventListener.change(myEvent); } } }
Property Change Source-Support-Listener (sketch)
59
Property Change Source-Support-Listener (model)
PropertyChangeEvent
source
propertyName
oldValue
newValue
PropertyChangeSupport
addPropertyChangeListener
removePropertyChangeListener
firePropertyChange
1
PropertyChangeListener
n
EventListener
propertyChange
java.beans
better design: define and use
an IPropertyChangeSupport
interface
MyObject
addPropertyChangeListener
removePropertyChangeListener
set<propertyName>
public class MyObject extends Object {
private String name;
private PropertyChangeSupport myPropertyChangeSupport;
public void setName(String aName) {
this.name = aName;
myPropertyChangeSupport.firePropertyChange(); } }
5. Build with Java
Design
it.
Build it with the "Chia Pet” Process J
Class
H
...
Initialize and implement
Access
Print
Equal
Test
61
A "Chia Pet" is a small toy, marketed
in various parts of the globe and honorably
mentioned in the movie, Wayne's World.
The letters C,I,A,P,E,T represent the steps
in this process.
Acknowledgement:
Jill Nicola developed this
process. [Nicola 97]
Design It.
Individual
name
borrower
TestIndividual
main
62
IName
getName
setName
1
-
IName
IBorrow
equals
toString
buildTestObject
0-1
IBorrow
totalOutstandingBalance
Class
// Individual.java
public class Individual extends Object implements IName, IBorrow {
private String name;
//attribute
private IBorrow borrower;
//object connection
public String getName() {/*code*/}
//methods
public void setName(String aName) {/*code*/}
public double totalOutstandingBalance() {/*code*/} }
// IBorrow.java
public interface IBorrow {
public double totalOutstandingBalance(); }
// IName.java
public interface IName {
public String getName();
public void setName (String aName); }
63
Initialize and Implement
// Individual.java (cont.)
public Individual(String aName) {
this.name = aName;
this.borrower = null; }
public String getName() {
return this.name; }
public void setName(String aName) {
this.name = aName; }
public double totalOutstandingBalance() {
if (this.borrower != null)
return this.borrower.totalOutstandingBalance(); /*delegates*/
else return 0.0d; }
64
Access
// Individual.java (cont.)
public String getName() {
return this.name; }
public void setName(String aName) {
this.name = aName; } }
65
Print
// Individual.java (cont.)
public static int MAXNAMELEN = 100;
public String toString() {
StringBuffer aStringBuffer = new StringBuffer(MAXNAMELEN);
aStringBuffer.append(this.getName());
if (this.borrower != null)
aStringBuffer.append(" is an individual and a borrower.");
else
aStringBuffer.append(" is an individual, not a borrower.");
return aStringBuffer.toString(); } }
66
meaning: does this other object in the same class
have equal attribute values and object connections?
Equal
// Individual.java (cont.)
public boolean equals(Object anObject) {
if ((anObject != null) && (anObject instanceof Individual)) {
Individual otherIndividual = (Individual)anObject;
return
this.name.equals(otherIndividual.name) &&
this.borrower.equals(otherIndividual.borrower); }
else return false; } }
67
Test
// Individual.java (cont.)
public static Individual buildTestObject() {
return new Individual ("Fred Flinstone”); } }
// TestIndividual.java
import java.io.*;
public class TestIndividual {
public static void main(String args[]) {
Individual anIndividual = Individual.buildTestObject();
for (int i = 0; i < 5; i++) System.out.println();
System.out.println("This is a test.");
System.out.println(anIndividual.toString());
System.out.println("The end.");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// A "stream" is a data-holding area with an associated cursor.
// An "input stream reader" reads characters from a byte input stream.
// A "buffered reader" reads a buffer of characters from a reader.
try {String line = in.readLine(); }
68 catch(Exception e) {System.out.println("readLine exception"); } } }
Summary
Purpose
To
gain insights into better design
Agenda
1.
Design with Composition, Rather than Inheritance.
2. Design with Interfaces.
3. Design with Threads.
4. Design with Notification.
5. Build with Java.
69
For more Java Design,
see [Coad97a]
For More...
Newsletter
Coad,"The Coad Letter." Free technical newsletter. Subscribe at
www.oi.com/newsletters.htm
Books and
70
Workshop Notes
[Coad97a] Coad and Mayfield, Java Design: Building Better Apps and
Applets. Prentice Hall, 1997.
[Coad97b] Coad, North, Mayfield, Object Models: Strategies, Patterns,
and Applications. Second Edition. Prentice Hall, 1997.
[Coad97c] Coad, Peter, How to Build Better Object Models. Workshop
Notes. Object International, 1997.
[Nicola97] Nicola, Jill, Java Programming: A Design-Centric Approach.
Workshop Notes. Object International, 1997.
JAVA DESIGN
Building Better Apps and Applets
Peter Coad
Object
International, Inc.
www.oi.com [email protected]
1-919-772-9350 fax -9389
direct line -7734 direct fax -8916
71