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

image with no caption

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.

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.

image with no caption

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 other duck sim 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?”

image with no caption
image with no caption
image with no caption

But something went horribly wrong...

image with no caption
image with no caption

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.

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

image with no caption
image with no caption

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

image with no caption

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.

image with no caption
image with no caption

What do YOU think about this design?

image with no caption

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

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.

image with no caption

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?

image with no caption

(use a mirror to see the answer)

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 have no implementation code, so no code reuse. And that means that whenever you need to modify a behavior, you’re 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.

Design Principle

Identify the aspects of your application that vary and separate them from what stays the same.

The first of many design principles. We’ll spend more time on these throughout the book.

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 flexibility in your systems!

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!

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.

image with no caption

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:

Design Principle

Program to an interface, not an implementation.

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.

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.

image with no caption
image with no caption

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

The word interface is overloaded here. There’s the concept of interface, but there’s also the Java construct 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!”

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:

Dog d = new Dog();
d.bark();

Note

Declaring the variable “d” as type Dog (a concrete implementation of Animal) forces us to code to a concrete implementation.

But programming to an interface/supertype would be:

Animal animal = new Dog();
animal.makeSound();

Note

We know it’s a Dog, but we can now use the animal reference polymorphically.

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

a = getAnimal();
a.makeSound();

Note

We don’t know WHAT the actual animal subtype is... all we care about is that it knows how to respond to makeSound().

image with no caption

Implementing the Duck Behaviors

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

image with no caption

Note

With this design, other types of objects can reuse our fly and quack behaviors because these behaviors are no longer hidden away in our Duck classes!

And we can add new behaviors without modifying any of our existing behavior classes or touching any of the Duck classes that use flying behaviors.

So we get the benefit of REUSE without all the baggage that comes along with inheritance.

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 Behavior

The key is that a Duck will now delegate its flying and quacking behavior, instead of using quacking and flying methods defined in the Duck class (or subclass).

Here’s how:

  • ① First we’ll add two instance variables to the Duck class called flyBehavior and quackBehavior that are declared as the interface type (not a concrete class implementation type). Each duck object will set these variables polymorphically to reference the specific behavior type it would like at runtime (FlyWithWings, Squeak, etc.).

    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.

    image with no caption
  • ② Now we implement performQuack():

    image with no caption

    Pretty simple, huh? To perform the quack, a Duck just allows 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 it is, all we care about is that it knows how to quack()!

More integration...

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

    image with no caption

    So MallardDuck’s quack is a real live duck quack, not a squeak and not a mute quack. So what happens here? 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 flyBehavior instance variable with an instance of type FlyWithWings (a FlyBehavior concrete implementation class).

image with no caption

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, but 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

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

    image with no caption
  • ② Type and compile the FlyBehavior interface (FlyBehavior.java) and the two behavior implementation classes (FlyWithWings.java and FlyNoWay.java).

    image with no caption
  • ③ Type and compile the QuackBehavior interface (QuackBehavior.java) and the three behavior implementation classes (Quack.java, MuteQuack.java, and Squeak.java).

    public interface QuackBehavior {
       public void quack();
    }
    _______________________________________________________________
    public class Quack implements QuackBehavior {
       public void quack() {
          System.out.println("Quack");
       }
    }
    _______________________________________________________________
    public class MuteQuack implements QuackBehavior {
       public void quack() {
           System.out.println("<< Silence >>");
       }
    }
    _______________________________________________________________
    public class Squeak implements QuackBehavior {
       public void quack() {
           System.out.println("Squeak");
       }
    }
  • ④ Type and compile the test class (MiniDuckSimulator.java).

    image with no caption
  • ⑤ Run the code!

    image with no caption

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 subclass, rather than by instantiating it in the duck’s constructor.

  • ① Add two new methods to the Duck class:

    image with no caption

    We can call these methods anytime we want to change the behavior of a duck on the fly.

    Note

    Editor note: gratuitous pun - fix

  • ② Make a new Duck type (ModelDuck.java).

    image with no caption
  • ③ Make a new FlyBehavior type (FlyRocketPowered.java).

    image with no caption
  • ④ Change the test class (MiniDuckSimulator.java), add the ModelDuck, and make the ModelDuck rocket-enabled.

    image with no caption

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.

image with no caption

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, we’ve been using our third design principle:

Design Principle

Favor composition over inheritance.

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.

Brain Power

A duck call is a device that hunters use to mimic the calls (quacks) of ducks. How would you implement your own duck call that does not inherit from the Duck class?

Speaking of Design Patterns...

Overheard at the local diner...

image with no caption

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

image with no caption
image with no caption

Brain Power

Can you think of other shared vocabularies that are used beyond OO design and diner talk? (Hint: how about auto mechanics, carpenters, gourmet chefs, air traffic control.) What qualities are communicated along with the lingo?

Can you think of aspects of OO design that get communicated along with pattern names? What qualities get communicated along with the name “Strategy Pattern”?

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 patterns 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 towards 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 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 of jungle spaghetti code.

image with no caption
image with no caption
image with no caption

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?

image with no caption

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.

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.

image with no caption

Get Head First Design Patterns 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.