Chapter 1. Welcome to Design Patterns: Intro to Design Patterns

Images

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 object-oriented (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.

It started with a simple SimUDuck app

Joe works for a company that makes a highly successful duck pond simulation game, SimUDuck. The game can show a large variety of duck species swimming and making quacking sounds. The initial designers of the system used standard OO techniques and created one Duck superclass from which all other duck types inherit.

Images

In the last year, the company has been under increasing pressure from competitors. After a week-long off-site brainstorming session over golf, the company executives think it’s time for a big innovation. They need something really impressive to show at the upcoming shareholders meeting in Maui next week.

But now we need the ducks to FLY

The executives decided that flying ducks is just what the simulator needs to blow away the competitors. And of course Joe’s manager told them it’ll be no problem for Joe to just whip something up in a week. “After all,” said Joe’s boss, “he’s an OO programmer…how hard can it be?”

Images

But something went horribly wrong...

Images

What happened?

Joe failed to notice that not all subclasses of Duck should fly. When Joe added new behavior to the Duck superclass, he was also adding behavior that was not appropriate for some Duck subclasses. He now has flying inanimate objects in the SimUDuck program.

Images

A localized update to the code caused a non-local side effect (flying rubber ducks)!

Images

What Joe thought was a great use of inheritance for the purpose of reuse hasn’t turned out so well when it comes to maintenance.

Joe thinks about inheritance...

Images

How about an interface?

Joe realized that inheritance probably wasn’t the answer, because he just got a memo that says that the executives now want to update the product every six months (in ways they haven’t yet decided on). Joe knows the spec will keep changing and he’ll be forced to look at and possibly override fly() and quack() for every new Duck subclass that’s ever added to the program... forever.

So, he needs a cleaner way to have only some (but not all) of the duck types fly or quack.

Images

What would you do if you were Joe?

We know that not all of the subclasses should have flying or quacking behavior, so inheritance isn’t the right answer. But while having the subclasses implement Flyable and/or Quackable solves part of the problem (no inappropriately flying rubber ducks), it completely destroys code reuse for those behaviors, so it just creates a different maintenance nightmare. And of course there might be more than one kind of flying behavior even among the ducks that do fly...

Images

At this point you might be waiting for a Design Pattern to come riding in on a white horse and save the day. But what fun would that be? No, we’re going to figure out a solution the old-fashioned way—by applying good OO software design principles.

Images

The one constant in software development

Okay, what’s the one thing you can always count on in software development?

No matter where you work, what you’re building, or what language you are programming in, what’s the one true constant that will be with you always?

Images

No matter how well you design an application, over time an application must grow and change or it will die.

Zeroing in on the problem...

So we know using inheritance hasn’t worked out very well, since the duck behavior keeps changing across the subclasses, and it’s not appropriate for all subclasses to have those behaviors. The Flyable and Quackable interface sounded promising at first—only ducks that really do fly will be Flyable, etc.—except Java interfaces typically have no implementation code, so no code reuse. In either case, whenever you need to modify a behavior, you’re often forced to track down and change it in all the different subclasses where that behavior is defined, probably introducing new bugs along the way!

Luckily, there’s a design principle for just this situation.

Images

In other words, if you’ve got some aspect of your code that is changing, say with every new requirement, then you know you’ve got a behavior that needs to be pulled out and separated from all the stuff that doesn’t change.

Here’s another way to think about this principle: take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don’t.

As simple as this concept is, it forms the basis for almost every design pattern. All patterns provide a way to let some part of a system vary independently of all other parts.

Okay, time to pull the duck behavior out of the Duck classes!

Take what varies and “encapsulate” it so it won’t affect the rest of your code.

The result? Fewer unintended consequences from code changes and more f lexibility in your systems!

Separating what changes from what stays the same

Where do we start? As far as we can tell, other than the problems with fly() and quack(), the Duck class is working well and there are no other parts of it that appear to vary or change frequently. So, other than a few slight changes, we’re going to pretty much leave the Duck class alone.

Now, to separate the “parts that change from those that stay the same,” we are going to create two sets of classes (totally apart from Duck), one for fly and one for quack. Each set of classes will hold all the implementations of the respective behavior. For instance, we might have one class that implements quacking, another that implements squeaking, and another that implements silence.

  • We know that fly() and quack() are the parts of the Duck class that vary across ducks.

  • To separate these behaviors from the Duck class, we’ll pull both methods out of the Duck class and create a new set of classes to represent each behavior.

Images

Designing the Duck Behaviors

So how are we going to design the set of classes that implement the fly and quack behaviors?

We’d like to keep things flexible; after all, it was the inflexibility in the duck behaviors that got us into trouble in the first place. And we know that we want to assign behaviors to the instances of Duck. For example, we might want to instantiate a new MallardDuck instance and initialize it with a specific type of flying behavior. And while we’re there, why not make sure that we can change the behavior of a duck dynamically? In other words, we should include behavior setter methods in the Duck classes so that we can change the MallardDuck’s flying behavior at runtime.

Given these goals, let’s look at our second design principle:

Images

We’ll use an interface to represent each behavior—for instance, FlyBehavior and QuackBehavior—and each implementation of a behavior will implement one of those interfaces.

So this time it won’t be the Duck classes that will implement the flying and quacking interfaces. Instead, we’ll make a set of classes whose entire reason for living is to represent a behavior (for example, “squeaking”), and it’s the behavior class, rather than the Duck class, that will implement the behavior interface.

This is in contrast to the way we were doing things before, where a behavior came either from a concrete implementation in the superclass Duck, or by providing a specialized implementation in the subclass itself. In both cases we were relying on an implementation. We were locked into using that specific implementation and there was no room for changing the behavior (other than writing more code).

With our new design, the Duck subclasses will use a behavior represented by an interface (FlyBehavior and QuackBehavior), so that the actual implementation of the behavior (in other words, the specific concrete behavior coded in the class that implements the FlyBehavior or QuackBehavior) won’t be locked into the Duck subclass.

Images

From now on, the Duck behaviors will live in a separate class—a class that implements a particular behavior interface.

That way, the Duck classes won’t need to know any of the implementation details for their own behaviors.

“Program to an interface” really means “Program to a supertype.”

The word interface is overloaded here. There’s the concept of an interface, but there’s also the Java construct of an interface. You can program to an interface without having to actually use a Java interface. The point is to exploit polymorphism by programming to a supertype so that the actual runtime object isn’t locked into the code. And we could rephrase “program to a supertype” as “the declared type of the variables should be a supertype, usually an abstract class or interface, so that the objects assigned to those variables can be of any concrete implementation of the supertype, which means the class declaring them doesn’t have to know about the actual object types!”

Images

This is probably old news to you, but just to make sure we’re all saying the same thing, here’s a simple example of using a polymorphic type—imagine an abstract class Animal, with two concrete implementations, Dog and Cat.

Programming to an implementation would be:

Images

But programming to an interface/supertype would be:

Images

Even better, rather than hardcoding the instantiation of the subtype (like new Dog()) into the code, assign the concrete implementation object at runtime:

Images
Images

Implementing the Duck Behaviors

Here we have the two interfaces, FlyBehavior and QuackBehavior, along with the corresponding classes that implement each concrete behavior:

Images
Images

there are no Dumb Questions

Q: Do I always have to implement my application first, see where things are changing, and then go back to separate and encapsulate those things?

A: Not always; often when you are designing an application, you anticipate those areas that are going to vary and then go ahead and build the flexibility to deal with it into your code. You’ll find that the principles and patterns can be applied at any stage of the development lifecycle.

Q: Should we make Duck an interface too?

A: Not in this case. As you’ll see once we’ve got everything hooked together, we do benefit by having Duck not be an interface, and having specific ducks, like MallardDuck, inherit common properties and methods. Now that we’ve removed what varies from the Duck inheritance, we get the benefits of this structure without the problems.

Q: It feels a little weird to have a class that’s just a behavior. Aren’t classes supposed to represent things? Aren’t classes supposed to have both state AND behavior?

A: In an OO system, yes, classes represent things that generally have both state (instance variables) and methods. And in this case, the thing happens to be a behavior. But even a behavior can still have state and methods; a flying behavior might have instance variables representing the attributes for the flying (wing beats per minute, max altitude, speed, etc.) behavior.

Answers:

1) Create a FlyRocketPowered class that implements the FlyBehavior interface.

