Not everyone can be the most popular. A lot has changed in the last 20 years. Since Design Patterns: Elements of Reusable Object-Oriented Software first came out, developers have applied these patterns thousands of times. The patterns we summarize in this appendix are full-fledged, card-carrying, official GoF patterns, but aren’t used as often as the patterns we’ve explored so far. But these patterns are awesome in their own right, and if your situation calls for them, you should apply them with your head held high. Our goal in this appendix is to give you a high-level idea of what these patterns are all about.
Use the Bridge Pattern to vary not only your implementations, but also your abstractions.
A scenario
Imagine you’re going to revolutionize “extreme lounging.” You’re writing the code for a new ergonomic and user-friendly remote control for TVs. You already know that you’ve got to use good OO techniques because while the remote is based on the same abstraction, there will be lots of implementations—one for each model of TV.
Your dilemma
You know that the remote’s user interface won’t be right the first time. In fact, you expect that the product will be refined many times as usability data is collected on the remote control.
So your dilemma is that the remotes are going to change and the TVs are going to change. You’ve already abstracted the user interface so that you can vary the implementation over the many TVs your customers will own. But you are also going to need to vary the abstraction because it is going to change over time as the remote is improved based on the user feedback.
So how are you going to create an OO design that allows you to vary the implementation and the abstraction?
The Bridge Pattern allows you to vary the implementation and the abstraction by placing the two in separate class hierarchies.
Now you have two hierarchies, one for the remotes and a separate one for platform-specific TV implementations. The bridge allows you to vary either side of the two hierarchies independently.
Use the Builder Pattern to encapsulate the construction of a product and allow it to be constructed in steps.
A scenario
You’ve just been asked to build a vacation planner for Patternsland, a new theme park just outside of Objectville. Park guests can choose a hotel and various types of admission tickets, make restaurant reservations, and even book special events. To create a vacation planner, you need to be able to create structures like this:
You need a flexible design
Each guest’s planner can vary in the number of days and types of activities it includes. For instance, a local resident might not need a hotel, but wants to make dinner and special event reservations. Another guest might be flying into Objectville and needs a hotel, dinner reservations, and admission tickets.
So, you need a flexible data structure that can represent guest planners and all their variations; you also need to follow a sequence of potentially complex steps to create the planner. How can you provide a way to create the complex structure without mixing it with the steps for creating it?
Remember Iterator? We encapsulated the iteration into a separate object and hid the internal representation of the collection from the client. It’s the same idea here: we encapsulate the creation of the trip planner in an object (let’s call it a builder), and have our client ask the builder to construct the trip planner structure for it.
Encapsulates the way a complex object is constructed.
Allows objects to be constructed in a multistep and varying process (as opposed to one-step factories).
Hides the internal representation of the product from the client.
Product implementations can be swapped in and out because the client only sees an abstract interface.
Use the Chain of Responsibility Pattern when you want to give more than one object a chance to handle a request.
A scenario
Mighty Gumball has been getting more email than they can handle since the release of the Java-powered Gumball Machine. From their own analysis they get four kinds of email: fan mail from customers that love the new 1-in-10 game, complaints from parents whose kids are addicted to the game, and requests to put machines in new locations. They also get a fair amount of spam.
All fan mail should go straight to the CEO, all complaints should go to the legal department and all requests for new machines should go to business development. Spam should be deleted.
Your task
Mighty Gumball has already written some AI detectors that can tell if an email is spam, fan mail, a complaint, or a request, but they need you to create a design that can use the detectors to handle incoming email.
With the Chain of Responsibility Pattern, you create a chain of objects to examine requests. Each object in turn examines a request and either handles it, or passes it on to the next object in the chain.
Each object in the chain acts as a handler and has a successor object. If it can handle the request, it does; otherwise, it forwards the request to its successor.
As email is received, it is passed to the first handler: the SpamHandler. If the SpamHandler can’t handle the request, it is passed on to the FanHandler. And so on...
Decouples the sender of the request and its receivers.
Simplifies your object because it doesn’t have to know the chain’s structure and keep direct references to its members.
Allows you to add or remove responsibilities dynamically by changing the members or order of the chain.
Commonly used in windows systems to handle events like mouse clicks and keyboard events.
Execution of the request isn’t guaranteed; it may fall off the end of the chain if no object handles it (this can be an advantage or a disadvantage).
Can be hard to observe and debug at runtime.
Use the Flyweight Pattern when one instance of a class can be used to provide many “virtual instances.”
A scenario
You want to add trees as objects in your hot new landscape design application. In your application, trees don’t really do very much; they have an X-Y location, and they can draw themselves dynamically, depending on how old they are. The thing is, a user might want to have lots and lots of trees in one of their home landscape designs. It might look something like this:
Your big client’s dilemma
You’ve just landed your “reference account.” That key client you’ve been pitching for months. They’re going to buy 1,000 seats of your application, and they’re using your software to do the landscape design for huge planned communities. After using your software for a week, your client is complaining that when they create large groves of trees, the app starts getting sluggish...
What if, instead of having thousands of Tree objects, you could redesign your system so that you’ve got only one instance of Tree, and a client object that maintains the state of ALL your trees? That’s the Flyweight!
Reduces the number of object instances at runtime, saving memory.
Centralizes state for many “virtual” objects into a single location.
The Flyweight is used when a class has many instances, and they can all be controlled identically.
A drawback of the Flyweight pattern is that once you’ve implemented it, single, logical instances of the class will not be able to behave independently from the other instances.
Use the Interpreter Pattern to build an interpreter for a language.
A scenario
Remember the Duck Simulator? You have a hunch it would also make a great educational tool for children to learn programming. Using the simulator, each child gets to control one duck with a simple language. Here’s an example of the language:
The Interpreter Pattern requires some knowledge of formal grammars.
If you’ve never studied formal grammars, go ahead and read through the pattern; you’ll still get the gist of it.
Now, remembering how to create grammars from one of your old introductory programming classes, you write out the grammar:
Now what?
You’ve got a grammar; now all you need is a way to represent and interpret sentences in the grammar so that the students can see the effects of their programming on the simulated ducks.
When you need to implement a simple language, the Interpreter Pattern defines a class-based representation for its grammar along with an interpreter to interpret its sentences. To represent the language, you use a class to represent each rule in the language. Here’s the duck language translated into classes. Notice the direct mapping to the grammar.
To interpret the language, call the interpret() method on each expression type. This method is passed a context—which contains the input stream of the program we’re parsing—and matches the input and evaluates it.
Representing each grammar rule in a class makes the language easy to implement.
Because the grammar is represented by classes, you can easily change or extend the language.
By adding methods to the class structure, you can add new behaviors beyond interpretation, like pretty printing and more sophisticated program validation.
Use interpreter when you need to implement a simple language.
Appropriate when you have a simple grammar and simplicity is more important than efficiency.
Used for scripting and programming languages.
This pattern can become cumbersome when the number of grammar rules is large. In these cases a parser/compiler generator may be more appropriate.
Use the Mediator Pattern to centralize complex communications and control between related objects.
A scenario
Bob has a Java-enabled auto-house, thanks to the good folks at HouseOfTheFuture. All of his appliances are designed to make his life easier. When Bob stops hitting the snooze button, his alarm clock tells the coffee maker to start brewing. Even though life is good for Bob, he and other clients are always asking for lots of new features: No coffee on the weekends... Turn off the sprinkler 15 minutes before a shower is scheduled... Set the alarm early on trash days...
HouseOfTheFuture’s dilemma
It’s getting really hard to keep track of which rules reside in which objects, and how the various objects should relate to each other.
With a Mediator added to the system, all of the appliance objects can be greatly simplified:
They tell the Mediator when their state changes.
They respond to requests from the Mediator.
Before we added the Mediator, all of the appliance objects needed to know about each other... they were all tightly coupled. With the Mediator in place, the appliance objects are all completely decoupled from each other.
The Mediator contains all of the control logic for the entire system. When an existing appliance needs a new rule, or a new appliance is added to the system, you’ll know that all of the necessary logic will be added to the Mediator.
Use the Memento Pattern when you need to be able to return an object to one of its previous states; for instance, if your user requests an “undo.”
A scenario
Your interactive role playing game is hugely successful, and has created a legion of addicts, all trying to get to the fabled “level 13.” As users progress to more challenging game levels, the odds of encountering a game-ending situation increase. Fans who have spent days progressing to an advanced level are understandably miffed when their character gets snuffed, and they have to start all over. The cry goes out for a “save progress” command, so that players can store their game progress and at least recover most of their efforts when their character is unfairly extinguished. The “save progress” function needs to be designed to return a resurrected player to the last level she completed successfully.
The Memento has two goals:
Saving the important state of a system’s key object.
Maintaining the key object’s encapsulation.
Keeping the single responsibility principle in mind, it’s also a good idea to keep the state that you’re saving separate from the key object. This separate object that holds the state is known as the Memento object.
Use the Prototype Pattern when creating an instance of a given class is either expensive or complicated.
A scenario
Your interactive role playing game has an insatiable appetite for monsters. As your heroes make their journey through a dynamically created landscape, they encounter an endless chain of foes that must be subdued. You’d like the monster’s characteristics to evolve with the changing landscape. It doesn’t make a lot of sense for bird-like monsters to follow your characters into underseas realms. Finally, you’d like to allow advanced players to create their own custom monsters.
The Prototype Pattern allows you to make new instances by copying existing instances. (In Java this typically means using the clone() method, or de-serialization when you need deep copies.) A key aspect of this pattern is that the client code can make new instances without knowing which specific class is being instantiated.
Use the Visitor Pattern when you want to add capabilities to a composite of objects and encapsulation is not important.
A scenario
Customers who frequent the Objectville Diner and Objectville Pancake House have recently become more health conscious. They are asking for nutritional information before ordering their meals. Because both establishments are so willing to create special orders, some customers are even asking for nutritional information on a per ingredient basis.
Lou’s proposed solution:
Mel’s concerns...
“Boy, it seems like we’re opening Pandora’s box. Who knows what new method we’re going to have to add next, and every time we add a new method we have to do it in two places. Plus, what if we want to enhance the base application with, say, a recipes class? Then we’ll have to make these changes in three different places...”
The Visitor works hand in hand with a Traverser. The Traverser knows how to navigate to all of the objects in a Composite. The Traverser guides the Visitor through the Composite so that the Visitor can collect state as it goes. Once state has been gathered, the Client can have the Visitor perform various operations on the state. When new functionality is required, only the Visitor must be enhanced.