UCL Logo

Snack Bar Example

These notes outline how you might go about writing a snack bar program based on this initial informal specification:

"The owner of a snack bar wants a system that will allow an order menu to be created and managed, allowing operations such as adding and deleting items, and adjusting prices. The system should also allow the owner to enter a customer's order and get a display of the bill, with the total price. Obviously the system will be interactive. It should be possible to store menus and billing information in files."

The notes below show the use of basic object-oriented development techniques, starting with a collection simple stories derived from the initial specification. Keep in mind that we are addressing design in the small scale for a simple (artificial) example. Also remember that these notes are only notes and are not meant to be a polished, complete (or even coherent!) description of the development process. You must sort out the ideas, concepts, skills and activities for yourself, make sure you understand them and make sure you can apply them in practice.

Step 1

Stories: The stories outline what the program is expected to do, so that we have a clear idea of what to implement and don't get side-tracked by features that turn out not to be needed. There are enough stories to make the program interesting but it does not pretend to be a complete menu management system.

Ideally, of course, the stories should be provided by the client that the program is being written for. We don't have one so will have to make our own assumptions about what a hypothetical client might want.

Here is the initial set of stories:

1. A menu can be displayed.

2. A menu consists of a list of menu items. Each menu item has a name and a price. Only one menu is needed per day.

3. A menu may be changed by adding or removing menu items.

5. The price of a menu item can be changed change.

6. A menu can be reused on different days.

7. A customer order consists of a sequence of menu items and a total price.

8. A record of each order must be kept.

The stories should be carefully reviewed to make sure they are coherent, consistent and complete. Any stories found to be doubtful or not needed should be removed. Note that the stories are strictly about what is required and not about how the program might be implemented.

We deliberately want to keep the program simple and straightforward. For example, although several stories mentions days, we will decide not make use of time or dates. Similarly, though we will keep a record of orders, we won't attempt to keep track of the total value of all orders, or things such as the numbers of each menu item ordered (it is important to avoid inventing lots of details and complications).

Given this we can simplify two of the stories:

2. A menu consists of a list of menu items. Each menu item has a name and a price. Only one menu is needed at any one time.

6. A menu can be reused.

Step 2

Identify classes and objects and propose a design for the program. 

Do some brainstorming. The Class-Responsibility-Collaboration (CRC) method (role playing) is ideal for working out what classes and objects are needed, as well as for creating use cases. The CRC approach is explained in more detail in appendix B of the course text book (Developing Java Software).

First, review the stories, and any other information to hand, to identify potential classes. Looking for nouns may help but we need to be selective.

Potential classes:

Menu
MenuItem (attributes name and price)
Order
Customer
OrderRecord

Looking at these, Customer can be eliminated as there is no need to represent customers; only orders are needed. Also, the role of OrderRecord is not clear, possibly as a poor choice of name has been made. An Order object would record an order, so OrderRecord is not needed for that purpose. We do, however, need to record the collection of orders. Let's try OrderList as a class instead.

We can now start the flesh-out the descriptions for the selected classes and do some role playing to start identifying the responsibilities and collaborations for each class. This is done by picking out or inferring each task the program is required to do. 

1. Looking at the first story we can directly identify a task: A menu can be displayed.

The Menu class will manage the menu so it must have the responsibility to provide access to the menu items but should it have the responsibility for displaying (i.e., writing to the screen) a menu? Design experience suggests the answer is no, as we don't want to put code into the Menu class that dictates how a menu must be displayed; display is the responsibility of some other class or classes. This allows the program to display the menu in (potentially) different ways but without having to keep editing or adding methods to the Menu class.

The Menu class responsibility will be to 'return a collection of MenuItems'. It will collaborate with class MenuItem to manage the collection of items and has a menuitems attribute. This information can be added to the Menu class card.

We still haven't got a menu displayed. Who invokes the 'return collection of MenuItems' responsibility? Who deals with the user input requesting the menu to be displayed? None of our current classes can do this, so we need to add a new class to deal with getting user input and displaying a menu. We decide to call this class SnackBar and give it a 'display menu responsibility' that is invoked by user input. Our class list is now:

Menu
MenuItem (attributes name and price)
Order
OrderList
SnackBar (attribute menu)

We missed class SnackBar in our initial brainstorming but the need for it appeared as we started role playing a task. This is entirely normal as working through the potential tasks is all about discovery.

2. Task: Add a menu item

The SnackBar class has the responsibility to input a new item and add it to the menu. SnackBar can do this by arranging to input the name and price of the item and using the Menu responsibility 'add new item to menu'. This responsibility has to be added to the Menu class, which now becomes the focus of attention. The Menu creates a new MenuItem and adds it to the menu list.

The MenuItem class now needs a constructor responsibility that needs an item name and price. The name and price attributes have already been identified from the stories. However, what should we use for the price? Pounds and pence, just pence or something else? There are advantages in storing prices in pence, as floating point values are unreliable when dealing with money.

3. Task: Remove a menu item