2) One example, a duck call (a device that makes duck sounds).

Integrating the Duck Behaviors

Note

Here’s the key: A Duck will now delegate its flying and quacking behaviors, instead of using quacking and flying methods defined in the Duck class (or subclass).

Here’s how:

  1. First we’ll add two instance variables of type FlyBehavior and QuackBehavior—let’s call them flyBehavior and quackBehavior. Each concrete duck object will assign to those variables a specific behavior at runtime, like FlyWithWings for flying and Squeak for quacking.

    We’ll also remove the fly() and quack() methods from the Duck class (and any subclasses) because we’ve moved this behavior out into the FlyBehavior and QuackBehavior classes.

    We’ll replace fly() and quack() in the Duck class with two similar methods, called performFly() and performQuack(); you’ll see how they work next.

    Images
  2. Now we implement performQuack():

    Images

    Pretty simple, huh? To perform the quack, a Duck just asks the object that is referenced by quackBehavior to quack for it. In this part of the code we don’t care what kind of object the concrete Duck is, all we care about is that it knows how to quack()!

More integration...

  1. Okay, time to worry about how the flyBehavior and quackBehavior instance variables are set. Let’s take a look at the MallardDuck class:

    Images

    MallardDuck’s quack is a real live duck quack, not a squeak and not a mute quack. When a MallardDuck is instantiated, its constructor initializes the MallardDuck’s inherited quackBehavior instance variable to a new instance of type Quack (a QuackBehavior concrete implementation class).

    And the same is true for the duck’s flying behavior—the MallardDuck’s constructor initializes the inherited flyBehavior instance variable with an instance of type FlyWithWings (a FlyBehavior concrete implementation class).

    Images

    Good catch, that’s exactly what we’re doing... for now.

    Later in the book we’ll have more patterns in our toolbox that can help us fix it.

    Still, notice that while we are setting the behaviors to concrete classes (by instantiating a behavior class like Quack or FlyWithWings and assigning it to our behavior reference variable), we could easily change that at runtime.

    So, we still have a lot of flexibility here. That said, we’re doing a poor job of initializing the instance variables in a flexible way. But think about it: since the quackBehavior instance variable is an interface type, we could (through the magic of polymorphism) dynamically assign a different QuackBehavior implementation class at runtime.

    Take a moment and think about how you would implement a duck so that its behavior could change at runtime. (You’ll see the code that does this a few pages from now.)

