Naper's Nop Slides to Knowledge

Design Patterns


~ 2013.04.03 ~

@author

Michael Naper

napermd@vt.edu

Sponsored by...


Video recording available at...


http://youtu.be/M9ekS5vuYo4

Who is this guy?

(And why should I believe him?)

  • Graduating senior in Computer Science & Math
  • Interned at Google for two summers
  • CS UTA for seven semesters (four being CS 3114)
  • Reads books on software design for fun ...no, really!

So what's a design pattern?


"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice."

- Christopher Alexander, talking about patterns in buildings and towns

In software engineering...


A design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. It is a description or template for how to solve a problem that can be used in many different situations.

Let's look at an example!

It started with a simple SimUDuck app...

...a "duck pond simulator" that can show a wide variety of duck species swimming and quacking.

But a request has arrived to allow ducks to also fly.
(We need to stay ahead of the competition!)

Easy fix: Add fly() to Duck ...and now all ducks can fly!

Whoops! Rubber ducks do not fly!

They don't quack, either, so we overrode quack() to make them squeak.

We could override fly() to make it do nothing, but that's less than ideal, especially...

...when we might find other Duck subclasses that would have to do the same thing.

What was supposed to be a good instance of reuse via inheritance has turned into a maintenance headache!

What about an Interface?

Here we define two interfaces and allow subclasses to inherit the interface they need.

What are the trade-offs?

With inheritance we get...

  • code reuse (e.g., one quack() method instead of multiple) --> good
  • common behavior in root class (but maybe no so common afterall) --> bad


With interfaces we get...

  • specificity (only those subclasses that need a fly() method get it) --> good
  • no code re-use (since interfaces only define signatures) --> bad

OO Principles to the Rescue!


Encapsulate what varies:
the behaviors between Duck subclasses

The Basic Idea

  • Take any behavior that varies across Duck subclasses and pull them out of Duck.
    • Duck's will no longer have fly() and quack() methods directly.
    • Create two sets of classes: one that implements fly behaviors and one that implements quack behaviors.
  • Code to an interface (i.e., variable whose type is the supertype)
    • For QuackBehavior: Quack, Squeak, Silence
    • For FlyBehavior: FlyWithWings, CantFly, FlyWhenThrown

Bringing It All Together


To take advantage of these new behaviors, we must modify Duck to delegate its flying and quacking behaviors to these other classes rather than implementing this behavior internally.

Bringing It All Together


We'll add two attributes that store the desired behavior.


This is an instance of the principle
"Favor composition over inheritance".


