Chapter 3. The Decorator Pattern: Decorating Objects

image with no caption

Just call this chapter “Design Eye for the Inheritance Guy.” We’ll re-examine the typical overuse of inheritance and you’ll learn how to decorate your classes at runtime using a form of object composition. Why? Once you know the techniques of decorating, you’ll be able to give your (or someone else’s) objects new responsibilities without making any code changes to the underlying classes.

Welcome to Starbuzz Coffee

image with no caption

Starbuzz Coffee has made a name for itself as the fastest growing coffee shop around. If you’ve seen one on your local corner, look across the street; you’ll see another one.

Because they’ve grown so quickly, they’re scrambling to update their ordering systems to match their beverage offerings.

When they first went into business they designed their classes like this...

image with no caption

In addition to your coffee, you can also ask for several condiments like steamed milk, soy, and mocha (otherwise known as chocolate), and have it all topped off with whipped milk. Starbuzz charges a bit for each of these, so they really need to get them built into their order system.

Here’s their first attempt...

image with no caption
image with no caption

Brain Power

It’s pretty obvious that Starbuzz has created a maintenance nightmare for themselves. What happens when the price of milk goes up? What do they do when they add a new caramel topping?

Thinking beyond the maintenance problem, which of the design principles that we’ve covered so far are they violating?

Hint: they’re violating two of them in a big way!

image with no caption

Well, let’s give it a try. Let’s start with the Beverage base class and add instance variables to represent whether or not each beverage has milk, soy, mocha, and whip...

image with no caption

Now let’s add in the subclasses, one for each beverage on the menu:

image with no caption
image with no caption

The Open-Closed Principle

Grasshopper is on to one of the most important design principles:

Design Principle

Classes should be open for extension, but closed for modification.

image with no caption

Come on in; we’re open. Feel free to extend our classes with any new behavior you like. If your needs or requirements change (and we know they will), just go ahead and make your own extensions.

image with no caption

Sorry, we’re closed. That’s right, we spent a lot of time getting this code correct and bug free, so we can’t let you alter the existing code. It must remain closed to modification. If you don’t like it, you can speak to the manager.

Our goal is to allow classes to be easily extended to incorporate new behavior without modifying existing code. What do we get if we accomplish this? Designs that are resilient to change and flexible enough to take on new functionality to meet changing requirements.

While it may seem like a contradiction, there are techniques for allowing code to be extended without direct modification.

Be careful when choosing the areas of code that need to be extended; applying the Open-Closed Principle EVERYWHERE is wasteful and unnecessary, and can lead to complex, hard-to-understand code.

Meet the Decorator Pattern

Okay, we’ve seen that representing our beverage plus condiment pricing scheme with inheritance has not worked out very well—we get class explosions and rigid designs, or we add functionality to the base class that isn’t appropriate for some of the subclasses.

So, here’s what we’ll do instead: we’ll start with a beverage and “decorate” it with the condiments at runtime. For example, if the customer wants a Dark Roast with Mocha and Whip, then we’ll:

  • ① Take a DarkRoast object

  • ② Decorate it with a Mocha object

  • ③ Decorate it with a Whip object

  • ④ Call the cost() method and rely on delegation to add on the condiment costs

Okay, but how do you “decorate” an object, and how does delegation come into this? A hint: think of decorator objects as “wrappers.” Let’s see how this works...

image with no caption

Constructing a drink order with Decorators

  • We start with our DarkRoast object.

    image with no caption
  • ② The customer wants Mocha, so we create a Mocha object and wrap it around the DarkRoast.

    image with no caption
  • ③ The customer also wants Whip, so we create a Whip decorator and wrap Mocha with it.

    image with no caption

    Note

    So, a DarkRoast wrapped in Mocha and Whip is still a Beverage and we can do anything with it we can do with a DarkRoast, including call its cost() method.

  • Now it’s time to compute the cost for the customer. We do this by calling cost() on the outermost decorator, Whip, and Whip is going to delegate computing the cost to the objects it decorates. Once it gets a cost, it will add on the cost of the Whip.

    image with no caption

Okay, here’s what we know so far...

  • Decorators have the same supertype as the objects they decorate.

  • You can use one or more decorators to wrap an object.

  • Given that the decorator has the same supertype as the object it decorates, we can pass around a decorated object in place of the original (wrapped) object.

  • The decorator adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job.

    Note

    Key point!

  • Objects can be decorated at any time, so we can decorate objects dynamically at runtime with as many decorators as we like.

Now let’s see how this all really works by looking at the Decorator Pattern definition and writing some code.

The Decorator Pattern defined

Let’s first take a look at the Decorator Pattern description:

Note

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

While that describes the role of the Decorator Pattern, it doesn’t give us a lot of insight into how we’d apply the pattern to our own implementation. Let’s take a look at the class diagram, which is a little more revealing (on the next page we’ll look at the same structure applied to the beverage problem).

image with no caption

Decorating our Beverages

Okay, let’s work our Starbuzz beverages into this framework...

image with no caption

Brain Power

Before going further, think about how you’d implement the cost() method of the coffees and the condiments. Also think about how you’d implement the getDescription() method of the condiments.

Cubicle Conversation

Some confusion over Inheritance versus Composition

image with no caption

Sue: What do you mean?