Testing the Duck code

  1. Type and compile the Duck class below (Duck.java), and the MallardDuck class from two pages back (MallardDuck.java).

    Images
  2. Type and compile the FlyBehavior interface (FlyBehavior.java) and the two behavior implementation classes (FlyWithWings.java and FlyNoWay.java).

    Images
  3. Type and compile the QuackBehavior interface (QuackBehavior.java) and the three behavior implementation classes (Quack.java, MuteQuack.java, and Squeak.java).

    Images
  4. Type and compile the test class (MiniDuckSimulator.java).

    Images
  5. Run the code!

    Images

Setting behavior dynamically

What a shame to have all this dynamic talent built into our ducks and not be using it! Imagine you want to set the duck’s behavior type through a setter method on the Duck class, rather than by instantiating it in the duck’s constructor.

  1. Add two new methods to the Duck class:

    Images
  2. Make a new Duck type (ModelDuck.java).

    Images
  3. Make a new FlyBehavior type (FlyRocketPowered.java).

    Images
  4. Change the test class (MiniDuckSimulator.java), add the ModelDuck, and make the ModelDuck rocket-enabled.

    Images
  5. Run it!

To change a duck’s behavior at runtime, just call the duck’s setter method for that behavior.

The Big Picture on encapsulated behaviors

Okay, now that we’ve done the deep dive on the duck simulator design, it’s time to come back up for air and take a look at the big picture.

Below is the entire reworked class structure. We have everything you’d expect: ducks extending Duck, fly behaviors implementing FlyBehavior, and quack behaviors implementing QuackBehavior.

Notice also that we’ve started to describe things a little differently. Instead of thinking of the duck behaviors as a set of behaviors, we’ll start thinking of them as a family of algorithms. Think about it: in the SimUDuck design, the algorithms represent things a duck would do (different ways of quacking or flying), but we could just as easily use the same techniques for a set of classes that implement the ways to compute state sales tax by different states.

