0% found this document useful (0 votes)
2 views

03 - Decorator Pattern

Uploaded by

buseccoban
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

03 - Decorator Pattern

Uploaded by

buseccoban
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

CSE351 DESIGN PATTERNS

03. DECORATOR PATTERN

Design Eye
for the Inheritance Guy
Welcome to Starbuzz Coffee
Starbuzz Coffee is the fastest growing coffee shop around
--> if you see one, look across the street, you’ll see another one :]

They need to update


their ordering system
to match beverage offerings.

Their initial design for beverages


<-- is like this…

2
aannnd condiments, the best..
What is best with Starbuzz is you can ask
for several condiments for any beverage:
• steamed milk
• soy
• mocha (known as chocolate)
• all topped with whipped milk
Starbuzz charges a bit for each of these.
Ok, let’s build options…

3
Power up your head - violating design principles
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!

4
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…
5
Sharpen your pencil
Write the cost() methods for the following classes
(pseudo-Java is okay):
Now, let’s add in the
public class Beverage {
subclasses, one for public double cost() {
each beverage // Fill this in
}
}

public class DarkRoast extends Beverage {


public DarkRoast() {
description = "Excellent Dark Roast";
}
public double cost() {
// Fill this in
}
}

6
Sharpen your pencil
What requirements or other factors might change that
will impact this design?
Price changes for condiments will force us to alter existing code

New condiments will force us to add new methods and alter the cost method in the superclass

We may have new beverages. For some of these beverages (ice tea?), the condiments may not
be approriate , yet the Tea subclass will still inherit methods like hasWhip().

What if a customer wants a double mocha?

7
Master and Grasshopper
Master: Grasshopper, it has been some time since our last meeting. Have you been deep in meditation
on inheritance?
Student: Yes, Master. While inheritance is powerful, I have learned that it doesn't always lead to the most
flexible or maintainable designs.
Master: Ah yes, you have made some progress. So, tell me my student, how then will you achieve reuse if not through inheritance?
Student: Master, I have learned there are ways of "inheriting" behavior at runtime through composition and delegation.
Master: Please, go on...
Student: When I inherit behavior by subclassing, that behavior is set statically at compile time. In addition, all subclasses must
inherit the same behavior. If however, I can extend an object's behavior through composition, then I can do this dynamically at
runtime.
Master: Very good, Grasshopper, you are beginning to see the power of composition.
Student: Yes, it is possible for me to add multiple new responsibilities to objects through this technique, including
responsibilities that were not even thought of by the designer of the superclass. And, I don't have to touch their code!
Master: What have you learned about the effect of composition on maintaining your code?
Student: Well, that is what I was getting at. By dynamically composing objects, I can add new functionality by writing new code
rather than altering existing code. Because I'm not changing existing code, the chances of introducing bugs or causing unintended
side effects in pre-existing code are much reduced.
Master: Very good. Enough for today, Grasshopper. I would like for you to go and meditate further on this topic... Remember,
code should be closed (to change) like the lotus flower in the evening, yet open (to extension) like the lotus flower in the morning.
8
The Open-Closed Principle
Grashopper is on to one of the most important design principles

Come on in; we’re open. Our goal is to allow classes Sorry, we are closed.
Feel free to extend our That’s right, we spent a
classes with any new to be easily extended to lot of time getting this
behavior you like. If your code correct and bug free,
needs or requirements
incorporate new behavior so we can’t let you alter
change (and we know they without modifying existing existing code. It must
will), just go ahead and remain closed to
make your own extensions. code. modification. If you don’t
like it, talk to the manager.

9
there are no
Dumb Questions
Q: Open for extension and closed for Q: Okay, I understand Observable, but how do I
modification? That sounds very contradictory.
How can a design be both? generally design something to be extensible, yet
closed for modification?
A: That's a very good question. It certainly
sounds contradictory at first. After all, the A: Many of the patterns give us time tested designs
less modifiable something is, the harder it is that protect your code from being modified by
to extend, right? As it turns out, though, there supplying a means of extension. You'll see a good
are some clever 00 techniques for allowing example of using the Decorator pattern to follow
systems to be extended, even if we can't
change the underlying code. Think about the the Open-Closed principle.
Observer Pattern… By adding new Observers,
we can extend the Subject at any time, without
adding code to the Subject. You'll see quite a
few more ways of extending behavior with
other 00 design techniques.

10
there are no
Dumb Questions
Q: How can I make every part of my design Q: How do I know which areas of change are
follow the Open-Closed Principle? more important?
A: Usually, you can't. Making 00 design A: That is partly a matter of experience in
flexible and open to extension without the designing 00 systems and also a matter of
modification of existing code takes time and the knowing the domain you are working in.
effort. In general, we don't have the luxury Looking at other examples will help you learn
of tying down every part of our designs
(and it would probably be wasteful). to identify areas of change in your own
Following the Open-Closed Principle usually designs.
introduces new levels of abstraction, which
adds complexity to our code. You want to
concentrate on those areas that are most
likely to change in your designs and apply
the principles there.
11
Meet the Decorator Pattern
Okay, we’ve seen beverage + condiment pricing hasn’t worked out
very well with inheritance.
We got class explosions, 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:
Start with a beverage and "decorate" it with the condiments at runtime.
For example; if a customer wants a Dark Roast with Mocha and Whip

