Head First - Design Patterns (2nd Edition)

x Intro Your brain on Design Patterns. Here you are trying to learn something, while here your brain is doing you a favor by making sure the learning ...

2112 downloads 4059 Views 54MB Size

table of contents

Table of Contents (summary) Intro 1

xxv

Welcome to Design Patterns: an introduction

1

2

Keeping your Objects in the know: the Observer Pattern

37

3

Decorating Objects: the Decorator Pattern

79

4

Baking with OO goodness: the Factory Pattern

109

5

One of a Kind Objects: the Singleton Pattern

169

6

Encapsulating Invocation: the Command Pattern

191

7

Being Adaptive: the Adapter and Facade Patterns

235

8

Encapsulating Algorithms: theTemplate Method Pattern

275

9

Well-managed Collections: the Iterator and Composite Patterns

315

10

The State of Things: the State Pattern

385

11

Controlling Object Access: the Proxy Pattern

429

12

Patterns of Patterns: Compound Patterns

499

13

Patterns in the Real World: Better Living with Patterns

577

14

Appendix: Leftover Patterns

611

Table of Contents (the real thing) Intro Your brain on Design Patterns.

Here you are trying to learn something, while

here your brain is doing you a favor by making sure the learning doesn’t stick. Your brain’s thinking, “Better leave room for more important things, like which wild animals to avoid and whether naked snowboarding is a bad idea.” So how do you trick your brain into thinking that your life depends on knowing Design Patterns?

x

Who is this book for?

xxvi

We know what your brain is thinking

xxvii

Metacognition

xxix

Bend your brain into submission

xxxi

Technical reviewers

xxxiv

Acknowledgements

xxxv

1

intro to Design Patterns Welcome to Design Patterns Someone has already solved your problems. In this chapter, you’ll learn why (and how) you can exploit the wisdom and lessons learned by other developers who’ve been down the same design problem road and survived the trip. Before we’re done, we’ll look at the use and benefits of design patterns, look at some key OO design principles, and walk through an example of how one pattern works. The best way to use patterns is to load your brain with them and then recognize places in your designs and existing applications where you can apply them. Instead of code reuse, with patterns you get experience reuse.

Remember, knowing concepts like abstraction, inheritance, and polymorphism do not make you a good object oriented designer. A design guru thinks about how to create flexible designs that are maintainable and that can cope with change.

The SimUDuck app

2

Joe thinks about inheritance...

5

How about an interface?

6

The one constant in software development

8

Separating what changes from what stays the same

10

Designing the Duck Behaviors

11

Testing the Duck code

18

Setting behavior dynamically

20

The Big Picture on encapsulated behaviors

22

HAS-A can be better than IS-A

23

The Strategy Pattern

24

The power of a shared pattern vocabulary

28

How do I use Design Patterns?

29

Tools for your Design Toolbox

32

Exercise Solutions

34

<
AIN

avior

fly beh

ulated

Encaps

Your BR

e>>

vior

FlyBeha

fly()

y FlyNoWa

ings FlyWithW

fly() { - can’t fly! // do nothing

fly() { ts duck flying // implemen ior flyBehav havior; havior quackBe

Client

}

}

ior;

FlyBehav

Encaps

swim() display()

e>>

<
ehavior

QuackB

uack()

performQ

avior

ck beh

qua ulated

QuackBe

quack()

ly()

performF

avior() ) Behavior( .. setQuack methods. duck-like // OTHER

setFlyBeh

MuteQuack Squeak Quack

Decoy Duck

Rubber Duck

Duck

{ display() } a redhead // looks like

int

quack) { ts duck quacking // implemen

{ display() duck } a decoy // looks like

quack() { duckie squeak // rubber

}

}

}

OBSERVER

8

8

Su bje

Duck

{ display() ck } a rubberdu // looks like

ct Obje

ct Dog Obje

8 8 8

Duck Obje

Mo

Automatic update/notification

ct

Cat Object

ec use Obj

t

Observers

oller Contr

jects

Mallard

{ display() } a mallard // looks like

