18.4. Use Case Realizations for the NextGen Iteration
The following sections explore the choices and decisions made during the design of a use case realization with objects based on the GRASP patterns. I intentionally detail explanations, to show that there's no magic in OO designit's based on justifiable principles.
Initialization and the 'Start Up' Use Case
The Start Up use case realization is the design context in which to consider creating most of the 'root' or long-lived objects. See p. 345 for some of the design details.
Guideline When coding, program at least some Start Up initialization first. But during OO design modeling, consider the Start Up initialization design last , after you have discovered what really needs to be created and initialized. Then, design the initialization to support the needs of other use case realizations. |
How to Design makeNewSale?
The makeNewSale system operation occurs when a cashier initiates a request to start a new sale, after a customer has arrived with things to buy. The use case may have been sufficient to decide what was necessary, but for this case study we wrote contracts for all the system operations, to demonstrate the approach.
Operation: | makeNewSale() |
Cross References: | Use Cases: Process Sale |
Preconditions: | none |
Postconditions: |
|
Choosing the Controller Class
Our first design choice involves choosing the controller for the system operation message enterItem . By the Controller pattern, here are some choices:
Represents the overall "system," "root object," a specialized device, or a major subsystem. | Store a kind of root object because we think of most of the other domain objects as "within" the Store .Register a specialized device that the software runs on; also called a POSTerminal .POSSystem a name suggesting the overall system |
Represents a receiver or handler of all system events of a use case scenario. | ProcessSaleHandler constructed from the pattern <use-case-name> "Handler" or "Session"ProcessSaleSession |
Remember, this Register is a software object in the Design Model. It isn't a physical register. |
Figure 18.5. Applying the GRASP Controller pattern.
Creating a New Sale
We must create a software Sale object, and the GRASP Creator pattern suggests assigning the responsibility for creation to a class that aggregates, contains, or records the object to be created.Analyzing the Domain Model reveals that a Register may be thought of as recording a Sale ; indeed, the word "register" in business has for hundreds of years meant the thing that recorded (or registered) account transactions, such as sales.Thus, Register is a reasonable candidate for creating a Sale . Note how this supports a low representational gap (LRG). And by having the Register create the Sale , we can easily associate the Register with it over time so that during future operations within the session, the Register will have a reference to the current Sale instance.In addition to the above, when the Sale is created, it must create an empty collection (such as a Java List ) to record all the future SalesLineItem instances that will be added. This collection will be contained within and maintained by the Sale instance, which implies by Creator that the Sale is a good candidate for creating the collection.Therefore, the Register creates the Sale , and the Sale creates an empty collection, represented by a multiobject in the interaction diagram.Hence, the interaction diagram in Figure 18.6 illustrates the design.
Figure 18.6. Sale and the collection creation.
[View full size image]
Conclusion
The design was not difficult, but the point of its careful explanation in terms of Controller and Creator was to illustrate that the details of a design can be rationally and methodically decided and explained in terms of principles and patterns, such as GRASP.
How to Design enterItem?
The enterItem system operation occurs when a cashier enters the itemID and (optionally) the quantity of something to be purchased. Here is the complete contract:
Operation: | enterItem(itemID : ItemID, quantity : integer) |
Cross References: | Use Cases: Process Sale |
Preconditions: | There is an underway sale. |
Postconditions: |
|
Choosing the Controller Class
Our first choice involves handling the responsibility for the system operation message enterItem . Based on the Controller pattern, as for makeNewSale , we will continue to use Register as a controller.
Display Item Description and Price?
Because of a principle of Model-View Separation, it is not the responsibility of non-GUI objects (such as a Register or Sale ) to get involved in output tasks. Therefore, although the use case states that the description and price are displayed after this operation, we ignore the design at this time.All that is required with respect to responsibilities for the display of information is that the information is known, which it is in this case.
Creating a New SalesLineItem
The enterItem contract postconditions indicate the creation, initialization, and association of a SalesLineItem . Analysis of the Domain Model reveals that a Sale contains SalesLineItem objects. Taking inspiration from the domain, we determine that a software Sale may similarly contain software SalesLineItem . Hence, by Creator, a software Sale is an appropriate candidate to create a SalesLineItem .We can associate the Sale with the newly created SalesLineItem by storing the new instance in its collection of line items. The postconditions indicate that the new SalesLineItem needs a quantity when created; therefore, the Register must pass it along to the Sale , which must pass it along as a parameter in the create message. In Java, that would be implemented as a constructor call with a parameter.Therefore, by Creator, a makeLineItem message is sent to a Sale for it to create a SalesLineItem . The Sale creates a SalesLineItem , and then stores the new instance in its permanent collection.The parameters to the makeLineItem message include the quantity , so that the SalesLineItem can record it, and the ProductDescription that matches the itemID .
Finding a ProductDescription
The SalesLineItem needs to be associated with the ProductDescription that matches the incoming itemID . This implies that we must retrieve a Product-Description, based on an itemID match.Before considering how to achieve the lookup, we want to consider who should be responsible for it. Thus, a first step is:
Start assigning responsibilities by clearly stating the responsibility. |
Who should be responsible for knowing a ProductDescription , based on an itemID match?
This is neither a creation problem nor one of choosing a controller for a system event. Now we see our first application of Information Expert in the design.In many cases, the Expert pattern is the principal one to apply. Information Expert suggests that the object that has the information required to fulfill the responsibility should do it. Who knows about all the ProductDescription objects?Analyzing the Domain Model reveals that the ProductCatalog logically contains all the ProductDescriptions . Once again, taking inspiration from the domain, we design software classes with similar organization: a software ProductCatalog will contain software ProductDescriptions .With that decided, then by Information Expert ProductCatalog is a good candidate for this lookup responsibility since it knows all the ProductDescription objects.The lookup can be implemented, for example, with a method called getProductDescription (abbreviated as getProductDesc in some of the diagrams).[2]
[2] The name of access methods is idiomatic to each language. Java always uses the object.getFoo() form; C++ tends to use object.foo() ; and C# uses object.Foo, which hides (like Eiffel and Ada) whether access is by a method call or is direct access of a public attribute.
Visibility to a ProductCatalog
Who should send the getProductDescription message to the ProductCatalog to ask for a ProductDescription ?It is reasonable to assume that a long-life Register and a ProductCatalog instance were created during the initial Start Up use case and that the Register object is permanently connected to the ProductCatalog object. With that assumption (which we might record on a task list of things to ensure in the design when we get to designing the initialization), we know that the Register can send the getProductDescription message to the ProductCatalog. This implies another concept in object design: visibility. Visibility is the ability of one object to "see" or have a reference to another object.
For an object to send a message to another object, it must have visibility to it. |
The Final Design
Given the above discussion, the interaction diagram in Figure 18.7 and the DCD in Figure 18.8 (dynamic and static views) reflects the decisions regarding the assignment of responsibilities and how objects should interact. Mark the considerable reflection on the GRASP patterns, that brought us to this design; the design of object interactions and responsibility assignment requires some deliberation.
Figure 18.7. The
enterItem interaction diagram. Dynamic view.[View full size image]
Figure 18.8. Partial DCD related to the
enterItem design. Static view.[View full size image]
Retrieving ProductDescriptions from a Database
In the final version of the NextGen POS application, it is unlikely that all the ProductDescriptions will be in memory. They will most likely be stored in a relational database and retrieved on demand; some may be locally cached for performance or fault-tolerance reasons. However, in the interest of simplicity, we defer for now the issues surrounding retrieval from a database and assume that all the ProductDescriptions are in memory.Chapter 38 explores the topic of database access of persistent objects, which is a larger topic influenced by the choice of technologies, such as Java or .NET.
How to Design endSale?
The endSale system operation occurs when a cashier presses a button indicating the end of entering line items into a sale (another name could have been endItemEntry ). Here is the contract:
Operation: | endSale() |
Cross References: | Use Cases: Process Sale |
Preconditions: | There is an underway sale. |
Postconditions: | Sale.isComplete became true (attribute modification). |
Choosing the Controller Class
Our first choice involves handling the responsibility for the system operation message endSale . Based on the Controller GRASP pattern, as for enterItem , we will continue to use Register as a controller.
Setting the Sale.isComplete Attribute
The contract postconditions state:
- Sale.isComplete became true (attribute modification).
As always, Expert should be the first pattern considered unless the problem is a controller or creation problem (which it is not).Who should be responsible for setting the isComplete attribute of the Sale to true?By Expert, it should be the Sale itself, since it owns and maintains the isComplete attribute. Thus, the Register will send a becomeComplete message to the Sale to set it to true (see Figure 18.9).[3]
[3] That style is especially a Smalltalk idiom. Probably in Java, setComplete(true) .
Figure 18.9. Completion of item entry.
[View full size image]
Calculating the Sale Total
Consider this fragment of the Process Sale use case:
Main Success Scenario :
|
- State the responsibility:
- Who should be responsible for knowing the sale total?
- Summarize the information required:
- The sale total is the sum of the subtotals of all the sales line-items.
- sales line-item subtotal := line-item quantity * product description price
- List the information required to fulfill this responsibility and the classes that know this information.
Information Required for Sale Total | Information Expert |
---|---|
ProductDescription.price | ProductDescription |
SalesLineItem.quantity | SalesLineItem |
all the SalesLineItems in the current Sale | Sale |
- Who should be responsible for calculating the Sale total? By Expert, it should be the Sale itself, since it knows about all the SalesLineItem instances whose subtotals must be summed to calculate the sale total. Therefore, Sale will have the responsibility of knowing its total, implemented as a getTotal method.
- For a Sale to calculate its total, it needs the subtotal for each SalesLineItem . Who should be responsible for calculating the SalesLineItem subtotal? By Expert, it should be the SalesLineItem itself, since it knows the quantity and the ProductDescription it is associated with. Therefore, SalesLineItem will have the responsibility of knowing its subtotal, implemented as a getSubtotal method.
- For the SalesLineItem to calculate its subtotal, it needs the price of the ProductDescription . Who should be responsible for providing the ProductDescription price? By Expert, it should be the ProductDescription itself, since it encapsulates the price as an attribute. Therefore, ProductDescription will have the responsibility of knowing its price, implemented as a getPrice operation.
My goodness, that was detailed! Although the above analysis is trivial in this case and the degree of excruciating elaboration presented is uncalled for in actual design practice, the same reasoning strategy to find an Expert can and should be applied in more difficult situations. If you follow the above logic, you can see how to apply Expert to almost any problem. |
The Sale.getTotal Design
Given the above discussion, let us construct an interaction diagram that illustrates what happens when a Sale is sent a getTotal message. The first message in this diagram is getTotal , but observe that the getTotal message is not a system operation message (such as enterItem or makeNewSale ).This leads to the following observation:
Not all interaction diagrams start with a system operation message; they can start with any message for which the designer wishes to show interactions. |
Figure 18.10. Sale.getTotal interaction diagram.
[View full size image]
Figure 18.12. Showing a method in a note symbol.
[View full size image]
Figure 18.11. Showing a method in a note symbol.
[View full size image]
How to Design makePayment?
The makePayment system operation occurs when a cashier enters the amount of cash tendered for payment. Here is the complete contract:
Operation: | makePayment( amount: Money ) |
Cross References: | Use Cases: Process Sale |
Preconditions: | There is an underway sale. |
Postconditions: |
|
Creating the Payment
One of the contract postconditions states:
- A Payment instance p was created (instance creation).
This is a creation responsibility, so we consider the Creator GRASP pattern.Who records, aggregates, most closely uses, or contains a Payment ? There is some appeal in stating that a Register logically records a Payment because in the real domain a "register" records account information; this motivates Register 's candidacy by the goal of reducing the representational gap in the software design. Additionally, we can reasonably expect that Sale software will closely use a Payment ; thus, it, too, may be a candidate.Another way to find a creator is to use the Expert pattern in terms of who the Information Expert is with respect to initializing datathe amount tendered in this case. The Register is the controller that receives the system operation makePayment message, so it will initially have the amount tendered. Consequently the Register is again a candidate.In summary, there are two candidates:
- Register
- Sale
Now, this leads to a key design idea:
Guideline When there are alternative design choices , take a closer look at the cohesion and coupling implications of the alternatives, and possibly at the future evolution pressures on the alternatives. Choose an alternative with good cohesion, coupling, and stability in the presence of likely future changes. |
Figure 18.13. Register.makePayment interaction diagram.
[View full size image]
Logging a Sale
Once complete, the requirements state that the sale should be placed in an historical log. As always, Information Expert should be an early pattern considered unless the problem is a controller or creation problem (which it is not), and the responsibility should be stated:
Who is responsible for knowing all the logged sales and doing the logging?
By the goal of low representational gap in the software design (in relation to our concepts of the domain), we can reasonably expect a Store to know all the logged sales since they are strongly related to its finances. Other alternatives include classic accounting concepts, such as a SalesLedger . Using a SalesLedger object makes sense as the design grows and the Store becomes incohesive (see Figure 18.14).
Figure 18.14. Who should be responsible for knowing the completed sales?
[View full size image]
Figure 18.15. Logging a completed sale.
[View full size image]
Calculating the Balance
The Process Sale use case implies that the balance due from a payment be printed on a receipt and displayed somehow.Because of the Model-View Separation principle, we should not concern ourselves with how the balance will be displayed or printed, but we must ensure that it is known. Note that no class currently knows the balance, so we need to create a design of object interactions that satisfies this requirement.As always, Information Expert should be considered unless the problem is a controller or creation problem (which it is not), and the responsibility should be stated:
Who is responsible for knowing the balance?
To calculate the balance, we need the sale total and payment cash tendered. Therefore, Sale and Payment are partial Experts on solving this problem.If the Payment is primarily responsible for knowing the balance, it needs visibility to the Sale , to ask the Sale for its total. Since it does not currently know about the Sale , this approach would increase the overall coupling in the designit would not support the Low Coupling pattern.In contrast, if the Sale is primarily responsible for knowing the balance, it needs visibility to the Payment , to ask it for its cash tendered. Since the Sale already has visibility to the Payment as its creatorthis approach does not increase the overall coupling and is therefore a preferable design.Consequently, the interaction diagram in Figure 18.16 provides a solution for knowing the balance.
Figure 18.16. Sale.getBalance interaction diagram.
[View full size image]
The Final NextGen DCD for Iteration-1
In accordance with the design decisions in this chapter, Figure 18.17 illustrates a static-view DCD of the emerging design for the domain layer , reflecting the use case realizations for the chosen scenarios of Process Sale in iteration-1.
Figure 18.17. A more complete DCD reflecting most design decisions.
[View full size image]
How to Connect the UI Layer to the Domain Layer?
Common designs by which objects in the UI layer obtain visibility to objects in the domain layer include the following:
- An initializer object (for example, a Factory object) called from the application starting method (e.g., the Java main method) creates both a UI and a domain object and passes the domain object to the UI.
- A UI object retrieves the domain object from a well-known source, such as a factory object that is responsible for creating domain objects.
Once the UI object has a connection to the Register instance (the facade controller in this design), it can forward system event messages, such as the enterItem and endSale message, to it (see Figure 18.18).
Figure 18.18. Connecting the UI and domain layers.
[View full size image]
- Add a getTotal method to the Register . The UI sends the getTotal message to the Register , which delegates to the Sale . This has the possible advantage of maintaining lower coupling from the UI to the domain layerthe UI only knows of the Register object. But it starts to expand the interface of the Register object, making it less cohesive.
- A UI asks for a reference to the current Sale object, and then when it requires the total (or any other information related to the sale), it directly sends messages to the Sale . This design increases the coupling from the UI to the domain layer. However, as we explored in the Low Coupling GRASP pattern discussion, higher coupling in and of itself is not a problem; rather, coupling to unstable things is a real problem. Assume we decide the Sale is a stable object that will be an integral part of the designwhich is reasonable. Then, coupling to the Sale is not a major problem.
As illustrated in Figure 18.19, this design follows the second approach.
Figure 18.19. Connecting the UI and domain layers.
[View full size image]
Initialization and the 'Start Up' Use Case
When to Create the Initialization Design?
Most, if not all, systems have either an implicit or explicit Start Up use case and some initial system operation related to the starting up of the application. Although abstractly, a startUp system operation is the earliest one to execute, delay the development of an interaction diagram for it until after all other system operations have been considered. This practice ensures that information has been discovered concerning the initialization activities required to support the later system operation interaction diagrams.
Guideline Do the initialization design last. |
How do Applications Start Up?
The startUp or initialize system operation of a Start Up use case abstractly represents the initialization phase of execution when an application is launched. To understand how to design an interaction diagram for this operation, you must first understand the contexts in which initialization can occur. How an application starts and initializes depends on the programming language and operating system.In all cases, a common design idiom is to create an initial domain object or a set of peer initial domain objects that are the first software "domain" objects created. This creation may happen explicitly in the starting main method or in a Factory object called from the main method.Often, the initial domain object (assuming the singular case), once created, is responsible for the creation of its direct child domain objects. For example, a Store chosen as the initial domain object may be responsible for the creation of a Register object.In a Java application, for example, the main method may create the initial domain object or delegate the work to a Factory object that creates it.
public class Main
{
public static void main( String[] args )
{
// Store is the initial domain object.
// The Store creates some other domain objects.
Store store = new Store();
Register register = store.getRegister();
ProcessSaleJFrame frame = new ProcessSaleJFrame( register );
...
}
}
Choosing the Initial Domain Object
What should the class of the initial domain object be?
Guideline Choose as an initial domain object a class at or near the root of the containment or aggregation hierarchy of domain objects. This may be a facade controller, such as Register, or some other object considered to contain all or most other objects, such as a Store. |
Store.create Design
The tasks of creation and initialization derive from the needs of the prior design work, such as the design for handling enterItem and so on. By reflecting on the prior interaction designs, we identify the following initialization work:
- Create a Store, Register, ProductCatalog, and ProductDescription s.
- Associate the ProductCatalog with ProductDescriptions.
- Associate Store with ProductCatalog .
- Associate Store with Register .
- Associate Register with ProductCatalog .
Figure 18.20 shows the design. We chose the Store to create the ProductCatalog and Register by the Creator pattern. Likewise, we chose ProductCatalog to create the ProductDescription s. Recall that this approach to creating the specifications is temporary. In the final design, we will materialize them from a database, as needed.
Figure 18.20. Creation of the initial domain object and subsequent objects.
[View full size image]
Observe that the creation of all the ProductDescription instances and their addition to a container happens in a repeating section, indicated by the * following the sequence numbers.An interesting deviation between modeling the real-world domain and the design is illustrated in the fact that the software Store object only creates one Register object. A real store may house many real registers or POS terminals. However, we are considering a software design, not real life. In our current requirements, our software Store only needs to create a single instance of a software Register .
Multiplicity between classes of objects in the Domain Model and Design Model may not be the same. |