?
1. Take a DarkRoast object How do you "decorate" an
2. Decorate it with a Mocha object object, and how does
3. Decorate it with a Whip object delegation come into this?

4. Call the cost() method and rely on delegation to add on the condiment costs
12
Constructing a drink order with Decorators
1. We start with our DarkRoast object

2. We create a Mocha object and Wrap


it around the DarkRoast

3. We also create a Whip decorator and


wrap Mocha with it

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

14
Okay, here’s what we know so far…
▪ Decorators have the same supertype as ▪ The decorator adds its own behavior either before
the objects they decorate. and/or after delegating to the object it decorates to
do the rest of the job.

▪ You can use one or more decorators to ▪ Objects can be decorated at any time, so we can
wrap an object. decorate objects dynamically at runtime with as
many decorators as we like.

▪ 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.
Now let’s see how this all really works by looking at the
Decorator Pattern definition and writing some code
15
The Decorator Pattern defined

16
Decorating our Beverages

Now, think about how you’d


implement the cost() method of
the coffees and the condiments.

Also think about how you’d


implement getDescription()
method of the condiments. 17
Confusions

• Yes, CondimentDecorator is extending the Beverage class, and this is inheritance


• However, it is vital that decorators have the same type as the objects they decorate
• So, we use inheritance for type matching, not to get behavior
• We add new behavior by composing a decorator with a component
• Not by inheriting from a superclass, but by composing objects together
• We get a whole lot more flexibility about how to mix and match condiments and beverages.
• If we rely on inheritance, then behavior can be determined statically at compile time
• With composition, we can mix and match at runtime

? 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?

18
Writing the Starbuzz code
Let’s start with the Beverage class, actually, it is the same with Starbuzz’s original design.
public abstract class Beverage {
String description = "Unknown Beverage";

public String getDescription() {


return description;
}

public abstract double cost();


}

public abstract class CondimentDecorator extends Beverage { Beverage is simple enough.


public abstract String getDescription(); Let’s implement the abstract
} class for the Condiments
(Decorators) as well.

19
Coding beverages
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}

public double cost() {


return 1.99;
}
}

public class HouseBlend extends Beverage {


public HouseBlend() {
description = "House Blend Coffee"; You can create other two
} Beverage classes
(DarkRoast and Decaf) in
public double cost() {
return .89; exactly the same way.
}
}

20
Coding condiments

public class Mocha extends CondimentDecorator {


Beverage beverage;

public Mocha(Beverage beverage) {


this.beverage = beverage;
}

public String getDescription() {


return beverage.getDescription() + ", Mocha";
}

public double cost() {


return .20 + beverage.cost();
}
}

21
Serving some coffees – test code to make orders
public class StarBuzzCoffee {
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $" +
beverage.cost());
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + " $" +
beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage2.cost());
}
} 22
Real World Decorators: Java I/O
The number of classes in
java.io package is
overwhelming.
You’re not alone if you said
"whoa" when you looked at
this API.
But now that you know the
Decorator Pattern, the I/O
classes should make more
sense since java.io package
is largely based on Decorator.
Here’s an example to read data
from file:

23
Decorating the java.io classes

• Not so different from Starbuzz design, huh?


• Now you can more easily understand java.io API docs.
• Downside of Decorator Pattern is that they often result in a large number of classes.
• But now that you know how Decorator Works, you can keep things in perspective
24
Writing your own Java I/O Decorator
Write a decorator that converts all uppercase characters to lowercase.
public class LowerCaseInputStream extends FilterInputStream {
public LowerCaseInputStream(InputStream in) {
super(in);
}
public int read() throws IOException {
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char)c));
}
public int read(byte[] b, int offset, int len) throws IOException {
int result = super.read(b, offset, len);
for (int i = offset; i < offset+result; i++) {
b[i] = (byte)Character.toLowerCase((char)b[i]);
}
return result;
}
}

25
Test out your new Java I/O Decorator
public class InputTest {
public static void main(String[] args) throws IOException {
int c;
try {
InputStream in =
new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")));
while((c = in.read()) >= 0) {
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

26
Tools for your Design Toolbox
Let’s look at the tools you’ve put in your OO toolbox.
Bullet Points
• Inheritance is one form of extension, but not necessarily the
best way to achieve flexibility in our designs.
• Composition and delegation can often be used to add new
behaviors at runtime.
• The Decorator Pattern provides an alternative to subclassing
for extending behavior.
• Decorator classes mirror the type of the components they
decorate. (In fact, they are the same type as the components
they decorate, either through inheritance or interface
implementation.)
• Decorators change the behavior of their components by
adding new functionality before and/or after (or even in place
of) method calls to the component.
• You can wrap a component with any number of decorators.
• Decorators can result in many small objects in our design,
and overuse can be complex.

27

You might also like