quack() { - can’t quack! // do nothing

Dependen t Ob

Redhead

Object that holds state

ct

A Bunch of Patterns

Duck

est Requ

View

MVC Mode

l

new Your Code,ednowwith and improv erns! design patt

xi

table of contents

2

the Observer Pattern Keeping your Objects in the Know Don’t miss out when something interesting happens! We’ve got a pattern that keeps your objects in the know when something they might care about happens. Objects can even decide at runtime whether they want to be kept informed. The Observer Pattern is one of the most heavily used patterns in the JDK, and it’s incredibly useful. Before we’re done, we’ll also look at one to many relationships and loose coupling (yeah, that’s right, we said coupling). With Observer, you’ll be the life of the Patterns Party.

OO Basics Abstraction n Encapsulatio m is Polymorph ce ies Inheriten

s OO Principle

what var Encapsulate herition over in si o p m o C r Favo tance not Interfaces, o t m a r g o r P ions implementat led loosely coup Strive for ween objects that designs bet interact

The Weather Monitoring application

39

Meet the Observer Pattern

44

Publishers + Subscribers = Observer Pattern

45

Five minute drama: a subject for observation

48

The Observer Pattern defined

51

The power of Loose Coupling

53

Designing the Weather Station

56

Implementing the Weather Station

57

Using Java’s built-in Observer Pattern

64

The dark side of java.util.Observable

71

Tools for your Design Toolbox

74

Exercise Solutions

78

ONE TO MANY RELATIONSHIP

Object that holds state

int

ct Obje

ct

Su bje

ct Dog Obje

8 8 8

Duck Obje

Automatic update/notification

xii

Mo

ct

Cat Object

ec use Obj

t

Observers

Dependent Objects

8

8

3

the Decorator Pattern Decorating Objects 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.

I used to think real men subclassed everything. That was until I learned the power of extension at runtime, rather than at compile time. Now look at me!

Welcome to Starbuzz Coffee

80

The Open-Closed Principle

86

Meet the Decorator Pattern

88

Constructing a Drink Order with Decorators

89

The Decorator Pattern Defined

91

Decorating our Beverages

92

Writing the Starbuzz code

95

Real World Decorators: Java I/O

100

Writing your own Java I/O Decorator

102

Tools for your Design Toolbox

105

Exercise Solutions

106

xiii

table of contents

4

the Factory Pattern Baking with OO Goodness Get ready to cook some loosely coupled OO designs. There is more to making objects than just using the new operator. You’ll learn that instantiation is an activity that shouldn’t always be done in public and can often lead to coupling problems. And you don’t want that, do you? Find out how Factory Patterns can help save you from embarrasing dependencies.

The clients of the Abstract Factory are the two instances of our PizzaStore, NYPizzaStore and ChicagoStylePizzaSore. NYPizzaStore createPizza()

The abstract PizzaIngredientFactory is the interface that defines how to make a family of related products - everything we need to make a pizza.

<> Dough

ThickCrustDough

ThinCrustDough

<> PizzaIngredientFactory

When you see “new”, think “concrete”

110

Objectville Pizza

112

Encapsulating object creation

114

Building a simple pizza factory

115

The Simple Factory defined

117

A Framework for the pizza store

120

Allowing the subclasses to decide

121

Let’s make a PizzaStore

123

Declaring a factory method

125

Meet the Factory Method Pattern

131

Parallel class hierarchies

132

Factory Method Pattern defined

134

A very dependent PizzaStore

137

Looking at object dependencies

138

The Dependency Inversion Principle

139

Meanwhile, back at the PizzaStore...

144

Families of ingredients...

145

Building our ingredient factories

146

Looking at the Abstract Factory

153

Behind the scenes

154

Abstract Factory Pattern defined

156

Factory Method and Abstract Factory compared

160

Tools for your Design Toolbox

162

Exercise Solutions

164

createDough()

<> Sauce

createSauce() createCheese() createVeggies() createPepperoni()

PlumTomatoSauce

createClam()

NYPizzaIngredientFactory

ChicagoPizzaIngredientFactory

createDough()

createDough()

createSauce()

createSauce()

createCheese()

createCheese()

createVeggies()

createVeggies()

createPepperoni()

createPepperoni()

createClam()

createClam()

MarinaraSauce

<> Cheese

Mozzarella Cheese

ReggianoCheese

<> Clams

The job of the concrete pizza factories is to make pizza ingredients. Each factory knows how to create the right objects for their region.

xiv

FrozenClams

Each factory produces a different implementation for the family of products.

FreshClams

5

the Singleton Pattern One of a Kind Objects The Singleton Pattern: your ticket to creating one-of-akind objects, for which there is only one instance. You might be happy to know that of all patterns, the Singleton is the simplest in terms of its class diagram; in fact the diagram holds just a single class! But don’t get too comfortable; despite its simplicity from a class design perspective, we’ll encounter quite a few bumps and potholes in its implementation. So buckle up—this one’s not as simple as it seems...

One and only one object

170

The Little Singleton

171

Dissecting the classic Singleton Pattern

173

Confessions of a Singleton

174

The Chocolate Factory

175

Singleton Pattern defined

177

Houston, we have a problem...

178

BE the JVM

179

Dealing with multithreading

180

Singleton Q&A

184

Tools for your Design Toolbox

186

Exercise Solutions

188

Hershey, PA

OO Pattenersnasinfaesmilya onofe-algotori-thmmnaans,r-ly

ef ak itiniote dso ad th vidi-ine eanan actjeesohct roat seyrov-ereaderachfit-beoondrtw mob StrOatbeg - Pth Adntt ysctdem r e,-an rysof eeF ef c a d-myn it th es c ri aliliDvalam t cy e at go c je o e, ul en al a D es h ob e at t e th st an am encadpsepenAdbstrra but s f M ts to le ge y ng s r an gy ie ti ibed o ch te x ea t lit ob le c bi cr f ct a an si . St je asitjeleshoctonut, lyg inha-s t a it F or on at . cl ng e e f ob d le sp tidurjeup reabn inonteerface at ectas wte atea pr th anusnsob ssed chanwge he Eid intoal poin fnttioor iecr -dov ieor f s e nd cl n t as ac or cl en om no t ec ob rf ex e fr gl le D ch te y g ar or ep hi a in . tl f d wovclidaseses. a class tsSdinor sssuesbcladec llyenreden geprte canden ssnciniddre te indedpeep laat bcto coan stan inlath eirceac leictifon Method lets . alivyisulyeeng ry atec to . F aualtotemsprn it e. to y. at lit ss ti anof acce ation to the subclasses iona functst ti defer instan

xv

table of contents

6

the Command Pattern Encapsulating Invocation In this chapter we take encapsulation to a whole new level: we’re going to encapsulate method invocation. That’s right, by encapsulating invocation we can crystallize pieces of computation so that the object invoking the computation doesn’t need to worry about how to do things; it just uses our crystallized method to get it done. We can also do some wickedly smart things with these encapsulated method invocations, like save them away for logging or reuse them to implement undo in our code.

er sists of an ord The Order con omer’s menu slip and the custwritten on it. are t tha s item crea

eese with Ch Burger Shake Malt

I’ll have a Burger with Cheese and a Malt Shake

teO

rde

r()

ere rt H Sta takeO

rder(

The customer knows what he wants and creates an order.

)

The Waitress takes the Order, gets around to it, she calls and when she orderUp() method to begin the Order’sitsprepar ation.

or

de

rU

p(

)

The Short Order Cook follows the instructions of the Order and produces the meal.

se with Chee Burger Shake Malt

makeBurger(), makeShake()

192

The Remote Control

193

Taking a look at the vendor classes

194

Meanwhile, back at the Diner...

197

Let’s study the Diner interaction

198

The Objectville Diner Roles and Responsibilities

199

From the Diner to the Command Pattern

201

Our first command object

203

The Command Pattern defined

206

The Command Pattern and the Remote Control

208

Implementing the Remote Control

210

Putting the Remote Control through its paces

212

Time to write that documentation

215

Using state to implement Undo

220

Every remote needs a Party Mode!

224

Using a Macro Command

225

More uses of the Command Pattern: Queuing requests

228

More uses of the Command Pattern: Logging requests

229

Tools for your Design Toolbox

230

Exercise Solutions

232

ou

tp

ut

s all rder ha The O structions e the in to prepar neededeal. The e the m directs th k Order Order Cooe Short methods lik with urger(). makeB

Home Automation or Bust

xvi

7

the Adapter and Facade Patterns Being Adaptive In this chapter we’re going to attempt such impossible feats as putting a square peg in a round hole. Sound impossible? Not when we have Design Patterns. Remember the Decorator Pattern? We wrapped objects to give them new responsibilities. Now we’re going to wrap some objects with a different purpose: to make their interfaces look like something they’re not. Why would we do that? So we can adapt a design expecting one interface to a class that implements a different interface. That’s not all, while we’re at it we’re going to look at another pattern that wraps objects to simplify their interface.

European Wall Outlet

AC Power Adapter

Standard AC Plug

Adapters all around us

236

Object Oriented Adapters

237

The Adapter Pattern explained

241

Adapter Pattern defined

243

Object and Class Adapters

244

Tonight’s talk: The Object Adapter and Class Adapter

247

Real World Adapters

248

Adapting an Enumeration to an Iterator

249

Tonight’s talk: The Decorator Pattern and the Adapter Pattern

252

Home Sweet Home Theater

255

Lights, Camera, Facade!

258

Constructing your Home Theater Facade

261

Facade Pattern defined

264

The Principle of Least Knowledge

265

Tools for your Design Toolbox

270

Exercise Solutions

272

Client

nted The Client is impleme against the target interface

reques

t()

tra

ted nsla

qu Re

)

