The code review for the Hotel application illustrated a number of useful points, such as the use of the canonical class form and the liberal use of const. But, these are all implementation level details.
Well defined objectives are the key to good design. From the article we don't know what all of the objectives are, but that doesn't stop us from making some intelligent guesses! At a high level we want to achieve a number of good 'ilities' such as maintainability and extensibility as part of the objectives. However, we want to achieve a balance between these and delivering a timely and effective solution.
Achieving this balance is difficult because of the number of competing and possibly conflicting project requirements. Considering top level issues provides a valuable scoping mechanism. It forms a basis for documenting design assumptions, which typically are not included in project documentation. This applies to corporate developer and hobbyist alike. So, let us assume our objectives incorporate scalability, distribution, etc. That leaves us to focus on the application level design (the Hotel) and the important facets here are perspective, boundary and granularity.
Perspective addresses the focal point for the design of the application (presumably the hotel staff), who is it for, what do they want it do now, and later. It also provides the first part of the mechanism to scope reuse. Reuse is a term that has been applied at many different levels and has plenty of baggage with it particularly following the market hype. So to focus our thoughts on reuse here, we are concerned with being able to reuse design via patterns (which is not considered here) and code via reusable objects (components) and extension of code via inheritance and polymorphism.
Boundary, addresses the system boundary, what is within the system and by definition what is therefore excluded. This is also the second part of the mechanism to scope reuse. The questions that we are interested in here are what are the current boundary points and which ones are likely to extend outwards? For example, what information about a customer needs to be captured? If initially that is scoped to include only their name, and payment method is separate for now, that might later be extended to include their address for future marketing mailshots.
Granularity relates to the question of what level of detail do we need to decompose the problem. This helps to define internal boundaries achieving the appropriate balance between the general and the specific, the complex and the simple. In principle we want a design that exposes a simple and general interface and hides the specific and complex implementation. In our example, we want our hotel design to hide detail that is too complex or unnecessary. For example, our hotel might be built up from objects that are contained within a room, (bed, bathroom, TV, fridge) but is that necessary? Is it decomposing the rent-a-room issue to a level of detail that is not useful? However, we definitely need to know how many people the room can accommodate. In addition, if these rooms are conference rooms we will need to know what presentation equipment is available. This forms the third and final part of the mechanism to scope reuse, which is discussed next.
Now that we have a perspective, an idea on the system boundaries and the granularity that we need to work at, we need to reconsider these in terms of likely boundary extensions or future reuse. For example, initially all of the bedrooms or meeting rooms had the same equipment level and were only differentiated by capacity and price - is that likely to change? Will these rooms have further factors that we may need to model in our design? For example, adjoining bedrooms for families, or special facilities for children. What about other hotel facilities such as lounge, diner, bar or staff facilities? Considering the hotel design model in this fashion allows us to make meaningful choices regarding reuse and explicitly understand what choices we are making and why.
So, to look at the design in terms of perspective, boundary and granularity, we could start by producing a design such as below. The numbers refers to indentation levels.
(1) Hotel (Top level class to provide hotel facilities for
customers)
(2) Facilities (customer facilites, free or paid for)
(3) HireFacilites (in use for a period of time)
(4) BedRooms (bedroom for one or more)
(4) Conference Rooms
(4) Presentation Equipment (used in meetings)
(3) Bar Lounge (recreation)
(3) Breakfast/Dining Room (restaurant facilities)
(2) Staff
(2) Private areas
(3) Kitchen
(3) Storage
To interpret this poor man's class inheritance diagram, a hotel is composed (HAS-A relationship) of customer facilities, which are either hired or rented for a period of time, or are generally available to customers. Notice that the Presentation Equipment is currently considered as part of the HireFacilities (IS-A relationship) since it is in use for a period of time and it is not a permanent fixture of a conference room. We can also reason about the design in terms of reuse and possible mechanisms to extend the design such as other hotel facilites (gymnasium, swimming pool...) or new HireFacilities (video equipment to record presentations). Does the current design support that?
So far we are considering reuse as primarily an extension mechanism (inheritance and polymorphism) and additional selective composition. We could also think in terms of reusable objects or components, such as the hotel itself. What interface specification should it support? Is that specification a general or specific case? Can we use the hotel generalisation in another similar context? What effort should we put into being able to use the hotel abstraction for similar concepts such as Bed and Breakfasts? Or could the HireFacilities concepts be used for a company to manage its own facilities?
In terms of perspective, boundary and granularity we may have created the above model of the hotel that is sufficient for our purposes. But, we will not really know that until we place it into context with other important classes that compose the solution's design. It is the essential Customer class that I wish to deal with next. From the implementation perspective Paul's original customer class had a number of problems (p19), however, although these were dealt with in terms of design consideration at implementation level coding with C++ (p20) I think it missed the point. Let me explain. A customer books one or more rooms for a defined time period and can pay in any number of ways (cash, cheque, credit card, account...) or that customer may not pay at all as the customer's company will pay the bill instead. Therefore we clearly have a number of concepts that emerge from this simple statement (a) Customer and (b) Payment. From the problem statement we already know that the Payment may not actually be related to the Customer, since it may be separately settled by the company. However, a further relationship ties these together, (c) Booking. The Hotel knows that once a booking is made, that room is now unavailable for a defined time period and a charge is due that will be paid. Therefore, in class design terms ,we could be considering the problem as:
(1) Booking (transaction that ties hotel, customer and payment
together)
(2) Hotel (facilities being hired or rented)
(2) Customer (customer who will be using those facilities)
(2) Payment (mechanism to settle hotel bill for use of
facilities)
We assume that the customer also books the facilities so need not model a separate entity for the booking customer. However, if a company secretary is booking the facilities then that may need to be captured for reference so we might do that like this:
(1) Booking (transaction ties hotel, customer and payment
together)
(2) Hotel (facilities being hired or rented)
(2) Customer (name, address, telephone number)
(3) Guest (person going to the hotel)
(3) Contact (person doing the booking of facilities)
(2) Payment (mechanism to settle hotel bill for use of
facilities)
Now we must question our Booking, Customer and Payment classes before looking at their implementation. For example, one question might be why is Booking the top level class and is composed of a Hotel, Customer and Payment? Why not let the Hotel be a collection of Bookings? Good question! Perhaps booking could be a centralised function, as when a group of hotels has a central booking office. This saves duplication and offers other advantages such as a customer may find that one hotel, in the area, is fully booked but that another hotel has spaces. If the bookings were part of the hotel only then that facility would not be available to a would-be customer.
Another possibility is to look at this in terms of distributing logic and what it exposing interfaces. For example, if the hotel simply informs the booking office of what is available it can attend to all of the low level day-to-day management issues of staff, rooms, etc. The booking office concentrates on providing a single point of contact for prospective customers.
In summary, after a sufficent period of poking, prodding and adjustment to our overall class design we would move on to the implementation and coding level considerations, which is really the point where The Harpist's code review comes in. The general coding advice offered seems sound and is not something I would question, except (given the design above) that the CustomerRecord (p20) is trying to do too much. Also, an opportunity was missed to discuss the design in more general terms and to offer concrete advice on how to apply the concept of reuse to a design. But more than that, in not considering the design as opposed to the implementation, a useful separation of Customer and Payee did not occur. Consequently we have a CustomerRecord which mixes up static members and pointers in an effort to address what may be considered a flawed design.