(For clarity, we'll also rename fly() and quack() to performFly() and performQuack().)

FlyBehavior and QuackBehavior define a set of behaviors that provide behavior to Duck. Duck is composing each set of behaviors and can switch among them dynamically, if needed.

While now each subclass has a performFly() and performQuack() method, at least the user interface is uniform and those methods can point to "null" behaviors when required.

Is It Time to Celebrate?


Not quite yet... Our duck simulator is not completely decoupled from the Duck subclasses:

Duck mallard = new MallardDuck();

Fortunately, we can eliminate this type of coupling if needed, using a design pattern called Factory.


(We'll get to talk about the Factory pattern another day...)

Meet the Strategy Pattern!


The solution that we applied to this design problem is known as the Strategy Design Pattern.


It features the following OO design concepts/principles:

  • Encapsulate what varies
  • Code to an interface
  • Delegation
  • Favor composition over inheritance

Definition:


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.

Strategy Pattern


Motivation

  • There are common situations when classes differ only in their behavior. For these cases, it is a good idea to isolate the algorithms in separate classes in order to have the ability to select different algorithms at runtime.
  • It's difficult to add new algorithms and vary existing ones when the behavior is an integral part of a client.
  • One of the dominant strategies of object-oriented design is the "open-closed principle": "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."

Strategy Pattern

Class Structure


  • AStrategy is pulled out of Context.
  • Context only makes use of the public interface of AStrategy and is not tied to concrete subclasses.
  • Context can change its behavior by switching among the various concrete algorithms.

Strategy Pattern


Applicability

  • Many related classes differ only in behavior
  • You need different variants of an algorithm
    • Reflecting different space/time trade-offs
  • An algorithm uses data that clients shouldn't know about
    • Avoid exposing complex, algorithm-specific data structures
  • Many behaviors appear as multiple conditional statements
    • Move related conditional branches into their own Strategy class

Strategy Pattern


Consequences

  • Encapsulating the algorithm in separate Strategy classes lets you vary the algorithm independently of its context, making it easier to switch, understand, and extend.
  • Clients must be aware of different Strategies.
  • Communication overhead between Strategy and Context.
  • Increased number of objects.

In general, a pattern has four essential elements:

  1. The pattern name is a handle we can use to describe a design problem, its solutions, and consequences in a word or two.
  2. The problem describes when to apply the pattern.
  3. The solution describes the elements that make up the design, their relationships, responsibilities, and collaborations.
  4. The consequences are the results and trade-offs of applying the pattern.

Let's look at another pattern!

The Singleton Pattern


Our ticket to creating one-of-a-kind objects for which there is only one instance.

A little dialogue overheard between a TA and her student...

Student: What use is that?

TA: There are many objects that we only need one of: thread pools, caches, dialog boxes, objects that handle preferences and registry settings, objects used for logging, and objects that act as device drivers to devices like printers and graphics cards.

TA: In fact, if we were to instantiate more than one, we'd run into all sorts of problems like incorrect program behavior, overuse of resources, or inconsistent results.

Student: Okay, so maybe there are classes that should only be instantiated once, but do I need a whole design pattern for this? Can't I just do this by convention or by global variables? You know, like in Java, I could do it with a static variable.

TA: In many ways, the Singleton Pattern is a convention for ensuring one and only one object is instantiated. It is a time-tested method, and gives us a global point of access, just like a global variable, but without the downsides.

Student: What downsides?

TA: Well, here's one example: if you assign an object to a global variable, then that object might be created when your application begins. Right? What if this object is resource intensive and your application never ends up using it? As you will see, with the Singleton Pattern, we can create our objects only when they are needed.

Student: This still doesn't seem like it should be so difficult...

TA: If you've got a good handle on static class variables and methods as well as access modifiers, it's not. But, as simple as it sounds, it's hard to get right. Just ask yourself: how do I prevent more than one object from being instantiated? It's not so obvious, is it?

  • How would you create a single object?
    • new MyObject();
  • And, what if another object wanted to create a MyObject? Could it call new on MyObject again?
    • Yes, of course.
  • So as long as we have a class, can we always instantiate it one or more times?
    • Yes. Well, only if it's a public class.
  • And if not?
    • Well, if it's not a public class, only classes in the same package can instantiate it. But they can still instantiate it more than once.
  • Hmm, interesting. Did you know you could do this?
    public MyClass {
      private MyClass() {}
    }
    • No, I'd never thought of it, but I guess it makes sense because it is a legal definition.
  • What does it mean?
    • I suppose it is a class that can't be instantiated because it has a private constructor.
  • Well, is there any object that could use the private constructor?
    • Hmm, I think the code in MyClass is the only code that could call it. But that doesn't make much sense.
  • Why not?
    • Because I'd have to have an instance of the class to call it, but I can't have an instance because no other class can instantiate it.
  • Okay. What does this mean?
    public MyClass {
      public static MyClass getInstance() {}
    }
    • MyClass is a class with a static method. We can call the static method like this: MyClass.getInstance()
  • Very interesting. What if we put things together? Now can I instantiate a MyClass?
    public MyClass {
      private MyClass() {}
      public static MyClass getInstance() {
        return new MyClass();
      }
    }
    • Wow, you sure can.
  • So, now can you think of a second way to instantiate an object?
    • MyClass.getInstance();
  • Can you finish the code so that only one instance of MyClass is ever created?
    • Yeah, I think so...

A first pass...


public class Singleton {

  private static Singleton uniqueInstance;

  // other useful instance variables here

  private Singleton() {}

  public static Singleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqueInstance;
  }

  // other useful methods here
}

Uh oh! What about multithreading?!

For most Java applications, we obviously need to ensure that the Singleton works in the presence of multiple threads. But, it looks fairly expensive to synchronize the getInstance() method, so what do we do?

Well, we have a few options...

Dealing with Multithreading

  1. Do nothing if the performance of getInstance() isn't critical to your application.
  2. Move to an eagerly created instance rather than a lazily created one.
    public class Singleton {
      private static Singleton uniqueInstance = new Singleton();
      private Singleton() {}
      public static Singleton getInstance() {
        return uniqueInstance;
      }
    }

Dealing with Multithreading

  1. Use "double-checked locking" to reduce the use of synchronization.
    public class Singleton {
      private volatile static Singleton uniqueInstance;
      private Singleton() {}
      public static Singleton getInstance() {
        if (uniqueInstance == null) {
          synchronized (Singleton.class) {
            if (uniqueInstance == null) { uniqueInstance = new Singleton(); }
          }
        }
        return uniqueInstance;
      }
    }

Definition:


The Singleton pattern ensures a class has only one instance, and provides a global point of access to it.

Singleton Pattern


Motivation

  • It's important for some classes to have exactly one instance.
  • A global variable makes an object accessible, but it doesn't keep you from instantiating multiple objects.
  • A better solution is to make the class itself responsible for keeping track of its sole instance.

Singleton Pattern

Class Structure


  • Singleton is responsible for instantiating itself.
  • Singleton provides a global point of access to that instance.

Singleton Pattern


Applicability

  • There must be exactly one instance of a class, and it must be accessible to clients from a well-known access point.
  • When the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.

Singleton Pattern


Consequences

  • Controlled access to a sole instance
  • Reduced name space
  • Permits a variable number of instances

Let's look at the big picture...

Importance of a Shared Vocabulary

  • Design Patterns are important because they provide a shared vocabulary to software design.
    Compare:
    • So I created this broadcast class. It tracks a set of listeners and anytime its data changes, it sends a message to the listeners. Listeners can join and leave at any time. It's really dynamic and loosely-coupled.
    • I used the Observer Design Pattern.

Importance of a Shared Vocabulary

  • Shared pattern vocabularies are powerful. You communicate not just a name but a whole set of qualities, services, and constraints associated with the pattern.
  • Paterns allow you to say more with less. Other developers quickly pick up on the design you are proposing.
  • Talking about the patterns let's you "stay in the design" longer. You don't have to jump into the nitty-gritty details.

How to Select a Design Pattern

  • Consider how design patterns solve design problems
  • Find a pattern whose intent is relevant to your problem
  • Study how patterns interrelate
  • Study patterns of like purpose
  • Examine a cause of redesign
  • Consider what should be variable in your design

Classification

Design patterns have been grouped into categories:

  • Creational patterns:
    • deal with object creation mechanisms, trying to create objects in a manner suitable to the situation
  • Structural patterns:
    • ease the design by identifying a simple way to realize relationships between entities
  • Behavioral patterns:
    • identify common communication patterns between objects and realize these patterns

Wrapping Up


Create flexible designs that are maintainable and that can cope with change!


Design patterns allow you to exploit the wisdom and lessons learned by other developers who've encountered design problems similar to the ones you are encountering.

Wrapping Up


There are tons of design patterns!


A short list: Abstract Factory, Builder, Factory Method, Prototype, Singleton, Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor

Wrapping Up


It is my hope that this exposure to design patterns will allow you to become outstanding software engineers!


While I've only given you a (very) brief look into the world of design patterns, you now have some of the best tools to create more flexible and maintainable software.

Wrapping Up


I'd be happy to come back and talk about some of the other popular design patterns you'll find out in the wild!


But in the meantime...

More Great Resources

Thanks for coming out!


A reminder that these slides can be found at...


http://MichaelNaper.com/talks/designpatterns1.html