Adap tee

est(

Adap ter adaptee interface

ace terf et in targ

nts the The Adapter impleme an target interface and holds instance of the Adaptee

Turkey was the ce adaptee interfa

xvii

table of contents

the Template Method Pattern

8

Encapsulating Algorithms We’ve encapsulated object creation, method invocation, complex interfaces, ducks, pizzas... what could be next? We’re going to get down to encapsulating pieces of algorithms so that subclasses can hook themselves right into a computation anytime they want. We’re even going to learn about a design principle inspired by Hollywood.

We’ve recognized that the two recipes are essentially the same, although some of the steps require different implementations. So we’ve generalized the recipe and placed it in the base class.

Te a Boil some wat

1

2

Ste ep

er

C o f fe e 1

the water the tea bag in

3

Pou r tea in a

4

Add lemon

2 3

cup

4

Bo il some Brew th e Po ur co ff

Add suga r

wate r

co ffee gr

ee in a cu

inds

p

and mi lk

Caffeine Beverage generalize

1 2

Brew

relies on

3

Pour beverage in a cup

4

Add condiments

subclass for

ss

Tea subcla

some steps

2

Steep the teabag in the

4

Add lemon

xviii

water

Boil some water

knows Caffeine Beveragesteps of and controls theperforms the recipe, and f, but itsel 3 steps 1 and fee relies on Tea orandCof4. to do steps 2

Whipping up some coffee and tea classes

277

Abstracting Coffee and Tea

280

Taking the design further

281

Abstracting prepareRecipe()

282

What have we done?

285

Meet the Template Method

286

Let’s make some tea

287

What did the Template Method get us?

288

Template Method Pattern defined

289

Code up close

290

Hooked on Template Method...

292

Using the hook

293

Coffee? Tea? Nah, let’s run the TestDrive

294

The Hollywood Principle

296

The Hollywood Principle and the Template Method

297

Template Methods in the Wild

299

Sorting with Template Method

300

We’ve got some ducks to sort

301

Comparing ducks and ducks

302

The making of the sorting duck machine

304

Swingin’ with Frames

306

Applets

307

Tonight’s talk: Template Method and Strategy

308

Tools for your Design Toolbox

311

Exercise Solutions

312

generalize

relies on subclass for some steps

Coffee subclass

fee gri nds

2

Bre w the cof

4

Add sug ar and

mil k

the Iterator and Composite Patterns

9

Well-Managed Collections There are lots of ways to stuff objects into a collection. Put them in an Array, a Stack, a List, a Map, take your pick. Each has its own advantages and tradeoffs. But when your client wants to iterate over your objects, are you going to show him your implementation? We certainly hope not! That just wouldn’t be professional. Don’t worry—in this chapter you’ll see how you can let your clients iterate through your objects without ever seeing how you store your objects. You’re also going to learn how to create some super collections of objects that can leap over some impressive data structures in a single bound. You’re also going to learn a thing or two about object responsibility.

nu

All Menus Pa

eM e

nc ake us Ho

Di ner nu Me

1

Ca feMenu

2

3

Café Menu

Pancake Menu

Diner Menu Me nuItem

Me nuItem

1

2

Me nuItem

Men

3

uItem

4

k ey

Array

1

k ey

Me nuItem

k ey

Me nuItem

Me nuItem

2

Me nuItem

Me nuItem

ArrayList

Dessert Menu

k ey

3 Me nuItem

4

1 Me nuItem

2 Me nuItem

3 Me nuItem

4 Men

Men

uItem

Men

uItem

Objectville Diner and Pancake House merge

316

Comparing Menu implementations

318

Can we encapsulate the iteration?

323

Meet the Iterator Pattern

325

Adding an Iterator to DinerMenu

326

Looking at the design

331

Cleaning things up with java.util.Iterator

333

What does this get us?

335

Iterator Pattern defined

336

Single Responsibility

339

Iterators and Collections

348

Iterators and Collections in Java 5

349

Just when we thought it was safe...

353

The Composite Pattern defined

356

Designing Menus with Composite

359

Implementing the Composite Menu

362

Flashback to Iterator

368

The Null Iterator

372

The magic of Iterator & Composite together...

374

Tools for your Design Toolbox

380

Exercise Solutions

381

uItem

xix

table of contents

the State Pattern

10

The State of Things A little known fact: the Strategy and State Patterns were twins separated at birth. As you know, the Strategy Pattern went on to create a wildly successful business around interchangeable algorithms. State, however, took the perhaps more noble path of helping objects learn to control their behavior by changing their internal state. He’s often overheard telling his object clients, “just repeat after me, I’m good enough, I’m smart enough, and doggonit...”

Where the Gumball Machine is Never Half Empty

gumbal

ls = 0

xx

ball

s>

ns

cra

arter

inserts qu

No r Quarte gum

tur

Has r Quarte

arter

Out obfalls Gum

0

ejects qu

Mighty Gumball, Inc.

controller needs to the gumball machine for us! We Here’s the way we thinkcan implement this in Java work. We’re hoping you e, so you need to keep futur the in ior behav more g may be addin le! possib as e ainabl maint and le the design as flexib - Mighty Gumball Engineers

dispenslle gumba

ll Gumba Sold

nk

How do we implement state?

387

State Machines 101

388

A first attempt at a state machine

390

You knew it was coming... a change request!

394

The messy STATE of things...

396

Defining the State interfaces and classes

399

Implementing our State Classes

401

Reworking the Gumball Machine

402

The State Pattern defined

410

State versus Strategy

411

State sanity check

417

We almost forgot!

420

Tools for your Design Toolbox

423

Exercise Solutions

424

11

the Proxy Pattern Controlling Object Access Ever play good cop, bad cop? You’re the good cop and you provide all your services in a nice and friendly manner, but you don’t want everyone asking you for services, so you have the bad cop control access to you. That’s what proxies do: control and manage access. As you’re going to see there are lots of ways in which proxies stand in for the objects they proxy. Proxies have been known to haul entire method calls over the Internet for their proxied objects; they’ve also been known to patiently stand in the place for some pretty lazy objects.

Not Hot

<>

Monitoring the gumball machines

430

The role of the ‘remote proxy’

434

RMI detour

437

GumballMachine remote proxy

450

Remote proxy behind the scenes

458

The Proxy Pattern defined

460

Get Ready for virtual proxy

462

Designing the CD cover virtual proxy

464

Virtual proxy behind the scenes

470

Using the Java API’s proxy

474

Five minute drama: protecting subjects

478

Creating a dynamic proxy

479

The Proxy Zoo

488

Tools for your Design Toolbox

491

Exercise Solutions

492

<> InvocationHandler

Subject

request()

invoke()

now consists The proxy sse s. cla o of tw Proxy

RealSubject request()

request()

InvocationHandler invoke()

xxi

table of contents

12

Compound Patterns Patterns of Patterns Who would have ever guessed that Patterns could work together? You’ve already witnessed the acrimonious Fireside Chats (and be thankful you didn’t have to see the Pattern Death Match pages that the publisher forced us to remove from the book so we could avoid having to use a Parent’s Advisory warning label), so who would have thought patterns can actually get along well together? Believe it or not, some of the most powerful OO designs use several patterns together. Get ready to take your pattern skills to the next level; it’s time for Compound Patterns. Just be careful—your co-workers might kill you if you’re struck with Pattern Fever.

The beat is set at 119 BPM and you would like to increase it to 120.

You click on the increase beat button. View

Which results in the controller being invoked.

Controller You see the beatbar pulse every 1/2 second.

Because the BPM is 120, the view gets a beat notification every 1/2 second.

View

The controller asks the model to update its BPM by one.

atMod Be on() el setBPM() off()

getBPM

The view is updated to 120 BPM.

xxii

BPM View is notified that theM() on changed. It calls getBP the model state.

()

Compound Patterns

500

Duck reunion

501

Adding an adapter

504

Adding a decorator

506

Adding a factory

508

Adding a composite, and iterator

513

Adding an observer

516

Patterns summary

523

A duck’s eye view: the class diagram

524

Model-View-Controller, the song

526

Design Patterns are your key to the MVC

528

Looking at MVC through patterns-colored glasses

532

Using MVC to control the beat...

534

The Model

537

The View

539

The Controller

542

Exploring strategy

545

Adapting the model

546

Now we’re ready for a HeartController

547

MVC and the Web

549

Design Patterns and Model 2

557

Tools for your Design Toolbox

560

Exercise Solutions

561

13

Better Living with Patterns Patterns in the Real World Ahhhh, now you’re ready for a bright new world filled with Design Patterns. But, before you go opening all those new doors of opportunity we need to cover a few details that you’ll encounter out in the real world—things get a little more complex out there than they are here in Objectville. Come along, we’ve got a nice guide to help you through the transition...

ide to The Objectville Gu ns h Design Patter Better Living wit patterns in the real

g with & tricks for livin handy guide of tips Please accept ourguide you will: world. In this the definition of a onceptions about misc mon com too all b Learn the tern.” just have to “Design Pat s alog and why you ign Pattern Cat r those nifty Des b Discove . get one. at the wrong time tern Pat ign using a Des embarrassment of b Avoid the e they belong. wher tions ifica in class to keep patterns quick b Learn how the gurus; read our erns isn’t just for discovering pattpatt r too. b See that me a erns write beco and wTo is revealed. Ho s Gang of Four eriou myst the of tify when the true iden patterns user b Be there e table books any coffe the – s with the neighbor b Keep up must own. a Zen master. Patterns mind like train your Design b Learn to patterns rs by improving your lope deve ence ds and influ b Win frien vocabulary.

Richa

Your Objectville guide

578

Design Pattern defined

579

Looking more closely at the Design Pattern definition

581

May the force be with you

582

Pattern catalogs

583

How to create patterns

586

So you wanna be a Design Patterns writer?

587

Organizing Design Patterns

589

Thinking in patterns

594

Your mind on patterns

597

Don’t forget the power of the shared vocabulary

599

Top five ways to share your vocabulary

600

Cruisin’ Objectville with the Gang of Four

601

Your journey has just begun...

602

Other Design Pattern resources

603

The Patterns Zoo

604

Annihilating evil with Anti-Patterns

606

Tools for your Design Toolbox

608

Leaving Objectville...

609

lm

rd He

Ralph Johnson

Gang of Four

John Vlissides

Erich Gamma

xxiii

table of contents

14

Appendix: Leftover Patterns Not everyone can be the most popular. A lot has changed in the last 10 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 always 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.

The Client asks the Visitor to get information from the Composite structure... New methods can be added to the Visitor without affecting the Composite.

g() tin Ra lth () ea ries ) H t o ( l ge tCa tein ge tPro bs() ge tCar ge

All these composite classes have to do is add a getState() method not worry about (and call to able be to needs The Visitor s, and this is exposing themselves : ). getState() across classemetho ds for where you can add new the client to use. te() getSta

Visitor

to The Traverser knows how gh guide the Visitor throu the Composite structure.

i xxiv

Index

MenuItem

e() tat tS ge

Client / Traverser

getState() getS tate () ge tS ta MenuItem te ()

Menu

Ingredient

Ingredient

Bridge

612

Builder

614

Chain of Responsibility

616

Flyweight

618

Interpreter

620

Mediator

622

Memento

624

Prototype

626

Visitor

628

631

3 the DecoratorPattern

g Decorating

h

Objects g

I used to think real men subclassed everything. That was until I learned the power of extension at runtime, rather than at compile time. Now look at me!

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.

this is a new chapter

79

the starbuzz story

Welcome to Starbuzz Coffee 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...

stract class, Beverage is an abbe verages subclassed by all ff ee shop. offered in the co

The description instance variable is set in each subclass and holds a description of the beverage, like “Most Excellent Dark Roast”. The getDescription() method returns the description.

Beverage description

The cost() method is abstract; subclassses need to define their own implementation.

getDescription() cost() // Other useful methods...

DarkRoast

HouseBlend cost()

cost()

Decaf cost()

Espresso cost()

Each subclass implements cost() to return the cost of the beverage.

80

Chapter 3

the decorator pattern

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... Beverage description getDescription() cost() // Other useful methods...

DarkRoastWithSteamedMilk andMocha

HouseBlendWithSteamedMilk andMocha HouseBlendWithSteamedMilk cost() andCaramel HouseBlendWithWhipandMocha

cost()

cost()

cost()

DecafWithSteamedMilk andCaramel

DarkRoastWithSteamedMilk andCaramel

cost()

HouseBlendWithMocha cost() HouseBlendWithSoyandMocha

EspressoWithSteamedMilk andMocha

DecafWithSteamedMilk andMocha

cost() DarkRoastWithWhipandMocha

cost()

EspressoWithSteamedMilk andCaramel

EspressoWithWhipandMocha

cost()

DecafWithWhipandMocha cost()

cost()

HouseBlendWithSteamedMilk andSoycost() cost()

HouseBlendWithWhip

cost()

cost()

HouseBlendWithSoy cost()

DarkRoastWithSteamedMilk andSoy

HouseBlendWithSteamedMilk cost() cost()

cost() DarkRoastWithSteamedMilk

cost() HouseBlendWithSteamedMilk andWhip

cost() HouseBlendWithWhipandSoy

cost()

EspressoWithSteamedMilk DecafWithSoy andSoy DecafWithSteamedMilk cost() andSoy EspressoWithSteamedMilk cost()

DarkRoastWithSoy cost() DecafWithSteamedMilk cost() DecafWithSoyandMocha DarkRoastWithSoy DarkRoastWithSoyandMocha cost() DecafWithSoy cost() DecafWithSoyandMocha cost() EspressoWhip cost() cost()

cost()

DecafWithWhip cost()

DarkRoastWithWhip

cost()

cost()

EspressoWithSteamedMilk andWhip DecafWithSteamedMilk DarkRoastWithSteamedMilk cost() andWhip andWhip EspressoWithWhipandSoy DecafWithWhipandSoy cost() DarkRoastWithWhipandSoy cost() cost()

cost()

EspressoWithMocha

cost() DecafWithMocha

cost()

DarkRoastWithMocha

cost() cost()

Whoa! Can you say “class explosion?”

cost()

mputes the Each cost method coalo ng with the cost of the coffee the order. other condiments in

you are here 4

81

violating design principles

A

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! This is stupid; why do we need all these classes? Can’t we just use instance variables and inheritance in the superclass to keep track of the condiments?

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

Beverage description milk soy mocha whip getDescription() cost() hasMilk() setMilk() hasSoy() setSoy() hasMocha() setMocha() hasWhip() setWhip() // Other useful methods..

82

Chapter 3

New boolean values for each condiment. Now we’ll implement cost() in Beverage (instead of keeping it abstract), so that it can calculate the costs associated with the condiments for a particular beverage instance. Subclasses will still override cost(), but they will also invoke the super version so that they can calculate the total cost of the basic beverage plus the costs of the added condiments.

e boolean These get and setndthiments. values for the co

the decorator pattern Beverage

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

e st() will calculate th The superclass coth ile wh , condiments costs for all of ste() in the subclasses the overridden cofunctionality to will extend that that specific include costs for beverage type. needs to compute Each cost() methobedverage and then the cost of the ents by calling the add in the condim tation of cost(). superclass implemen

description milk soy mocha whip getDescription() cost() hasMilk() setMilk() hasSoy() setSoy() hasMocha() setMocha() hasWhip() setWhip() // Other useful methods..

DarkRoast

HouseBlend

Decaf

cost()

cost()

Sharpen your pencil

cost()

Espresso cost()

Write the cost( ) methods for the following classes (pseudo-Java is okay):

public class Beverage { public double cost() {

public class DarkRoast extends Beverage { public DarkRoast() { description = “Most Excellent Dark Roast”; } public double cost() {

}

}

}

}

you are here 4

83

impact of change

See, five classes total. This is definitely the way to go.

I’m not so sure; I can see some potential problems with this approach by thinking about how the design might need to change in the future.

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 (iced tea?), the condiments may not be appropriate, yet the Tea subclass will still inherit methods like hasWhip(). What if a customer wants a double mocha?

rn:

Your tu

84

Chapter 3

aw in this is s e As w pter 1, idea! Cha ery bad av

the decorator pattern

Master and Student... 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.

you are here 4

85

the open-closed principle

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.

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

86

Chapter 3

the decorator pattern

there are no

Dumb Questions

Q:

Open for extension and closed for modification? That sounds very contradictory. How can a design be both?

A:

That’s a very good question. It certainly sounds contradictory at first. After all, the less modifiable something is, the harder it is to extend, right? As it turns out, though, there are some clever OO techniques for allowing systems to be extended, even if we can’t change the underlying code. Think about the Observer Pattern (in Chapter 2)... 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 OO design techniques.

Q:

Okay, I understand Observable, but how do I generally design something to be extensible, yet closed for modification?

A:

Usually, you can’t. Making OO design flexible and open to extension without the modification of existing code takes time and effort. In general, we don’t have the luxury of tying down every part of our designs (and it would probably be wastefu). Following the Open-Closed Principle usually 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.

Q:

How do I know which areas of change are more important?

A:

That is partly a matter of experience in designing OO systems and also a matter of the knowing the domain you are working in. Looking at other examples will help you learn to identify areas of change in your own designs.

A:

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

Q:

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

Many of the patterns give us time tested designs that protect your code from being modified by supplying a means of extension. In this chapter you’ll see a good example of using the Decorator pattern to follow the OpenClosed principle.

How can I make every part of my design follow the Open-Closed Principle?

you are here 4

87

meet the decorator pattern

Meet the Decorator Pattern

Okay, enough of the “Object Oriented Design Club.” We have real problems here! Remember us? Starbuzz Coffee? Do you think you could use some of those design principles to actually help us?

Okay, we’ve seen that representing our beverage plus condiment pricing scheme with inheritance has not worked out very well – we get 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: 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:

1

Take a DarkRoast object

2

Decorate it with a Mocha object

3

Decorate it with a Whip object

4

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

88

Chapter 3

the decorator pattern

Constructing a drink order with Decorators 1

We start with our DarkRoast object.

cost()

DarkRoast

2

t DarkRoaasnd has t a h t r e b Remem from Beverage mputes inherits method that co a cost() of the drink. the cost

The customer wants Mocha, so we create a Mocha object and wrap it around the DarkRoast.

or. Its ject is a decorisatdecorating, The Mocha obth object it type mirrors Beeverage. (By “mirror”, in this case, a the same type..) we mean it is

cost()

cost()

DarkRoast

Mocha

3

oo, st() method tn treat co a s a h a ch o e ca So, M olymorphism w and through p wrapped in Mocha as any Beveragetoo (because Mocha is a a Beverage, Beverage). subtype of

The customer also wants Whip, so we create a Whip decorator and wrap Mocha with it.

cost()

cost()

Whip

cost()

DarkRoast

Mocha

Whip is a decorator, so it also mirrors DarkRoast’s type and includes a cost() method.

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. you are here 4

89

decorator characteristics

4

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.

in (You’ll see how ) s. ge a few pa

Mocha. 2 Whip calls cost() on t() on the 1 First, we call cos , Whip. tor ora dec st mo out

$1.29

.10

3

cost()

.20

cost()

Whip 5

Whip adds its total, 10 cents, to the result from Mocha, and returns the final result—$1.29.

5

Mocha calls cost() on DarkRoast.

cost()

t .99 D ar k Ro as

Mocha

4

Dar kRoast returns its cost, 99 cents.

Mocha adds its cost, 20 cents, to the result from Dar kRoast, and returns the new total, $1.19.

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.

ß 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. 90

Chapter 3

t Key Poin

!

the decorator pattern

The Decorator Pattern defined Let’s first take a look at the Decorator Pattern description: 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).