Pay careful attention to the relationships between the classes. In fact, grab your pen and write the appropriate relationship (IS-A, HAS-A, and IMPLEMENTS) on each arrow in the class diagram.

Note

Make sure you do this.

Images

HAS-A can be better than IS-A

The HAS-A relationship is an interesting one: each duck has a FlyBehavior and a QuackBehavior to which it delegates flying and quacking.

When you put two classes together like this you’re using composition. Instead of inheriting their behavior, the ducks get their behavior by being composed with the right behavior object.

This is an important technique; in fact, it is the basis of our third design principle:

Images

As you’ve seen, creating systems using composition gives you a lot more flexibility. Not only does it let you encapsulate a family of algorithms into their own set of classes, but it also lets you change behavior at runtime as long as the object you’re composing with implements the correct behavior interface.

Composition is used in many design patterns and you’ll see a lot more about its advantages and disadvantages throughout the book.

Speaking of Design Patterns...

Images

You just applied your first design pattern—the STRATEGY Pattern. That’s right, you used the Strategy Pattern to rework the SimUDuck app.

Thanks to this pattern, the simulator is ready for any changes those execs might cook up on their next business trip to Maui.

Now that we’ve made you take the long road to learn it, here’s the formal definition of this pattern:

Note

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Note

Use THIS definition when you need to impress friends and influence key executives.

Inline Design Puzzle

Below you’ll find a mess of classes and interfaces for an action adventure game. You’ll find classes for game characters along with classes for weapon behaviors the characters can use in the game. Each character can make use of one weapon at a time, but can change weapons at any time during the game. Your job is to sort it all out...

(Answers are at the end of the chapter.)

Your task:

  1. Arrange the classes.

  2. Identify one abstract class, one interface, and eight classes.

  3. Draw arrows between classes.

    a. Draw this kind of arrow for inheritance (“extends”). Images

    b. Draw this kind of arrow for interface (“implements”). Images

    c. Draw this kind of arrow for HAS-A. Images

  4. Put the method setWeapon() into the right class.

Images

Overheard at the local diner...

Images

What’s the difference between these two orders? Not a thing! They’re both the same order, except Alice is using twice the number of words and trying the patience of a grumpy short-order cook.

What’s Flo got that Alice doesn’t? A shared vocabulary with the short-order cook. Not only does that make it easier to communicate with the cook, but it gives the cook less to remember because he’s got all the diner patterns in his head.

Design Patterns give you a shared vocabulary with other developers. Once you’ve got the vocabulary, you can more easily communicate with other developers and inspire those who don’t know patterns to start learning them. It also elevates your thinking about architectures by letting you think at the pattern level, not the nitty-gritty object level.

Overheard in the next cubicle...

Images
Images

The power of a shared pattern vocabulary

When you communicate using patterns, you are doing more than just sharing LINGO.

Shared pattern vocabularies are POWERFUL. When you communicate with another developer or your team using patterns, you are communicating not just a pattern name but a whole set of qualities, characteristics, and constraints that the pattern represents.

Note

“We’re using the Strategy Pattern to implement the various behaviors of our ducks.” This tells you the duck behavior has been encapsulated into its own set of classes that can be easily expanded and changed, even at runtime if needed.

Patterns allow you to say more with less. When you use a pattern in a description, other developers quickly know precisely the design you have in mind.

Talking at the pattern level allows you to stay “in the design” longer. Talking about software systems using patterns allows you to keep the discussion at the design level, without having to dive down to the nitty-gritty details of implementing objects and classes.

Note

How many design meetings have you been in that quickly degrade into implementation details?

Shared vocabularies can turbo-charge your development team. A team well versed in design patterns can move more quickly with less room for misunderstanding.

Note

As your team begins to share design ideas and experience in terms of patterns, you will build a community of pattern users.

Shared vocabularies encourage more junior developers to get up to speed. Junior developers look up to experienced developers. When senior developers make use of design patterns, junior developers also become motivated to learn them. Build a community of pattern users at your organization.

Note

Think about starting a patterns study group at your organization. Maybe you can even get paid while you’re learning...

How do I use Design Patterns?

