710 likes | 844 Vues
This document outlines strategies for building better Java applications and applets, focusing on key design principles. It emphasizes the use of composition over inheritance to ensure flexibility and better encapsulation. Additionally, it discusses the importance of interfaces for abstraction and ease of substitution between objects. Topics covered include designing with threads for improved performance and leveraging notification mechanisms. By following these guidelines, developers can create more robust, maintainable, and adaptable Java applications.
E N D
JAVA DESIGNBuilding Better Apps and Applets • Peter Coad • Object International, Inc. • www.oi.com pc@oi.com • 1-919-772-9350 fax -9389 • direct line -7734 direct fax -8916 Version Date: Jan. 2, 1998
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."
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 work to other objects. • Message-based encapsulation • Gracious accommodation of change over time
Inheritance: Good Uses (i) MomentInterval dateTime number Reservation Purchase dateTimeExpiration amount notifyPendingExpiration total publicabstractclass MomentInterval extends Object { /*code*/ } publicclass Reservation extends MomentInterval { /*code*/ } publicclass Purchase extends MomentInterval { /*code*/ }
Composition publicclass Passenger extends Object { private Vector reservations; /*created by constructor*/ }
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. publicinterface IName { public String getName(); publicvoid setName(String aName); } interface public Person extends Object implements IName { public String getName() {/*code*/} publicvoid setName(String aName) {/*code*/} } Person IName implementer
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.
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.
a. Common Method Signatures (i) • Strategy: factor-out common method signatures. Person Store Sale Customer name number dateTime number address n 1 n 1 1 n grandTotal calcTotal howMuch howMuch calcTax n 1-n 1 1 Item SaleLineItem number quantity description 1 n calcTotal price calcTax howMany
a. Common Method Signatures (ii) Store ICount Customer Person Sale number howMany number name dateTime n 1 n 1 1 n ITotal address ICount ISell ICount ITotal 1-n calcTotal n ITax 1 calcTax 1 Item ISell SaleLineItem number ITotal quantity description ITax 1 n price ISell ICount publicinterface ISell extends ITotal, ITax {}
b. Proxies (i) • Strategy: factor-out proxies. Passenger Person type name IName 1 1 number address getName INameAddress setName IAddress getAddress setAddress INameAddress Person Passenger IName name type IAddress address number 1 1 INameAddress INameAddress
b. Proxies (ii) INameAddress NameAddressUI IName n IAddress display publicclass NameAddressUI { private Vector nameAddresses; /*created by constructor*/ publicvoid addNameAddress(INameAddress aNameAddress) { this.nameAddresses.addElement(aNameAddress); } publicvoid display() { Enumeration nameAddressList = this.nameAddresses.elements(); while (nameAddressList.hasMoreElements()) { StringnameAddress = (String) nameAddressList.nextElement(); /*display using nameAddress.getName()*/ /*display using nameAddress.getAddress()*/ } } }
c. By Analogy (i) • Strategy: factor-out by analogy. FlightDescription hasAvailableSeat reserveSeat cancelSeat IDateReserve FlightDescription available (date) reserve (date, reserver) IDateReserve cancel (date, reserver)
c. By Analogy (ii) DateReserveUI IDateReserve available (date) invokeAvailable n reserve (date, reserver) invokeReserve cancel (date, reserver) invokeCancel FlightDescription IDateReserve
c. By Analogy (iii) IDateReserve DailyWorkOrder available (date) reserve (date, reserver) n reserveResources cancel (date, reserver) Equipment Worker Workspace IDateReserve IDateReserve IDateReserve publicclass DailyWorkOrder { private Vector dateReservables; /*created by constructor*/ publicvoid addDateReserves(IDateReserve aDateReserve) { this.dateReservables.addElement(aDateReserve); } publicvoid 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 UML notation
d. Future Expansion (ii) IActivate Zone activate deactivate n IActivate Sensor IActivate
d. Future Expansion (iii) IActivate Zone activate deactivate n IActivate Sensor Motor RobotArm Switch IActivate IActivate IActivate IActivate publicclass Zone { private Vector activatibles; /*created by constructor*/ publicvoid addActivate(IActivate anActivate) { this.activatibles.addElement(anActivate); publicvoid 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.
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. • 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 BorrowingAccount Applicant Borrower 0-1 1 0-1 1 getBalance IAccount IAccount publicinterface IAccount { publicdouble 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 role-inspired 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 totalApprovedLimits totalAvailableLimits
f. Role to Interface to Proxies (ii) Borrower IBorrow totalApprovedLimits totalAvailableLimits IBorrow Borrower Party Applicant 0-1 1 n 1 IBorrow IBorrow IBorrow Person Organization IBorrow totalApprovedLimits totalAvailableLimits
f. Role to Interface to Proxies (iii) • publicinterface IBorrow { • publicdouble totalApprovedLimits(); • publicdouble totalAvailableLimits(); • } • public Borrower extends Object implements IBorrow { • publicdouble totalApprovedLimits() {/*real work*/} } • publicdouble totalAvailableLimits() {/*real work*/} • } • public Applicant extends Object implements IBorrow { • private Borrower borrower; /*add/remove with add/remove methods*/ • publicdouble totalApprovedLimits() { • returnthis.borrower. totalApprovedLimits (); /*delegate*/} • publicdouble totalAvailableLimits() { • returnthis.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 implementers.
g. Collections and Members to Interfaces (ii) ITotalApprovedLimit totalApprovedLimit ICompareAppliedVsApproved compareAppliedVsApproved Approval Application n 1 ITotalApprovedLImit ITotalApprovedLimit ICompareAppliedVsApproved 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.
h. Scenarios to Interfaces (ii) Name: Assess profit and risk (i). Applicant Application Borrower BorrowingAccount Constraints: assessProfit assessRisk assessProfit assessProfit assessRisk assessRisk assessRisk assessProfit assessProfit ( ; profit) assessProfit assessProfit ( ; profit) n n assessProfit assessProfit assessRisk assessRisk ( ; risk) n n assessRisk assessRisk ( ; risk) assessRisk assessRisk n n assessRisk assessRisk Name: Assess profit and risk (ii). Applicant IAssessProfitAndRisk Implementer Constraints: assessProfit assessProfit assessRisk assessRisk assessProfit assessProfit ( ; profit) assessProfit assessProfit ( ; profit) assessRisk assessRisk ( ; risk) assessRisk assessRisk ( ; risk)
h. Scenarios to Interfaces (iii) IAssessProfitAndRisk IAssessRisk Application assessProfit assessRisk IAssessRisk Applicant IAssessRisk n 1 IAssessProfitAndRisk Borrower 0-1 1 BorrowingAccount n 1 IAssessProfitAndRisk IAssessProfitAndRisk assessRisk
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.
i. Intra-Class Roles to Interfaces (ii) ITransferSource Account transferTo ITransferSource ITransferDestination ITransferDestination transferFrom Name: Transfer from one account to another. Account [from] Account [to] Constraints: transferFrom transferTo transferFrom transferFrom (amount, transferTo ; result) transferTo transferTo (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.
j. Plug-In Methods to Interfaces (ii) Term IValidateTerm 1 validateTerm validate Name: Validate a term. Term IValidateTerm Implementer Constraints: validate validateTerm validate validate ( ; result) validateTerm validateTerm ( ; result) • publicclass Term extends Object { • private IValidateTerm validater; • publicint validate() { • return validater.validateTerm(); } • publicinterface IValidateTerm { • publicint validateTerm(); }
3. Design with Threads • A thread is a stream of program execution.
Multiple Threads this.mainThread = new Thread (this); this.mainThread.start();
Threads and Synchronized Methods (a) Name: Reserve space. FlightDescription ScheduledFlight Constraints: reserve reserve reserve reserve (passenger, date; reservation) SYNC SYNC reserve reserve (passenger; reservation) ENDSYNC ENDSYNC reserve reserve SYNC SYNC reserve reserve (passenger; reservation) ENDSYNC ENDSYNC publicclass ScheduledFlight extends Object { publicsynchronized Reservation reserve(Passenger aPassenger, Date aDate) {/*code*/} }
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.
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 cross-messaging objects.
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