Each component can be used on its own, or wrapped by a decorator.

Component

component

methodA() methodB()

The ConcreteComponent is the object we’re going to dynamically add new behavior to. It extends Component.

// other methods

Decorator

ConcreteComponent methodA()

methodA()

methodB()

methodB()

// other methods

// other methods

ConcereteDecoratorA Component wrappedObj

s an The ConcreteDecoratorehathing th r fo ble instance varia t it decorates (the Componen s). ap wr r the Decorato

methodA() methodB() newBehavior() // other methods

Each decorator HAS-A (wraps) a component, which means the decorator has an instance variable that holds a reference to a component.

ent the Decorators implem abstract same interfacemorponent they class as the co corate. are going to de ConcereteDecoratorB Component wrappedObj Object newState

methodA() methodB() // other methods

e Decorators can extendt.th en on mp state of the co

Decorators can add new methods; however, new behavior is typically added by doing computation before or after an existing method in the component. you are here 4

91

decorating beverages

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

r Beverage acts asneou class. nt po m abstract co

component

Beverage description

getDescription() cost() // other useful methods

CondimentDecorator

DarkRoast

HouseBlend cost()

cost()

getDescription()

Decaf

Espresso cost()

cost()

e concretper r u o f e e Th nents, on compo e type. coffe