We’ve all used off-the-shelf libraries and frameworks. We take them, write some code against their APIs, compile them into our programs, and benefit from a lot of code someone else has written. Think about the Java APIs and all the functionality they give you: network, GUI, IO, etc. Libraries and frameworks go a long way toward a development model where we can just pick and choose components and plug them right in. But...they don’t help us structure our own applications in ways that are easier to understand, more maintainable, and more flexible. That’s where design patterns come in.

Design patterns don’t go directly into your code, they first go into your BRAIN. Once you’ve loaded your brain with a good working knowledge of patterns, you can then start to apply them to your new designs, and rework your old code when you find it’s degrading into an inflexible mess.

Images

there are no Dumb Questions

Q: If design patterns are so great, why can’t someone build a library of them so I don’t have to?

A: Design patterns are higher level than libraries. Design patterns tell us how to structure classes and objects to solve certain problems, and it is our job to adapt those designs to fit our particular application.

Q: Aren’t libraries and frameworks also design patterns?

A: Frameworks and libraries are not design patterns; they provide specific implementations that we link into our code. Sometimes, however, libraries and frameworks make use of design patterns in their implementations. That’s great, because once you understand design patterns, you’ll more quickly understand APIs that are structured around design patterns.

Q: So, there are no libraries of design patterns?

A: No, but you will learn later about patterns catalogs with lists of patterns that you can apply to your applications.

Images

Developer: Okay, hmm, but isn’t this all just good object-oriented design; I mean as long as I follow encapsulation and I know about abstraction, inheritance, and polymorphism, do I really need to think about Design Patterns? Isn’t it pretty straightforward? Isn’t this why I took all those OO courses? I think Design Patterns are useful for people who don’t know good OO design.

Guru: Ah, this is one of the true misunderstandings of object-oriented development: that by knowing the OO basics we are automatically going to be good at building flexible, reusable, and maintainable systems.

Developer: No?

Guru: No. As it turns out, constructing OO systems that have these properties is not always obvious and has been discovered only through hard work.

Developer: I think I’m starting to get it. These, sometimes non-obvious, ways of constructing object-oriented systems have been collected...

Guru: ...yes, into a set of patterns called Design Patterns.

Developer: So, by knowing patterns, I can skip the hard work and jump straight to designs that always work?

Guru: Yes, to an extent, but remember, design is an art. There will always be tradeoffs. But, if you follow well-thought-out and time-tested design patterns, you’ll be way ahead.

Developer: What do I do if I can’t find a pattern?

Images

Guru: There are some object-oriented principles that underlie the patterns, and knowing these will help you to cope when you can’t find a pattern that matches your problem.

Developer: Principles? You mean beyond abstraction, encapsulation, and...

Guru: Yes, one of the secrets to creating maintainable OO systems is thinking about how they might change in the future, and these principles address those issues.

Images Tools for your Design Toolbox

You’ve nearly made it through the first chapter! You’ve already put a few tools in your OO toolbox; let’s make a list of them before we move on to Chapter 2.

Images

Inline Design Patterns Crossword

Let’s give your right brain something to do.

It’s your standard crossword; all of the solution words are from this chapter.

Images

ACROSS

1. Paatterns can help us build ________ applications.

4. Strategies can be __________.

7. Favor this over inheritance.

8. Development constant.

9. Java IO, Networking, Sound.

10. Most patterns follow from OO _________.

12. Design patterns are a shared __________.

14. High-level libraries.

15. Learn from the other guy’s ___________.

17. Pattern that fixed the simulator.

18. Program to this, not an implementation.

DOWN

2. Patterns go into your _______.

3. Duck that can’t quack.

5. Rubber ducks make a _______.

6. ________ what varies.

11. Grilled cheese with bacon.

13. Rick was thrilled with this pattern.

16. Duck demo was located here.

Inline Design Puzzle Solution

Character is the abstract class for all the other characters (King, Queen, Knight, and Troll), while WeaponBehavior is an interface that all weapon behaviors implement. So all actual characters and weapons are concrete classes.

To switch weapons, each character calls the setWeapon() method, which is defined in the Character superclass. During a fight the useWeapon() method is called on the current weapon set for a given character to inflict great bodily damage on another character.

Images
Note

Note that ANY object could implement the WeaponBehavior interface—say, a paper clip, a tube of toothpaste, or a mutated sea bass.

Inline Design Patterns Crossword Solution

Images

Get Head First Design Patterns, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.