CSE2305 - Object-Oriented Software Engineering
Assessment

 

Assignment 3: solutions


This assignment was due Friday, 23 September 2005.
Total marks: 5.
Contribution to final mark: 5%.

Synopsis


Question 1

[50 marks]

Develop a design for Bernard's Book shop.

The first part of developing a design is to look for possible classifications from which classes and objects can be discovered. Here a combination of classical and conceptual classification can help (refer Lecture topic 15). Most of the items stocked by the store have a lot in common, so we can devise a base level abstraction that represents items that are sold by the store. We'll call this class PublishedItem. More specialised classes will inherit from this base class, such as books and serials.

Class diagram showing key classes of Item, Customer, DVDItem, VideoItem and GameItem

PublishedItem represents the base level abstraction. Notice how only the required methods are exposed in its public interface. We assume that classes like string, Percentage and Price are value objects (see Lecture Topic 17 for details). We can use the standard library string class. Percentage and Price could be typedef'd to floats as a first step.

The Print function of PublishedItem and its sub-classes is a good example of a polymorphic function – what it does depends on type. For example, if we are printing a Book object the code would be something like this:


ostream& Book::print(ostream& s) const
{
	s << getTypeName() << "(" << getBookTypeAsString() << ")" << ": " << title << endl;
	s << "Author: " << author << endl;
	s << "Published by: " << publisher << endl;
	s << "Place of publication: " << placeOfPublication << endl;
	s << "Retail Price: " << getRetailPrice() << ", " << getItemsAvailable() << " copies available." << endl;
	return s;
}

Serial and Newspaper would define a print function with different behaviour.

Looking more closley at the Price type, it may become important to make Price a class in its own right (to aid with expanded options such as rounding, change, pretty printing, currency conversion, etc.). Price has been expanded in the diagram below for this reason (it is still a value object however).

A number of classes so far need to define methods to read from a stream, write to a stream and print to a stream. The read/write methods are for saving and loading objects to a stream (a serialisation mechanim) – something that forms part of a persistence mechanism. The print method is used to print the object in a human readable format (which can be used when printing search matches for example). Because this appears in many classes it makes sense to factorise these methods into separate class and get all the necessary classes to inherit from this class. As we'll need to input object information from the user, we'll also define a get method, that reads object state in from the user (i.e. prompts to enter values like the Book's title). The base class, Streamable, contains only pure virtual functions (defining an interface but no implementation – that is left to subclasses). From Streamable's point of view all we know is that the object can be read, written and printed to a supplied stream. The getObject method is used to return an object read from a stream - this will be required to stream PublishedItem and its subclasses from files. Please see Stroustrup section 25.4.1 for more details.

Class diagram showing Streamable mixin class and subclasses

This type of class is known as a mixin (or property) class. It represents a "uses a" rather than an "is a" relationship, even though it uses inheritance (usually multiple inheritance).

The next stage is the database component of the system. We need a database of PublishedItems. We need to add and delete, search and sort PublishedItems. Here there are a number of possible solutions. We could use the List class from assignment 1 or the associative Array class from assignment 2, with the key type a string or UniqueID and the data a pointer to a PublishedItem. We can also use the idea of genericity and develop a generic "database" class. This will effectively be a wrapper class that can wrap around an STL container class.

Class diagram showing Database and RentalLibrary classes

The Bookshop class implements the functionality required by the assignment specification. If the requirements change, then this class would likely have to change as well. Notice how the BookshopManager class is not streamable – why?

A simpler (hence prefered) solution would be to use an STL list or vector class to replace the custom Database class show above. These container classes are not directly streamable, however it is a simple matter to iterate over the container and stream each object.

The diagram doesn't tell us much about how the system interacts with the user. A more complex solution decouples the functionality of the database from the way it is interacted with – this gives us much greater flexibility to change the interaction and functionality of the system independently. The method presented here is based on a couple of simple design patterns: the Command and Composite patterns.

Users of the system interact with menus. A menu has a title and the ability to add other Menu objects to it. The display method causes the menu to be displayed and then waits for the user to make a selection. The select method is called if the particular menu has been selected (clicked on in the case of a graphical user interface or it's particular number selected for a text-based menu). Notice two sub-classes of Menu: MenuItem, representing a single Menu item, and CompoundMenu representing a collection of Menus. When a MenuItem is asked to display it just prints its title, when a CompoundMenu is asked to display it prints all its Menu object's titles and waits for the user to make a selection. If the user's selection happens to be a CompoundMenu object then that object's display method is called and a new-sub menu is displayed. If the user has selected a MenuItem that menu item also stores a Command object, which is executed at this point, performing the required action.

Class diagram showing Menu and Command abstract classes and their associated sub-classes

The Command classes represent objects that perform commands. The only thing a Command object can do is execute(). What execute() actually does depends on the concrete class implementing the abstract Command class's interface. A new class is required for each command, but by breaking more complex commands into a set of simple steps we can combine them in a MacroCommand object. This gives us a lot of flexibility to change the functionality of our system, even at run time (for example based on licence type we can add extra features).

Not all the commands needed for the full system are show here (that's what the dotted line means).

The object diagram below shows a sample of the menu structure for the Bookshop system:

Object diagram showing key menu items

Notice how the menu structure and the actions they perform are kept separate – an instance of the Model/View/Controller pattern (except that view is implicit). If we wanted to get really fancy we could make our menus streamable, giving us the ability to configure the menu system from an external file at runtime – also offering the ability for the user to customise menu layouts and options (for example a novice user might not want so many options in the main menus or may be restricted in the types of database modification they can do).

Since the responsibility for running the system has been delegated to Command classes we need to create a BookshopSystem controller class. This class controls a BookshopManager object via a collection of menus and commands.

Class diagram showing RentalLibrary and Menu classes

You can download a set of Menu and Command classes suitable for use in the above design here.

 


This material is part of the CSE2305 - Object-Oriented Software Engineering course.
Copyright © Jon McCormack, 2004. All rights reserved.

Last Modified: October 1, 2005