Milk

Whip

Soy

Mocha

Beverage beverage

Beverage beverage

Beverage beverage

Beverage beverage

cost()

cost()

cost()

cost()

getDescription()

getDescription()

getDescription()

getDescription()

And here are our condiment decorators; notice they need to implement not only cost() but also getDescription(). We’ll see why in a moment...

A

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.

92

Chapter 3

the decorator pattern

Cubicle Conversation Some confusion over Inheritance versus Composition

ry

Ma

Okay, I’m a little confused...I thought we weren’t going to use inheritance in this pattern, but rather we were going to rely on composition instead.

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. you are here 4

93

decorator training

New barista training

Okay, I need for you to make me a double mocha, soy latte with whip.

Make a picture for what happens when the order is for a “double mocha soy lotte 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): Mocha. 2 Whip calls cost() on

3

) on the 1 First, we call cost( outmost decorator, Whip.

$1.29

.10

cost()

.20

cost()

Whip 5

Whip adds its total, 10 cents, to the result from Mocha, and returns the final result—$1.29.

5

Mocha calls cost() on DarkRoast.

cost()

t .99 D ar k Ro as

Mocha

4

was for This picturoeast mocha a “dark r rage. whip” beve

DarkRoast returns its cost, 99 cents.

Mocha adds its cost, 20 cents, to the result from DarkRoast, and returns the new total, $1.19.

Sharpen your pencil

Draw your picture here.

Starbuzz Coffee

Coffees House Blend .89 Dark Roast .99 Decaf 1.05 Espresso 1.99 Condiments Steamed Milk .10 Mocha .20 Soy .15 Whip .10

bu tar zz

u tarb zz

ffee S Co

fe Cof e S

le “doub ke a whip” a m n h ca oy, : you te wit nd, S ! HINTha soy lat HouseBle and Whip moc ombining Mocha by c shots of two

94

Chapter 3

the decorator pattern

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:

public abstract class Beverage { String description = “Unknown Beverage”; public String getDescription() { return description; } public abstract double cost(); }

abstract Beverage is an two methods class with the n() and cost(). getDescriptio

getDescription is already implemented for us, but we need to implement cost() in the subclasses.

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

to be First, we needle with a Beverage, interchangeab the Beverage class. so we extend public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); }

