Exercise Solutions (Restricted)
The aim of an object-oriented development activity should be to ensure that the right objects are available to the right objects at the right time and that they are providing the right services by accepting the right messages. Classes are a means to that end in most languages. The class mechanism tends to vary from language to language; indeed, some languages don't really have them. A development driven by a quest for the right objects will thus be a little more language-independent than a development driven by classes.
The input available to a designer -- an object-oriented analysis -- tends to suggest object instances rather than classes, so trying too early to figure out the classes that are needed means that the available evidence isn't being used or is being used inappropriately.
It's more difficult to design classes than objects or object types (certainly if we are obeying the prime directive, and keeping all our instance variables private). Classes have extra considerations; their factoring and their constructors, for example. Despite what Dijkstra might have said, the best objects are often designed anthropomorphically or metaphorically; it's easier for us human developers to see things with an object orientation than it is with a class orientation.
Objects can be "bigger" than classes. By thinking about objects first and classes second, we can satisfy two goals in a neat way. We can get the right objects in an intuitive way, and without worrying about their size or complexity metrics. You see, we won't be maintaining objects; we will be maintaining classes; it's classes whose size and complexity metrics are important; but we can worry about that when we've got the right objects; and we can improve things by factoring out component objects, private and protected (C++ sense) methods and superclasses.
The practical upshot of this is that we shouldn't begin the design activity by staring at class structure diagrams. Instead, we should begin design by carrying out exercises like CRC, and by drawing sequence diagrams as well as structure diagrams. And those structure diagrams should aim, initially, to depict object types rather than classes.
Only when the right objects are becoming clear to a designer and a design, does the evidence start to exist as to what classes should be developed.
[When one looks back at the origins of OOA and OOD, one can see that some of our early thoughts, about the criteria to be used, only work if we apply them to objects; and then follow that up with extra, possibly more recent, considerations about good, and maintainable, classes. JD]
If you are doing this on your own, I can’t give you much help on developing the necessary split personality other than visualization. Before deciding if you as menu, as menu section or as dish, etc. would take a responsibility, visualize the menu, the menu section, etc. If you are a dish, remember that we decided a dish was the concept that a serving realized.
[Warning: this exercise takes a long time to do properly; longer than I estimated when I put the exercise into the book. A regular CRC session, with several experienced players could expect to take a couple of hours. One one's own, and with limited experience, one can multiply that fivefold or even tenfold. My solution might look enormously long, but that's because I've included a transcript of the CRC session. JD]
[I think that one of the most typical scenarios would be taking an existing menu -- probably yesterday's or last week's -- and replacing one dish by another, prior to printing. Lecturers and tutors might want to give more guidance, when setting the exercise, as to the particular scenario. JD]
Name: Printing a menu created by modification
Level: Primary task
Typical actor: Non-novice restaurant staff member
Stakeholder-interest: Owner/manager - maximize fame and profit; Staff - quick, easy and foolproof menu creation
Precondition(s): The dishes to be used are already registered with the system
Minimal guarantees: No existing menus are changed; all menus registered with the system are consistent and valid
Success guarantees: New menu registered; said menu available for formatting, printing, etc.
Initiating event: User elects to prepare a menu for printing
Output(s): Sequential-character-based representation of new menu
State change(s): system registers details of a new menu
[This is just an example. The respondent, with the sponsor hat on, may detail a different scenario. What we are looking for is a sensible use case description with good words and as little ambiguity as possible. JD]
[The book wasn't trying to be a book about requirements. That may well end up an entire book in its own right. The book didn't go into detail about extra or alternative detail for use cases. Your study may well have led you to, for example, Cockburn's suggestions for use case content, in which case you may have had some of the extra detail above. If you just had initiating event, outputs, state changes and storyboard, that'll do for now. JD]
[I've done this as a transcript of a CRC; a little bit like the one in the book; here though, I haven't given the characters names, nor have I set the scene. I've only occasionally put in who is talking.
As usual my personal interruptions are in square brackets.
"OK. Let's say that there's some kind of menu-search form on the screen able to accept a date."
"Or a date range," interrupts someone else.
"Able to accept a date, or date range, from the user. It gets filled in and validated."
[Yes, GUIs and interfaces/views can and should do lexical and syntactic validation. What they should not do is semantics. The test is portability. Can we disconnect the application from the GUI? And can we disconnect the GUI from the application?]
"Then a button is pressed. We've often found that the button's label gives a good clue as to the message, so what do we think the button would say? Search? Find? Let's try 'Find'."
Mod: "So we've a message, headed from the view [GUI] to the model [application], findMenu(Date) return a menu object."
"What about date ranges? What if there's more than one menu because a date range was given?"
"We should send two parameters. No, whoops, that's one parameter too many. Ah, we need a Duration or Period object. After all, one day is a period of time, isn't it?" Someone makes a note to check if our language's library or framework has such a thing.
"OK, using UML syntax, the message is 'findMenu(searchPeriod: Duration ): Menu'."
"Now, which type of instance gets the message?"
[Someone might suggest that Menu finds menus. But that won't work. Which menu instance would take responsibility for finding other menus? And class methods (known as "statics" in Java and C++) won't do either. Once you start doing things with class methods, you've drifted away from object-orientation towards class-orientation. And the former definitely works better.]
[Someone might suggest "the database". Well, no. Once one object knows that any kind of database exists, they'll all want to and then we're doomed if, for example, we want to move from one persistent data technology to another; especially if it's from a creaky or rickety technology like relational or object-relational to something more svelte like persistent objects.
In order to hide a non-relational database, and to better use an "object-oriented database", we should ask an object to find menus, and hide the way it's done in there (and maybe even one more level of hiding than that). So, which object?]
[Someone might suggest the Restaurant. However, while the typical statement, "Let's make a booking at Zingarelli's restaurant," left me (the designer) reasonably happy that a restaurant be the first application object to receive a message in a "booking" use case scenario, I (a restaurant instance) would not be quite so happy to accept findMenu(). It seems "beneath me"; although I know enough (or enough objects that know enough), I know more than is needed in order to take responsibility for this message. I suspect that there's another object that's "closer" to the information. I suspect that if I took responsibility there would be delegation with no added value, i.e. buck-passing rather than real delegation.]
Our participants look at some structure diagrams ("class" diagrams), pinned to board. There seem to be no clues, however.
Everyone seems to be stymied.
"Let's try some existing-systems analysis. How do they currently record the menu usage patterns? First possibility: they have a log book; second possiblity: they write it in the diary."
However, Diary refuses. "Outside my character," it says. Especially if it sees itself as the same kind of diary that was deployed in the "booking" CRC, in the book (11.11.2).
"Looks like we need a new object. Maybe we have to invent one. Or maybe they really do use a log book. We need a 'menuLog'."
A card is written out and a player chosen.
"MenuLog, would you take responsibility for 'findMenu(Duration searchPeriod):Menu'?"
"Yes. It would be churlish to turn it down if that's what I've been invented for. But I could be a bad idea. However, I don't think I am a bad idea. Yes, I'll do it. Now, if I'm a log, it seems in my nature that I would be strongly aware of the idea of a time or time period, so I would simply return the IDs of the menu instances referenced by the time period. I don't think I have to delegate."
"Are you sure? We shouldn't pass up an opportunity for an object to be simpler."
"Or more general-purpose. What if we needed several logs, and this log is just an instance of a general-purpose log class of some kind? In fact, some platforms provide generalized logging services."
"You're right! I could simply be a collection of log entries and I message them to divulge their menus."
"That's not particularly general purpose -- assuming that a log entry contains a menu."
"Why not ask it if it has a menu instance associated with it. Ah, no. That would involve a cast, and we don't like casts."
"Let's try 'turn it around'. Could we say to an entry, 'Do you have one of these?' passing an example instance, a menu instance in this case. The entry simply does a type comparison between the argument object and object it's linked to, and returns it if there's a type match, without the code ever specifically referring to the type, or doing a cast."
[This isn't quite good enough. At a low level the return type makes the method too specific. At a more conceptual level, we object to a method doing to jobs: "Are you linked to one of these?" and "Give me a reference to your linked object." In a language with generics or templates, they would offer a neat solution.]
"I like it. Let's write an 'algorithm suggestion' straight after the session. I think we can agree that the responsibility has been taken and that any further exploration of the delegation isn't validating object type interfaces. Let's move on."
"OK. A set of menu instance references or pointers is returned to the GUI."
"Eek. Doesn't that mean that I (the GUI) have to start knowing an awful lot about restauranting?"
"Seems inescapable. You (the GUI) have to display enough characteristics of the menus that they can be recognized by the user."
[The worry about too much knowledge is a good one. Within the "view" of the highest-level model-view separation, there needs to be a mini-model-view separation. What Swing (a Java view technology) calls "model-delegate". There is a little more on the "model-view" architecture in a moment. Quite rightly, however, these people are focusing on the design of the application (the "model" of the highest-level model-view separation).]
"You're right. I'll have to message the menu for some things."
"Let's just try an example. It'll validate a bit of the menu object type."
"Menu, would you take responsibility for dateDeployed()?"
"Fair enough. What does the use case involve next? Ah, yes. A menu is selected, portrayed and then a dish is found and removed."
"I think the details of the portrayal will be the same kind of details as dateDeployed() just now; only more of them. Do we need to go through that?"
"Yes. No short circuiting. I mean, to truly confirm that you've found the menu you're looking for, surely you (the user) will need to see some dish details."
"Don't we just message the menu in question to portray itself suitably for a screen presentation?"
"Yes but I can envisage such a message eventually being replicated in myriad subtly different ways for different clients' needs. That's a mess. The trouble is, a general toString() method isn't necessarily going to work either; there's no guaranteeing it'll be suitable." [Java and Smalltalk have a guaranteed string representation (toString and printString) of any object; but only its presence is guaranteed; what the string actually contains could be anything.]
"OK. The GUI has to send individual query messages to get the items of information that it can then assemble into a reasonable display."
"But doesn't that make the GUI hugely complicated and highly dependent on forbidden knowledge of the model [the application classes]?"
"Well a view has to have some knowledge of the model -- just minimal knowledge. And it doesn't have to be complicated if it's factored into a set of objects: for example, one for the intrinsic menu stuff and another for dish stuff. Now, we shouldn't be designing the GUI in a CRC, but I think it's reasonable to say that it could be done with decent 'objects' that would each know only about their model counterpart."
"OK. For example, then, the menu gets a message title(). Menu are you happy to accept responsibility for that?" [Menu nods.] "Do you need to collaborate?" [Menu shakes her head.]
"And, Menu, are you happy to accept responsibility for giveUsYourDishes()?" [Menu shakes her head. Her interlocutor is somewhat taken aback.]
"Hmm. Why not?"
[Menu waives a picture of a Greek lady attached to a short stick.] "Demeter."
"You're right, of course. That would be wrongful access of the objects inside the object I can see. What would be an acceptable message? It can't be give me a description of your first dish, since we want a GUI [view] dish object to send its choice of query messages to a model dish object."
"I think you've put your finger on it there with 'first'. The idea of an iterator is well-established. I recall that we noted a peculiar characteristic of the menu when doing the subject matter analysis. It had hardly any attributes [intrinsic measureable properties] it was mostly who it knew rather than what it knew."
"Aha! Menu would you accept responsibility for dishIterator()?"
"Yes I think I would."
Mod: "So the GUI 'menu' knows some menu query messages, and that a menu 'has' dishes and can be asked for a dish iterator. We presume, because we're not designing the GUI [which anyway is usually done by tool rather than by hand-coding] that successive dish references/pointers are passed to a GUI 'dish' object whose mission is portrayal of a dish. We want to know about example messages from the GUI 'dish' object to the model dish object instance, however, so let's posit name() and price(). Dish, would you accept responsibility for answering a name() message." Dish nods. "And price()?" Dish nods once more.
Menu: "I think we should follow the rules properly and ask me if I need to collaborate over my responsibility for dishIterator()." The moderator looks embarrassed.
Mod: "Quite right. Who would you like to collaborate with?"
Menu: "I'd like to collaborate with my menu sections."
There are several "Doh!"s from around the table.
"We need to backtrack a bit. Won't GUI.Menu need to ask our menu what sections there are and then display the menu section by section?"
"You're right. Menu would you take responsibility for 'what sections have you got?'"
Menu: "I think I'll refuse on the grounds of vague return."
"We should do what we did before. Why not ask Menu for a section iterator that returns successive section pointers …" Menu nods. "… and then GUI.Menu or GUI.Section messages sections for their names and for a dish iterator from each." [People tend to say "dot" when qualifying names: "GUI dot Menu". To use the UML/C++ "dot, dot, dot, dot" (::) is a little cumbersome.]
A menu section card is created. The bottle is spun. The Menu Section player is asked, "Would you take responsibility for name()?" "Yes," says Menu Section. "Do you need to collaborate?" "No," says Menu Section. "Would you take responsibility for dishIterator()?" "Yes." "Do you need to collaborate?" "No."
"So the selected menu has been portrayed, its contents have been portrayed, and now the user indicates their selection of a dish. If the GUI messages the selected dish for some information to portray for recognition, such as name(), price() and maybe describe() as well this time, Dish would you still be happy for the first two and for the third? Dish says, "Yes".
"And now the user indicates that the selected dish is to be deleted. So where does that message go?"
"To the … Oops. We've missed a whole chunk of stuff here. Where do we create the new menu? The one that's a copy of the chosen menu."
[Many programmers [hackers] would find themselves doing all this to and fro, back and forth, in code. And the code would very soon be a complete mess. Isn't it much better to be doing the toing and froing here?]
"OK, how does the user indicate that they want to copy the found menu and modify the copy. The use case doesn't say? Does the whole dialog start off as a "New Menu" diaglog? Or does the system ask them if they wouldn't rather modify a copy, at the point where they are about to change the found menu?"
"Well, as we know, the GUI design is still evolving over with Team B. And we are CRCing the application [model] classes. Is it sufficient for us just to explore who takes responsibility for copying a menu instance, for changing a menu instance, etc. We don't have to ask exactly when those messages occur."
"That's true. Except for John, here, who has to draw the sequence diagram, and who will need to put that message somewhere."
Mod: "OK. Let's make a note that this CRC session has arbitrarily put the menu copying message at the point where the menu that we just realized had to be copied, was selected." "Do we agree that that's OK?" There is agreement. "And which object should perform a copy of a menu?" It is decided, quite reasonably, that the source menu object should be asked to make and return a copy of itself.
"So where were we? The user indicates that a dish that's selected/highlighted in some way on the screen is to be removed from the menu-under-creation-by-copy-and-modification. Now, who takes responsibility for that? Menu? Menu Section?"
They glance at the structure diagrams ("class" diagrams).
"Seems like it should be Menu Section. What do you think, Menu Section? Is it your responsibility to remove a listed dish?"
Menu Section agreed and no collaboration seems to be required.
Another message comes in from the GUI requesting that the available dishes be listed, as the use case says that our user is going to pick a new dish for the menu-under-construction. Who will take responsibility for availableDishes()?
[Someone, especially in an experienced group might say, "A static (class) method of Dish." This isn't a good idea, however. If one is intending to be object-oriented, then functionality should be provided by object instances running methods on their own, individual state. A class method (a "static" in C++, etc.) is run by the system in C++, where a static is more like a regular (global, C, free) function rather than a member function); or run by a class in Java and Smalltalk].
Chef? Well, … it seems promising. Chef would probably agree. There are no show-stopping reasons to refuse. If you went with Chef, it should work.
Repetoire? Yes. Repetoire agrees that it's its responsibility. [There was a repetoire object suggested by the analysis. However, it's quite likely that many analysis effort might not have included it -- initially at least. Hence going with Chef might be an alternative. On balance, I think I (the designer) prefer Repetoire. It seems a better home for "all the dishes we might offer". Chef seems close but not quite as close.]
"OK, Repetoire, who are you going to collaborate with? Who are you going to delegate to?"
Repetoire: "It seems pretty simple to me, I message each dish for it's name and I return … Ah, it's not so obvious. What good is a collection of strings? GUI.Repetoire should have some choice as to which messages it uses in order to get what it wants to display. I guess it's an iterator again."
"I'm just a bit worried about these iterators. Can I think aloud for a moment about the validity of what we're doing? We are considering an interface message, dishIterator(), both for a menu section and a repetoire." ("So there's scope for some kind of super-type similarity between these two?") Now, it's true that while they might not substitute for list objects [the Principle of Substitutability would say they shouldn't carry a list type], a large part of their nature is to list dishes. And it's been found time and time again that a flexible way to access a list without actually accessing the list is to ask the list to provide an iterator. And it would certainly enable the application-facing side of the GUI to obtain a collection of strings with which to populate some kind of screen choice list. OK. I'm happy.
[Students who are familiar neither with iterators nor model-view, should consider finding out more about both. Neither are particularly specific to object-orientation. I have, on another web site, a paper that would get you started on model-view. For iterators you could look in the design patterns world for the External Iterator pattern; or you could look in the C++ world for the STL's (Standard Template Library) iterators; or you could look in the Java world for the Collections Framework.]
"The user indicates a chosen dish which is portrayed. We have already established some messages with which a dish could be portrayed to the user; so the GUI or whatever can do its portrayal."
"Now we want to modify the portrayed price of the dish. The clicking and typing isn't germane. Let's assume we get to a point where some kind of 'Update' button has been pressed."
"Who should take responsibility for price(newPrice:Money)?"
"I would have thought Dish. Dish, what do you think?"
"I'm not sure. What price am I modifying? Is it that I used to have a price, even if I'd never been used in a menu before? And now that I'm about to be used, I am learning of a more correct price? Or, if it's the case that I've never been used before, am I learning my price?"
"Or maybe the price we're talking about is the price for this appearance of this dish on this menu and that another appearance of this dish, in the future, say, might carry a different price. And maybe the system needs to be able to recall the price of a dish for any of its appearances."
"Ah. I don't think that's ever occurred to us before. Maybe the price we're talking about is a property of the menu and not the dish."
"Yes, Menu, would you take responsibility for price(newPrice:Money)?"
"I don't see how I can. Which dish's price is it?"
"Hmm. We've encountered this many times before, if you see what I mean. Perhaps we're missing the intersection object. What has appeared so far to be a many-to-many relationship, and thus already problematical, has encountered further problems, and only now are we being led to discover the 'missing object/entity' that the OOAandD course we attended went on about so much. Is it that we need an 'Offering' between a menu and its particular use of a dish?"
Randomization devices are deployed; a card is written out; a player gets into character; and, yes, an offer object is a righteous object and it knows the price. And perhaps a dish has a "nominal" or "suggestion" or "guide" price. An offer instance is in a many-to-one, existence-dependent (aggregation) relationship with a menu instance; and in a one-to-one relationship with a dish instance. Offer objects are nicer object to festoon with query messages delivering the details of the offer: the price, the name and the description. It is more natural that they describe the offer, rather than the dish. The dish's interface doesn't need to be "polluted" in any way. If the offer finds it appropriate to ask its dish then fair enough; if the offer stores or computes its answer, so be it.
[So another many-to-many relationship bites the dust. And more evidence accumulates that those who say that not only are n-ary associations a chimera and a mirage, but many-to-many associations fall into the same category, might be right.]
"Now that tonight's new dish has been chosen, the use case says that this dish -- sorry dish offering -- is added to the menu-under-construction."
"I don't think that's quite true. The menu sections are listed, and the user chooses one. And I've just had a thought. Somebody might decide to support this kind of thing with drag-drop. So the user might drop the dish offering into the menu section of their choice."
"You're right. Presumably we message Menu for it's menu sections. Ah, but we've already established that that's possible a moment ago. We're determining application object type messages and this wouldn't uncover anything further. Onwards."
"Right, we want a message to a menu section. How about addOffering(Offering)?"
"It's best not to include type names in message names; add(Offering)."
"Hey. I've just realized. I had been wondering about to type of the argument if we had been wanting to add a dish to a section; whether it would have been add(MenuItem) or add(Victual) or add(Comestible). That's another design decision that the existence of the offer object can hide."
"OK. Menu Section would you take responsibility for add(Offering)?"
"I'm almost happy. But I think I'd like an indication of where to put it. And, on behalf of Offering, I'd like to ask where the offering came from. Who makes it?"
There is some debate. Then someone realizes that the natural perception is that one adds a dish to a section of a menu and the section then offers that dish at some price. So they revert to adding a dish to a section, with the section creating the offer object. They then move on to MenuSection's position worry.
"How about two arguments add(Offering, position)?"
"We'd prefer one argument over two."
"I know, I know. Menu Section has given us an iterator. Can we message the iterator to move to a place and then message it to insert a dish?"
"Let's ask." The moderator writes out a card, spins the bottle and passes the card to a new role-player. "You're an iterator. Would you accept responsibility for moving to a particular place in the collection you're iterating over?"
"And would you accept responsibility for insert(T)?"
"What do you mean 'T'?"
"Sorry, I was assuming something like C++ templates or Java generics and that an iterator would be fairly independant of what it was iterating over. 'T' is a traditional, formal name for an as-yet-unchosen type."
"I see. Yes I think I would accept responsibility. But what about if I was asked to go to a non-existent position?"
"Well this use case/CRC is exploring a successful menu adaptation and print. It's a good point, though. Why don't we make a note of the question? Iterators are well-known; there must be some standardized answers to that kind of question."
The use case is consulted. The time has arrived to get the menu output.
"Where does the interface send its message for the lists of possibilities as to layout, format and form?"
[At this point your author has run out of time, stops playing the roles and starts to write what he imagines is a likely answer. If you carry on playing the roles and following the CRC method as per the first part of this answer, you may well come up with a better thought out solution to the remainder of the use case than that which follows. You might drop me an email if you do.]
Various possibilities are tried for layout and format. No-one seems to want to take responsibility for format. Eventually Menu agrees that it should offer its layout possibilities, presented of course, as menu layout objects. There might, once again, be debate as to whether such functionality should be via a class [static] method, or via an ordinary instance method. While a class method might seem possible, it's felt that an instance method could work equally well, that something might crop up in the future that would change the functionality into eventually being menu-specific, and most of all, that the general principle, that in object-oriented systems, functionality is best delivered by objects and not classes, should be followed.
When they realize that the return type is a collection of layout objects, one of which is chosen, they further realize that a layout object can take responsibility for the format, and that the format can take responsibility for the form. I.e. they ended up using a chain of "factories" -- a well-tried combination of two famous design patterns: chain of responsibility and factory method [TCP/IP socket connecting and JDBC connecting in Java, for example]. The format possibilities are likely to be volatile -- HTML yesterday, XML today and who knows what tomorrow -- so a factory method is a pretty good idea. The form is likely to a little bit platform dependent, but they realize, that an even more well-known, classic solution to the "producer-consumer" problem would do nicely: streams. The selected form object presents the selected format object with an stream, the selected format object presents itself to the layout object as a stream -- a filter stream.
Much of the describing has been covered already. It would be worrying if the final portrayal mechanisms differed significantly from those deployed so far. The layout object messages the menu to get menu-specific descriptions, and then through a "section" iterator and "offering" iterators it obtains the remaining descriptions it needs. All of the descriptions will be in terms of character streams, to which the layout object adds its own characters. If it's a simple layout, it just adds characters like spaces, tabs and linefeeds. If it's a cleverer kind of layout object, it adds things like RTF, HTML or XML markup. The user might then take the output (a file typically) and pretty it up even futher, in a DTP (desktop publishing) package for example; ALICE has finished its part of the job, though.
[The separation of responsibilities might strike some as being over-the-top. However, it is much more amenable to extension. For example, one could easily create a new kind of menu layout object that used a JSP page as its form object. The JSP page could have beans that send messages to the other objects.]
Sequence Diagrams (click in images for a higher-resolution version):
[I've broken the sequence into two diagrams, the second simply following on from the first. JD]