OO Analysis Example: Online Bookstore

(see 1.4.2 of Object-Oriented Software Development Using Java)

An Informal Description

"The system is an online, web-based bookstore. The bookstore sells books, music CDs, and software. Typically, a customer first logs on to the system, entering a customer ID and password. The customer can then browse for titles or search by keyword. The customer puts some of the titles into a "shopping cart" which keeps track of the desired titles. When the customer is done shopping, he/she confirms the order, shipping address, and billing address. The bookstore system then issues a shipping order, bills the customer, and issues an electronic receipt. At the end of the transaction, the customer logs off."

A Scenario

A scenario is a very specific sequence of actions and responses that detail how the system will interact with a user. It is useful for pinpointing vague or missing requirements, and for verifying that the system does what's expected. A complete system would have several top-level scenarios, each showing a typical interaction with the system.

  1. User logs on.
  2. System displays welcome message and requests customer ID and password.
  3. User enters customer ID and password.
  4. System validates the ID and password.
  5. User searches for a title by browsing or keyword search
  6. System displays information about the title.
  7. User selects a title to buy
  8. System adds title to the customer's shopping cart
  9. [Repeat 5-8 until done]
  10. User is done with shopping
  11. System displays shopping cart, shipping address, and billing address
  12. User confirms order and payment method.
  13. System processes order, notifies warehouse for shipping, and issues an electronic receipt.
  14. User logs off.

In a real world system, the scenarios are the basis for the detailed requirements. You can walk clients through scenarios and see if the system behavior is what's desired. Typically scenarios change and expand over time.

System Diagram

This helps answer very high-level questions: What is part of the system, and what is not? What people interact with the system? What external systems will our system interact with?

(I drew the Warehouse System and Billing System with square heads to remind me that they're not actual people, they're just external systems.)

Candidate Classes

Look for important nouns: Objects, people, organizations, places, events, concepts. Give them memorable names. Reflect real world objects where that is useful, and create new ones as needed to help support the scenarios.

OnLineBookstore, Customer, Book, MusicCD, Software, ...

Class Attributes

Analyze the requirements to find the attributes of each class.

A Customer has a name, customerID, password, shippingAddress, and billingAddress.
An Address has a street, city, state, country, and postalCode.
etc.

Note that some attributes may be references to other objects.

Relationships

Analyze the requirements to find relationships between classes. Draw relationships as lines between class boxes. Annotate the relationships: How many OrderItems for each Order are possible? Use symbols to indicate the kind of relationship: Generic, aggregation (has-a or owns-a), inheritance (is-a). For generic relationships, use an arrowhead to indicate whether the relationship is navigable: Does the source object know about the destination object?

After identifying candidate classes, see if there are any commonalities between them that might be an IS-A relationship. It may make sense to factor the common parts into a superclass, and create an inheritance relationship:

Item is a useful abstraction, because most of the system doesn't care what precise kind of item we're talking about. Whether you should have actual subclasses for each specific kind of item is a design detail. If you want to let behavior vary between types of items, it's usually a good idea to use inheritance. In this example, the only behavior we look at is the display() method (below), but we would also want methods like match() for searching and perhaps preview() for giving users a taste for the item. Clearly preview() varies a lot between the different kinds of items.

Class Diagram

Draw the entire system on one page (hide details about classes if you run out of room, and display them on subsequent pages). Be sure to include all important relationships, especially where one class depends on another.

Note that the method names on the class diagram are very simple, usually just a verb. That's because the class itself usually supplies the noun: item.display(), customer.bill(). There's usually no reason to have methods like item.itemDisplay() or customer.customerBill(); they're redundant.

A special case arises when you want to extend a method of a superclass, as with Item.display(). For Book to override the superclass method, it _has_ to name it the same thing: Book.display(). That way, when clients call item.display(), they'll get the Book version if appropriate. (Inside Book.display(), it is usually advisable to start the method with a call to Item.display(); you can do this with the special syntax "super.display()". This executes the superclass version of the method, then returns to continue the subclass code.)

In thinking about how this system would work in the real world, there are two critical pieces not shown on the diagram: UI and data storage (database). This is actually deliberate. The classes shown here form the "business logic" of the application, which should remain the same no matter what kind of UI or data storage mechanism you choose. You don't have to show everything on one diagram.

Verify the design by going back and checking the system against the scenarios; are all the scenarios supported by the system? Is all data owned by some class? Is it clear which class will be responsible for which actions? Are resonsibilities distributed sensibly among different classes, so that each class is cohesive and there is no unnecessary coupling between classes?

Code

Now write the code corresponding to the class diagram. In reality, you will probably need to write a high-level diagram to get a general architecture, then write code and refactor individual pieces as needed. For example, you may decide later on that you really just need a String to represent an address, so you don't need a separate Address class.

Examples: Customer.java, ShoppingCart.java

Customer.java has some examples of methods that provide controlled access to the customer object's data. Often these are just get/set pairs, but not always. The accessor methods aren't shown on the class diagram (although they could be). Often it's easiest to let them be implied by the variables shown on the diagram, unless you need to be very precise.

I like to name member variables starting with a leading underscore: _item, _address. This can help distinguish member variables from local variables and method parameter names. There are other ways of doing this as well.

A one-to-one or many-to-one relationship is often most easily expressed with a simple object reference, for example _shippingAddress. A one-to-many relationship usually needs some kind of collection of references, for example _itemOrders. A many-to-many relationship is difficult and best avoided if possible.