The SnackBar class has the responsibility to remove an item. How is the item to be removed identified? Typing in the name of the item is awkward (especially if it is consists of several words), so we make the decision to number each item. When a menu is displayed the item numbers are included. We make a note that task 1 (display a menu) should do this when implemented, as we can use that task to display the menu to allow the user to see the item numbers when removing an item.

SnackBar then inputs the item number and uses the Menu responsibility 'remove item from menu'. This responsibility, has to be added to the Menu class, which now becomes the focus of attention. The Menu removes the corresponding MenuItem from the menu list.

4. Task: Change price of an item

The SnackBar class has the responsibility to change the price of an item. The item number is input to identify which item to change, followed by the new price. SnackBar then uses the Menu responsibility 'change price of item', which is added to the Menu class.

5. Task: Save a menu to a file

We infer this task from the need to reuse a menu, realising that the menu has to be saved to a file in order to be reused. The SnackBar class provides the responsibility to save the menu. A filename is input and the Menu 'save menu' responsibility is used (and added to the Menu class).

6. Task Load a menu from a file

Much the same as task 5 except we are dealing with loading rather than saving. Add the responsibilities to the classes.

7. Task: Enter an order

What is an order? It looks like a collection of menu items and a total price.

How is an order entered? We can repeatedly display the menu and ask for the user to type in an item number (again we need the menu items to be numbered). We stop doing this when the user enters a stop code. Given that, we can give the SnackBar a responsibility to do the input and create an order. The order is then stored in the order list.

The SnackBar collaborates with an Order by using its 'create' responsibility (i.e., create a new Order object) and then repeatedly using its 'add item responsibility'. Each item is a MenuItem. Then the SnackBar uses the OrderList responsibility 'add order'.

8. Task: Display an order

Story 8 says that a record of each order must be kept. So far we have provided OrderList to do this, so orders can be kept as long as the program is running. Should it be possible to display an order? What about saving orders to file? To answer these questions we really need to go back and re-visit the story. The client (the person who wants the program written) must be asked for more information to resolve this correctly. For the moment we decide that we need some mechanism to look at a stored order (if only to check it is correct), so will provide a simple display task.

Again, the SnackBar gains a responsibility: 'display an order' and collaborates with OrderList, Order and MenuItem to display an order.

This process continues until all the tasks from the stories have been addressed. The end result is a basic specification for each class. Notice that throughout this we haven't been too concerned with the distinction between classes and objects. This keeps things simple but the distinction should not be forgotten: classes define instance variables (attributes) but it is the instance objects of the classes that get created and actually perform the tasks. There are doubtless still lots of details to resolve and problems to find. These will be systematically addressed during prototyping of the implementation.

Initial Class Diagram

From the class descriptions an initial UML style class diagram can be created and class relationships inspected.

This is useful for review and to see an overall picture of the program structure. Remember that objects of these classes get created and call each others methods in order to perform the tasks. You might want to sketch out some method call sequences to see how they work and that they are possible.

Step 3:

Write the code, following the design information discovered so far, and using a prototyping strategy. Ideally do this by first writing test code, so you have a clear idea what your code should do, and then the code to be tested.

Writing the code will require translating the specifications and the task information into working Java classes. This should be reasonably straightforward but, of course, all the detail needed by the code will have to be filled in. Along the way many implementation decisions will need to be made. As always keep things simple.

Step 4:

Review everything.

  • Does the choice of classes still seem reasonable?
  • Is each class properly designed and cohesive (focussed on representing one concept or data item)?
  • Is the code properly written and presented?
  • Have sensible class, method and variable names been used?
  • Has good use of object-oriented design been made?
  • Is the program simple enough but no simpler?
  • Does the program do what the customer needs (does it add value to their business)?
  • Have I written an object-oriented program or a procedural program that's been forced into some classes?

If problems are found fix them.

Take pride in your work. What have you learnt? What could you do better next time? Remember you are learning to be a professional; sloppy work should simply not be acceptable to you or anybody else. If you can't be bothered to take a serious and professional approach, leave now...

 

The "Process"

This description of the development process has been written down as a sequence of 4 steps but that does not imply a strictly linear ordering. An initial pass at steps 1 and 2 must be done first, otherwise we simply won't know what the program should do or what the classes, their responsibilities and their key attributes are. After that everything follows an iterative prototyping cycle, with the different steps revisited as required and as more is discovered about the program. The end result should be a clean, simple, straightforward, working and thoroughly tested program. If at any stage there is any doubt about what the program or a task should be about, always re-visit the stories immediately. The most common and costly mistake made during program development is that of producing a program that doesn't do what the client actually needs.

At the end of all this, we hopefully have a working program. More importantly, we should have discovered a good design for the program, and identified and resolved many of the design details. If this was a real commercial development, we might now fully document the design and then start implementing the robust commercial version of the program, replacing the prototype code.

 

Last updated: September 2, 2006

Computer Science Department - University College London - Gower Street - London - WC1E 6BT - Telephone: +44 (0)20 7679 7214 - Copyright 1999-2006 UCL


 Search by Google