We’re also going to require that the condiment decorators all reimplement the getDescription() method. Again, we’ll see why in a sec...

you are here 4

95

implementing the beverages

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.

public class Espresso extends Beverage { public Espresso() { description = “Espresso”; } public double cost() { return 1.99; } }

public double cost() { return .89; }

Okay, here’s another Beverage. All we do is set the appropriate description, “House Blend Coffee,” and then return the correct cost: 89¢. You can create the other two Beverage classses (DarkRoast and Decaf) in exactly the same way.

96

Chapter 3

we To take care of the description,the for or uct str con set this in the instance class. Remember the description ge. variable is inherited from Bevera

’t spresso. We djuonst E an of st co e to compute th this class, we Finally, we needabout adding in condiments in.99. need to worry the price of an Espresso: $1 need to return

public class HouseBlend extends Beverage { public HouseBlend() { description = “House Blend Coffee”; }

}

d the Beverage First we exthiens is a beverage. class, since t

Coffee Starbuzz s Coffee Blend House oast R Dark Decaf so Espres

.89 .99 1.05 1.99

ents Condim d Milk Steame Mocha Soy Whip

.10 .20 .15 .10

the decorator pattern

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:

tor tDecora n e im d n o Mocha is a decorator, so we e Mocha with at ti an mber, Cverage. st e in m to e g in R We’re go extend CondimentDecorator. Be Beverage using: extends a reference to a e variable to hold th (1) An instancewrapping. public class Mocha extends CondimentDecorator { beverage we are Beverage beverage; t this instance (2) A way to seject we are wrapping. public Mocha(Beverage beverage) { variable to theinob pass the beverage this.beverage = beverage; ere, we’re go g tothe decorator’s H } we’re wrapping to constructor. public String getDescription() { return beverage.getDescription() + “, Mocha”; } public double cost() { return .20 + beverage.cost(); } }

beverage ute the cost of oullr to mp co to ed e ne we w No delegate the ca mpth we t, rs Fi . ha e the oc ut M th co wi ng, so that it can to the result. ti ra co de ’re we ct je ob e cost of Mocha cost; then, we add th

We want our description to not only include the beverage - say “Dark Roast” - but also to include each item decorating the beverage, for instance, “Dark Roast, Mocha”. So we first delegate to the object we are decorating to get its description, then append “, Mocha” to that description.

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

