S19_Chapter_18

advertisement
Object-Oriented Analysis and Design
CHAPTER 18: GRASP PAT TERNS - EXAMPLES
1
What will we learn?
GRASP – Object Design Examples
Applying GRASP to our case studies
2
Overview
We will practice assigning responsibilities, using the GRASP principles we have
learned
Before we apply the GRASP principles to Domain Objects, we first need to look
at how to design the Domain Objects for an entire use case scenario
Use Case Realization: How a use case is realized in the Design Model in terms of collaborating
objects
This connects the requirements (expressed in a use case) and the object design
that satisfies the requirements
3
Getting Started …
The use case suggests system operations that are shown in the SSDs
The system operations become the starting messages entering the
Controllers for the domain layer interaction diagrams
Domain layer interaction diagrams illustrate how objects interact to
fulfill the required tasks and meet the responsibility defined by the
system operation – this is the use case realization
4
NextGen POS – Getting Started
Recall that for the basic Process Sale use case, the SSD gave us the
following system operations:
makeNewSale
enterSale
endSale
makePayment
These will be the starting messages into the Domain Layer controller object
We can also consider the Operation Contracts we developed to further help
explain how the system changes with these operations
We will assign the responsibility of handling these system operations to various
objects in the Domain Layer – this is RDD
5
Initialization?
Shouldn’t we first design a “Start up” use case to initialize the
objects, i.e. “turn the system on”?
Not a bad idea, and when coding this is often the first thing done
But in design, we usually do this last, once we know all the objects
that will make up the design
So we will first explore the Process Sale use case, and leave the
details of a Start Up use case until later
6
Process Sale: makeNewSale
We start with the makeNewSale system operation – this occurs when the
Cashier initiates a request to begin a new sale
Recall we had an operations contract for this operation, which had the
following post conditions:
- A Sale instance s was created
- s was associated with the Register
- Attributes of s were initialized
We need to define a Domain Layer object to receive the makeNewSale message
that occurs when this system operation is started
7
Process Sale: makeNewSale
We could look to the Domain Model, and consider Store or Register. We
could also create a special handler class to deal with this use case
Since this use case has few system operations, defining a new handler
class is probably not needed – we can use an existing Domain Object
without overloading it (losing cohesion)
Defining a Register software object makes sense, since it can handle the
few system operations, and is related to the Domain Model object that
hosts the software
So we have decided that there will be a Register object, and this object
will handle the responsibility of processing the makeNewSale system
operation - we have made our first design decision!
8
Process Sale: makeNewSale
Note the operation contract mentions that a new Sale object is created;
applying the GRASP Creator principle, we look to find an object that can
contain, aggregate, or record the Sale
Again, looking in the Domain Model, it appears the Register object meets
the need (a register literally means to “register” sales)
Also recall that the Sale object was a composite of SalesLineItem objects
in the Domain Model
At this point, we can decide to initialize an (empty) collection of
SalesLineItem objects, to be filled in later as the sale is processed
Again, the Creator principle implies that the Sale object is the likely
creator for this list …
9
by Creator
and
Controller
Register creates a
Sale by Creator
:Register
makeNewSale
create
by Creator, Sale
creates an empty
collection (such as a
List) which will
eventually hold
SalesLineItem
instances
:Sale
create
lineItems :
List<SalesLineItem>
this execution specification is implied to be
within the constructor of the Sale instance
10
Process Sale: enterItem
This system operation occurs when the Cashier enters an itemID and possibly a
quantity for an item in the sale.
The postconditions in the operations contract were:
-
A SalesLineItem instance sli was created
sli was associated with the current Sale
sli.quantity became quantity
sli was associated with a ProductDescription, based on itemID match
We will now walk through the design decisions required to handle this system
operation
11
Process Sale: enterItem
Controller: Based upon what we decided earlier, the Register will handle
this incoming message
Note the original use case mentioned that the description and price for
the item are displayed
By Model-View separation, we generally do not allow Domain Objects to be involved
in output tasks, so we will not worry about this requirement right now
Operations Contract states that a new SalesLineItem is created
Based upon the Domain Model, it appears that the Sale object would be
the likely candidate to create the new SalesLineItem
Postcondition implies that SalesLineItem needs a quantity when created, and this is
derived from the original system operation (input by Cashier)
Register must pass this parameter to Sale, which must pass it in the creation message
to SalesLineItem
12
Process Sale: enterItem
Note that we also need to associate a ProductDescription with this item, based upon
the itemID entered by the Cashier and included in the system operation handled by the
Register
Who should do the lookup – who gets this responsibility? I.e., who should have the responsibility
for knowing a ProductDescription based on an itemID match?
This is an application of the Expert GRASP principle
Question: Who knows the most about the ProductDescription objects? Looking at the Domain
Model, we see it is the ProductCatalog
Next design decision: We should have an object called ProductCatalog that will contain
the objects ProductionDescription.
Then, ProductCatalog is a good candidate to take responsibility for looking up the
ProductDescription based on the itemID.
Let’s call the method generated to meet this responsibility getProductDescription
13
Process Sale: enterItem
Who sends the getProductDescription message to the ProductCatalog?
Recall that in the Domain Model, Product Catalog was not really associated with any other
object
Could use Register or Sale?
Note that Sale is temporary, gets created each time a new sale is started, so it would need to
be associated with the ProductCatalog each time it is created
Register is a better choice – can be associated with ProductCatalog upon instantiated; both
objects probably instantiated once upon start up
Note that for an object to send a message to another object, it must have
“visibility” to it
Final design for this system operation is given on the next slides
14
by Creator
by Controller
enterItem(id, qty)
2: makeLineItem(desc, qty)
:Register
:Sale
1: desc = getProductDesc(id)
2.1: create(desc, qty)
:Product
Catalog
by Expert
sl: SalesLineItem
1.1: desc = get(id)
2.2: add(sl)
: Map<ProductDescription>
lineItems :
List<SalesLineItem>
add the newly created
SalesLineItem instance to the List
15
ProductDescription
ProductCatalog
1
catalog
...
descriptions
description : Text
{Map}
price : Money
1..* itemID: ItemID
getProductDesc(...)
...
description 1
Sale
Register
...
enterItem(...)
...
isComplete : Boolean
time : DateTime
currentSale
1
makeLineItem(...)
...
lineItems
{ordered}
1..*
SalesLineItem
quantity : Integer
...
16
Process Sale: endSale
The endSale system operation occurs when the Cashier presses a button
indicating the end of entering line items into a sale. Who is responsible for
handling this operation?
The postcondition in the Operation Contract states: Sale.isComplete became true
In other words, this system operation modified a attribute
Again, based upon out earlier design decision, the Register is the controller that
will initially receive the system operation message endSale
Who sets the attribute?
Using the Expert principle, the Sale should do this
Have the Register send a becomeComplete message to the Sale (same as a “set” method for
the attribute
17
endSale(
by Controller
:Register
1: becomeComplete
s :Sale
by Expert
18
Process Sale: Calculate Sale Total
The use case for Process Sale has these steps in the Main Success Scenario:
3. Customer arrives
4. Cashier tells System to create new sale
5. Cashier enters item identifier
6. System records sale line item …
Steps 5-6 repeat until no items left
7. System presents total with taxes calculated
Question – how does the system calculate the total? Who is responsible for
this?
Note that this is a behavior captured in a use case, not a system operation from an SSD
19
Process Sale: Calculate Sale Total
For this responsibility, we can go right to the Expert principle, since there is no
initial system operation to handle for the controller
The Sale object is the obvious expert object to handle this responsibility
The information required to compute the total is the sum of the subtotals for each line item
The SalesLineItem knows the subtotal of the line item, and the Sale contains the
SalesLineItems
Each subtotal is the line item quantity times the product price (part of product description)
The SalesLineItem knows the quantity of the line item, and the ProductDescription knows the
price of each item
Each SalesLineItem is the expert that can compute the subtotal, if it is associated with a
ProductDescription to get the price
Each ProductDescription is the expert best suited to provide the price for each product
20
by Expert
by Expert
tot = getTotal
:Sale
1 * [i = 1..n]: st = getSubtotal
UML: note the selector
notation to select elements
from the lineItems collection
lineItems[ i ]:
SalesLineItem
1.1: pr = getPrice
:ProductDescription
21
Process Sale: Calculate Sale Total
You can follow this communication diagram and see how the system would
handle the responsibility of calculating the sale total.
Note the low coupling!
Note that this was not triggered by a known system operation
The getTotal message will probably be sent from some object in the UI, probably after the
Cashier hits the button that indicates the end of the sale
It is also possible to include some text in the diagram to indicate how the
getTotal method will do the calculation
Note that we have left off the taxes for now
22
«method»
public void getTotal()
{
int tot = 0;
for each SalesLineItem, sli
tot = tot + sli.getSubtotal();
return tot
}
tot = getTotal
:Sale
1 *[ i = 1..n]: st = getSubtotal
lineItems[ i ] :
SalesLineItem
1.1: pr = getPrice
:ProductDescription
23
Process Sale: makePayment
The makePayment system operation occurs when the Cashier enters the
amount of cash given for the Sale (recall we are only looking at the first
iteration here)
Here are the postconditions from the Operations Contract:
-
A Payment instance p was created
p.amountTendered became amount
p was associated with the current Sale
The current Sale was associated with the Store (logging)
Question: Who is responsible for handling this system operation?
24
Process Sale: makePayment
Again, the Register will handle the initial makePayment message
Who creates the Payment instance p?
Best candidates are Register (in the Domain Model, the actual register would record the
payment information) and Sale (has the Total Amount)
Note Register will have the amountTendered parameter it received in the system operation
Guideline: When the Expert principle does not give a clear winner, consider
coupling and cohesion
From this perspective, we are probably better off letting the Sale object create the Payment
instance – makes Register simpler, reduces coupling
Register can pass the amountTendered (called cashTendered in the next slide) to the Sale
25
by Controller
makePayment(cashTendered)
by Creator and Low Coupling
:Register
1: makePayment(cashTendered)
:Sale
1.1: create(cashTendered)
:Payment
26
Process Sale: Logging the Sale
The last postcondition notes that the Sale is logged with the Store
This is not a creation or attribute modification – it is an association that is
formed
Note we can associate the Sale with a Store object, or we can use a SalesLedger
object (not defined in the original Domain Model, but probably a good
enhancement)
Either object will contain a list of completed sales, and the object will add to
that list
27
Sale
Sale
...
...
...
...
*
Logs-completed
*
Logs-completed
1
1
Store
SalesLedger
...
...
addSale(s : Sale)
...
addSale(s : Sale)
...
Store is responsible for
knowing and adding
completed Sales.
Acceptable in early
development cycles if the
Store has few
responsibilities.
SalesLedger is responsible
for knowing and adding
completed Sales.
Suitable when the design
grows and the Store
becomes uncohesive.
28
note that the Sale instance is named
's' so that it can be referenced as a
parameter in messages 2 and 2.1
makePayment(cashTendered)
:Register
2: addSale(s)
1: makePayment(cashTendered)
s :Sale
1.1: create(cashTendered)
by Expert
:Payment
:Store
2.1: add(s)
completedSales:
List<Sale>
29
Process Sale: Calculating the Balance
Finally, we need to decide who handles the responsibility of calculating the
balance
This is the difference between the total for the sale and the amount tendered
(cash given by the customer)
Apply the Expert principle: Who has the best knowledge to handle this?
Sale and Payment are the most likely candidates – each has one piece of the information
needed
What is the visibility of these two objects? Sale created Payment, so it knows
about Payment; Payment does not know about Sale
Best option: Let Sale do the calculation
30
{ bal = pmt.amount - s.total }
bal = getBalance
s :Sale
1: amt = getAmount
pmt: Payment
2: t = getTotal
31
Store
address : Address
name : Text
catalog
1
ProductDescription
ProductCatalog
addCompleteSale(...)
1
catalog
...
descriptions
description : Text
{Map}
price : Money
1..* itemID: ItemID
getProductDesc(...)
...
description
register
1
Sale
Register
...
endSale()
enterItem(...)
makeNewSale()
makePayment(...)
1
isComplete : Boolean
time : DateTime
currentSale
1
lineItems
{ordered}
becomeComplete()
makeLineItem(...)
makePayment(...)
getTotal()
completedSales
{ordered}
1..*
SalesLineItem
quantity : Integer
getSubtotal()
*
Payment
payment
1
amount : Money
...
32
Process Sale: Connecting UI and Domain Layer
The objects in the UI need to obtain visibility of the objects in the Domain Layer
that they interact with
Java main() that creates the objects in the UI and Domain layers and passes the domain
objects to the UI objects
The UI objects can obtain the Domain objects from a known source in the Domain layer that
is responsible for creating the Domain objects
Once the UI objects have been connected to the Domain Layer objects, they
can forward system operations to be handled
To get the running total for the Sale to display in the UI object, we could …
Add a getTotal method to Register, and have the Register get this from Sale (lowers cohesion)
Have the UI object access the getTotal method of the Sale directly (increased coupling)
33
presses button
Cashier
actionPerformed( actionEvent )
UI
Layer
:ProcessSale
JFrame
1: enterItem(id, qty)
Domain
Layer
system event
:Register
34
presses button
Cashier
actionPerformed( actionEvent )
UI
Layer
:ProcessSale
JFrame
3: t = getTotal
1: enterItem(id, qty)
2 [no sale] : s = getSale : Sale
Domain
Layer
:Register
s : Sale
35
Initialization
How do we start up the system?
Somewhat dependent on language and operating system …
There is usually an initial domain object (or set of objects) that are the first
ones created
Note that some objects will create others (Store creates Register in the above
model), so these new objects can then be passed to other objects as they are
created
Guideline: Choose an initial domain object at or near the root of the
containment or aggregation hierarchy of domain objects.
Look for an object that can be considered to contain all or most of the other objects
36
Initialization
Reviewing the DCD, we can conclude the following sequence of steps (these
would be executed as part of main(), for example, in this order):
Create a Store, Register, ProductCatalog, and ProductDescriptions
Associate the ProductCatalog with ProductDescriptions
Associate Store with ProductCatalog
Associate Store with Register
Associate Register with ProductCatalog
The communication diagram (which would essentially drive the model for the
main() function) is shown on the next slide
37
pass a reference to the ProductCatalog to the
Register, so that it has permanent visibility to it
create
2: create(pc)
:Store
by Creator
:Register
create an empty
collection object
1: create
1.1: create
pc:
ProductCatalog
1.2.2*: put(id, pd)
descriptions:
Map<ProductDescription>
1.2.1*: create(id, price, description)
1.2: loadProdSpecs()
the * in sequence number indicates the
message occurs in a repeating section
pd:
ProductDescription
38
Takeaways from Chapter 18
Understand the Design Model the POS case study
Understand how the GRASP principles were used to create this design
39
Next …
Gang of Four Patterns – Chapter 26
40
Download