Mary: Look at the class diagram. The CondimentDecorator is extending the Beverage class. That’s inheritance, right?

Sue: True. I think the point is that it’s vital that the decorators have the same type as the objects they are going to decorate. So here we’re using inheritance to achieve the type matching, but we aren’t using inheritance to get behavior.

Mary: Okay, I can see how decorators need the same “interface” as the components they wrap because they need to stand in place of the component. But where does the behavior come in?

Sue: When we compose a decorator with a component, we are adding new behavior. We are acquiring new behavior not by inheriting it from a superclass, but by composing objects together.

Mary: Okay, so we’re subclassing the abstract class Beverage in order to have the correct type, not to inherit its behavior. The behavior comes in through the composition of decorators with the base components as well as other decorators.

Sue: That’s right.

Mary: Ooooh, I see. And because we are using object composition, we get a whole lot more flexibility about how to mix and match condiments and beverages. Very smooth.

Sue: Yes, if we rely on inheritance, then our behavior can only be determined statically at compile time. In other words, we get only whatever behavior the superclass gives us or that we override. With composition, we can mix and match decorators any way we like... at runtime.

Mary: And as I understand it, we can implement new decorators at any time to add new behavior. If we relied on inheritance, we’d have to go in and change existing code any time we wanted new behavior.

Sue: Exactly.

Mary: I just have one more question. If all we need to inherit is the type of the component, how come we didn’t use an interface instead of an abstract class for the Beverage class?

Sue: Well, remember, when we got this code, Starbuzz already had an abstract Beverage class. Traditionally the Decorator Pattern does specify an abstract component, but in Java, obviously, we could use an interface. But we always try to avoid altering existing code, so don’t “fix” it if the abstract class will work just fine.

New barista training

Make a picture for what happens when the order is for a “double mocha soy latte with whip” beverage. Use the menu to get the correct prices, and draw your picture using the same format we used earlier (from a few pages back):

image with no caption
image with no caption

Writing the Starbuzz code

It’s time to whip this design into some real code.

Let’s start with the Beverage class, which doesn’t need to change from Starbuzz’s original design. Let’s take a look:

image with no caption
image with no caption

Beverage is simple enough. Let’s implement the abstract class for the Condiments (Decorator) as well:

image with no caption

Coding beverages

Now that we’ve got our base classes out of the way, let’s implement some beverages. We’ll start with Espresso. Remember, we need to set a description for the specific beverage and also implement the cost() method.

image with no caption

Coding condiments

If you look back at the Decorator Pattern class diagram, you’ll see we’ve now written our abstract component (Beverage), we have our concrete components (HouseBlend), and we have our abstract decorator (CondimentDecorator). Now it’s time to implement the concrete decorators. Here’s Mocha:

image with no caption

On the next page we’ll actually instantiate the beverage and wrap it with all its condiments (decorators), but first...

Serving some coffees

Congratulations. It’s time to sit back, order a few coffees, and marvel at the flexible design you created with the Decorator Pattern.

Here’s some test code*to make orders:

image with no caption

* We’re going to see a much better way of creating decorated objects when we cover the Factory and Builder Design Patterns. Please note that the Builder Pattern is covered in the Appendix.

Now, let’s get those orders in:

image with no caption

Real World Decorators: Java I/O

The large number of classes in the java.io package is... overwhelming. Don’t feel alone if you said “whoa” the first (and second and third) time you looked at this API. But now that you know the Decorator Pattern, the I/O classes should make more sense since the java.io package is largely based on Decorator. Here’s a typical set of objects that use decorators to add functionality to reading data from a file:

image with no caption

BufferedInputStream and LineNumberInputStream both extend FilterInputStream, which acts as the abstract decorator class.

Decorating the java.io classes

image with no caption

You can see that this isn’t so different from the Starbuzz design. You should now be in a good position to look over the java.io API docs and compose decorators on the various input streams.

You’ll see that the output streams have the same design. And you’ve probably already found that the Reader/Writer streams (for character-based data) closely mirror the design of the streams classes (with a few differences and inconsistencies, but close enough to figure out what’s going on).

Java I/O also points out one of the downsides of the Decorator Pattern: designs using this pattern often result in a large number of small classes that can be overwhelming to a developer trying to use the Decorator-based API. But now that you know how Decorator works, you can keep things in perspective and when you’re using someone else’s Decorator-heavy API, you can work through how their classes are organized so that you can easily use wrapping to get the behavior you’re after.

Writing your own Java I/O Decorator

Okay, you know the Decorator Pattern, you’ve seen the I/O class diagram. You should be ready to write your own input decorator.

How about this: write a decorator that converts all uppercase characters to lowercase in the input stream. In other words, if we read in “I know the Decorator Pattern therefore I RULE!” then your decorator converts this to “i know the decorator pattern therefore i rule!”

image with no caption
image with no caption

REMEMBER: we don’t provide import and package statements in the code listings. Get the complete source code from http://wickedlysmart.com/head-first-design-patterns/.

Test out your new Java I/O Decorator

Write some quick code to test the I/O decorator:

image with no caption

Give it a spin

image with no caption

Tools for your Design Toolbox

You’ve got another chapter under your belt and a new principle and pattern in the toolbox.

image with no caption
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.236.142.143