Write and compile the code for the other Soy and Whip condiments. You’ll need them to finish and test the application.

you are here 4

97

testing the beverages

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: public class StarbuzzCoffee { public static void main(String args[]) { Beverage beverage = new Espresso(); System.out.println(beverage.getDescription() + “ $” + beverage.cost());

s condimentt. o n , o s s e nd cos an espr Order upt its description a and prin

oast object. Make a DarkR Beverage beverage2 = new DarkRoast(); Wrap it with a Mocha. beverage2 = new Mocha(beverage2); Wrap it in a second Mocha. beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); Wrap it in a Whip. 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() + “ $” + beverage3.cost());

Finally, give us a HouseBlend with Soy, Mocha, and Whip.

} }

Now, let’s get those orders in:

We’re going to see a much better way of creating decorated objects when we cover the Factory and Builder Design Patterns.

File Edit Window Help CloudsInMyCoffee

% java StarbuzzCoffee Espresso $1.99 Dark Roast Coffee, Mocha, Mocha, Whip $1.49 File Edit Window Help CloudsInMyCoffee House Blend Coffee, Soy, Mocha, Whip $1.34 %

98

Chapter 3

the decorator pattern

there are no

Dumb Questions

Q:

I’m a little worried about code that might test for a specfic concrete component – say, HouseBlend – and do something, like issue a discount. Once I’ve wrapped the HouseBlend with decorators, this isn’t going to work anymore.

A:

That is exactly right. If you have code that relies on the concrete component’s type, decorators will break that code. As long as you only write code against the abstract component type, the use of decorators will remain transparent to your code. However, once you start writing code against concrete components, you’ll want to rethink your application design and your use of decorators.

Q:

Wouldn’t it be easy for some client of a beverage to end up with a decorator that isn’t the outermost decorator? Like if I had a DarkRoast with Mocha, Soy, and Whip, it would be easy to write code that somehow ended up with a reference to Soy instead of Whip, which means it would not including Whip in the order.

A:

You could certainly argue that you have to manage more objects with the Decorator Pattern and so there is an increased chance that coding errors will introduce the kinds of problems you suggest. However, decorators are typically created by using other patterns like Factory and Builder. Once we’ve covered these patterns, you’ll see that the creation of the concrete component with its decorator is “well encapsulated” and doesn’t lead to these kinds of problems.

Q:

Can decorators know about the other decorations in the chain? Say, I wanted my getDecription() method to print “Whip, Double Mocha” instead of “Mocha, Whip, Mocha”? That would require that my outermost decorator know all the decorators it is wrapping.

A:

Decorators are meant to add behavior to the object they wrap. When you need to peek at multiple layers into the decorator chain, you are starting to push the decorator beyond its true intent. Nevertheless, such things are possible. Imagine a CondimentPrettyPrint decorator that parses the final decription and can print “Mocha, Whip, Mocha” as “Whip, Double Mocha.” Note that getDecription() could return an ArrayList of descriptions to make this easier.

Sharpen your pencil Our friends at Starbuzz have introduced sizes to their menu. You can now order a coffee in tall, grande, and venti sizes (translation: small, medium, and large). Starbuzz saw this as an intrinsic part of the coffee class, so they’ve added two methods to the Beverage class: setSize() and getSize(). They’d also like for the condiments to be charged according to size, so for instance, Soy costs 10¢, 15¢ and 20¢ respectively for tall, grande, and venti coffees. How would you alter the decorator classes to handle this change in requirements?

you are here 4

99

decorators in java i/o

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:

A text file for reading.

ered

e InputStr

a

ff

m

ne Nu mberInputStrea

LineNumberInputStream is also a concrete decorator. It adds the ability to count the line numbers as it reads data.

m

Li

Bu

FileInputStream

BufferedInputStream is a concrete decorator. BufferedInputStream adds behavior in two ways: it buffers input to improve performance, and also augments the interface with a new method readLine() for reading character-based input, a line at a time.

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

100

Chapter 3

hat’s component tary e h t is m a e r FileInputStr ted. The Java I/O lib g a in r d o c lu e c nts, in being d ral compone BufferInputStream, e v se s e li p p su others. eam, String FileInputStr putStream and a fewent from ByteArrayIn give us a base compon All of thesead bytes. which to re

the decorator pattern

Decorating the java.io classes ent. ract compon st b a r u o ’s Here

FilterInputStream is an abstract decorator.

InputStream

FileInputStream

StringBufferInputStream

PushbackInputStream

These InputStreams act as the concrete components that we will wrap with decorators. There are a few more we didn’t show, like ObjectInputStream.

FilterInputStream

ByteArrayInputStream

BufferedInputStream

DataInputStream

LineNumberInputStream

crete decorators.

And finally, here are all our con

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.

you are here 4

101

write your own i/o decorator

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!”

t to import e g r o f ’t n o D t shown) java.io... (no

No problem. I just have to extend the FilterInputStream class and override the read() methods.

First, extend the FilterInputStream, the abstract decorator for all InputStreams.

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]); Now we need to implement two } read methods. They take a return result; byte (or an array of bytes) } convert each byte (that

REMEMBER: we don’t provide import and package statements in the code listings. Get the complete source code from the wickedlysmart web site. You’ll find the URL on page xxxiii in the Intro. 102

Chapter 3

and represents a character) to lowercase if it’s an uppercase character.

the decorator pattern

Test out your new Java I/O Decorator Write some quick code to test the I/O decorator: public class InputTest { public static void main(String[] args) throws IOException { int c; try { m InputStream in = ileInputStrea new LowerCaseInputStream( Set up the tFe it, first with new BufferedInputStream( and decoradInputStream new FileInputStream(“test.txt”))); Buffere while((c = in.read()) >= 0) { System.out.print((char)c); } in.close(); } catch (IOException e) { e.printStackTrace(); } } }

Just use the stream to read characters until the end of file and print as we go.

Give it a spin:

a brand new and then ouInr putStream filter. LowerCase

I know the Decorator Pattern therefore I RULE!

test.txt file

You need tfoile. make this

File Edit Window Help DecoratorsRule

% java InputTest i know the decorator pattern therefore i rule! %

you are here 4

103

decorator interview

Patterns Exposed This week’s interview:

Confessions of a Decorator HeadFirst: Welcome Decorator Pattern. We’ve heard that you’ve been a bit down on yourself lately? Decorator: Yes, I know the world sees me as the glamorous design pattern, but you know, I’ve got my share of problems just like everyone. HeadFirst: Can you perhaps share some of your troubles with us? Decorator: Sure. Well, you know I’ve got the power to add flexibility to designs, that much is for sure, but I also have a dark side. You see, I can sometimes add a lot of small classes to a design and this occasionally results in a design that’s less than straightforward for others to understand. HeadFirst: Can you give us an example? Decorator: Take the Java I/O libraries. These are notoriously difficult for people to understand at first. But if they just saw the classes as a set of wrappers around an InputStream, life would be much easier. HeadFirst: That doesn’t sound so bad. You’re still a great pattern, and improving this is just a matter of public education, right? Decorator: There’s more, I’m afraid. I’ve got typing problems: you see, people sometimes take a piece of client code that relies on specific types and introduce decorators without thinking through everything. Now, one great thing about me is that you can usually insert decorators transparently and the client never has to know it’s dealing with a decorator. But like I said, some code is dependent on specific types and when you start introducing decorators, boom! Bad things happen. HeadFirst: Well, I think everyone understands that you have to be careful when inserting decorators, I don’t think this is a reason to be too down on yourself. Decorator: I know, I try not to be. I also have the problem that introducing decorators can increase the complexity of the code needed to instantiate the component. Once you’ve got decorators, you’ve got to not only instantiate the component, but also wrap it with who knows how many decorators. HeadFirst: I’ll be interviewing the Factory and Builder patterns next week – I hear they can be very helpful with this? Decorator: That’s true; I should talk to those guys more often. HeadFirst: Well, we all think you’re a great pattern for creating flexible designs and staying true to the Open-Closed Principle, so keep your chin up and think positively! Decorator: I’ll do my best, thank you. 104

Chapter 3

the decorator pattern

Tools for your Design Toolbox

BULLET POINTS

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

ß Inheritance is one form of extension, but not necessarily the best way to achieve flexibility in our designs.

ß In our designs we should allow behavior to be extended without the need to modify existing code.

OO Basincs Abstractio tion s OO Principle Encapsulaphism Polymor hat varies. w e t la su p a Enc nchee. ritance eritaIn h in r e v o n ositio Favor comp not interfaces, Program to s. ion implementat gns coupled desi ly se o lo r o Strive f eract. ts that int c je b o n e e w bet for

ld be open r u o h s s e s s la o C ut closed f extension b n. modificatio

ß Composition and delegation can often be used to add new behaviors at runtime.

ß The Decorator Pattern provides an alternative to subclassing for extending behavior.

ß The Decorator Pattern involves a set of decorator classes that are used to wrap concrete components.

ß We now have the Open-Closed g Principle to guide us. We’re goin to strive to design our system so that the closed parts are . isolated from our new extensions

OO Patterr-ndsefineysofa aolgneor-itthom-s,tmhaanty

bys-erdvefeinyesbaetfwameilenkeosbjethcetms soddaitllioitnsal O g e t a r St depenedaecnhcone, andrm-aanAgtets ascthaotareit, hm o ch lg pddaytneadmically. encapsuwlahteensDoenc.e oSortbrajeattcetgy slettsoietahdne aaonbdjecutit able ilritme ienclioetnifts thaat fulesexib. le a interchdanegpeernedseptnolytnsib r tending nden f o s provide vary indaeupteoDmeactoicralltyor o subclassing for ex t alternative y. t functionali designs n for creatingOr was it er t t pa st ir f r . And here’s ou the Open-Closed Principle ern we’ve that satisfy st? Is there another patt really the fir lows this principle as well? used that fol

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 are typically transparent to the client of the component; that is, unless the client is relying on the component’s concrete type.

ß Decorators can result in many small objects in our design, and overuse can be complex. you are here 4

105

exercise solutions

Exercise solutions public class Beverage { // declare instance variables for milkCost, // soyCost, mochaCost, and whipCost, and // getters and setters for milk, soy, mocha // and whip.

public class DarkRoast extends Beverage { public DarkRoast() { description = “Most Excellent Dark Roast”; }

public float cost() { float condimentCost = 0.0; if (hasMilk()) { condimentCost += milkCost; } if (hasSoy()) { condimentCost += soyCost; } if (hasMocha()) { condimentCost += mochaCost; } if (hasWhip()) { condimentCost += whipCost; } return condimentCost;

public float cost() { return 1.99 + super.cost();

}

}

} }

New barista training

“double mocha soy lotte with whip” on Mocha 2 Whip calls cost() another Mocha. 3 Mocha calls cost() on the Mocha calls cost() on Soy. First, we call cost() on 4 Next, 1 outmost decorator, Whip. Soy calls 5 Last topping! cost() on HouseBlend.

6

offee

tarbu

zz C

$1.54

.10

cost()

.20

S

W

hip

Whip’s cost(), which adds .10 and we have a final cost of $1.54.

Chapter 3

.15

.20

M

11 Finally, the result returns to

106

cost() cost()

Mo

cha

oc

ha

cost()

So

y

cost()

.89 nd HouseBle

7

8

9

HouseBlend’s cost() method returns .89 cents and pops off the stack. Soy’s cost() method adds .15 and returns the result, and pops off the stack.

The second Mocha’s cost() method adds .20 and returns the result, and pops off the stack.

method The first Mocha’s cost() result, adds .20 and returns the and pops off the stack.

the decorator pattern

Exercise solutions Our friends at Starbuzz have introduced sizes to their menu. You can now order a coffee in tall, grande, and venti sizes (for us normal folk: small, medium, and large). Starbuzz saw this as an intrinsic part of the coffee class, so they’ve added two methods to the Beverage class: setSize() and getSize(). They’d also like for the condiments to be charged according to size, so for instance, Soy costs 10¢, 15¢, and 20¢ respectively for tall, grande, and venti coffees. How would you alter the decorator classes to handle this change in requirements?

public class Soy extends CondimentDecorator { Beverage beverage; public Soy(Beverage beverage) { this.beverage = beverage; } public getSize() { return beverage.getSize(); }

propagate the Now we need thoo d to the wrapped getSize() mete should also move this beverage. W he abstract class since method to t l condiment decorators. it’s used in al

public String getDescription() { return beverage.getDescription() + “, Soy”; } public double cost() { double cost = beverage.cost(); if (getSize() == Beverage.TALL) { cost += .10; } else if (getSize() == Beverage.GRANDE) { cost += .15; } else if (getSize() == Beverage.VENTI) { cost += .20; } return cost; }

Here we get the size (which propagates all the way to the concrete beverage) and then add the appropriate cost.

}

you are here 4

107

Subscribe

© Copyright 2013 - 2018 AZDOC.PL All rights reserved.