Strategy Design Pattern

Suppose you work 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.

For swimming and quacking operations Duck superclass will care of.But we defined display() method abstract to force implement each duck draw itself as what it resembles.

Untitled Diagram

So as we know ducks can fly we need to add this ability. It is time to show up OO programmer skills. Even there already implemented dozens of duck we should add this ability easily, shouldn’t we?

Untitled Diagram2(1)

Here it seems to easy right? But what if a rubber duck comes into play and it can’t (and shouldn’t) fly?

Untitled Diagram(2)

A localized update to the code caused a non-local side effect(flying rubber ducks)! So what the thought was a great use of inheritance for the purpose of reuse hasn’t turned out so well when it comes to maintenance.

Think about that if you decide to add a duck just for decoration purpose that duck will not fly and quack.

decoyduck

We realized that inheritance probably wasn’t the answer, because we just got a memo that says that it is possible to update the product every six months. We know the spec will keep changing and we’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 we need a cleaner way to have only some of the duck types fly or quack.

Interfaces comes into play..

We could take the fly() out of the duck superclass, and make a Flyable interface with a fly() method. That way, only the ducks that ore supposed to fly will implement that interface and have a fly() method… and we might as well make a Quackable, too, since not all ducks can quack.

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.

Wouldn’t it be dreamy if only there were a way to build software so that when we need to change it, we could do so with the least possible impact on the existing code? We could spend less time reworking code and more making the program do cooler things…

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!

Take what varies and “encapsulate” it so it won’t affect the rest of the code. This results fewer unintended consequences 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!

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

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, say, change the MallardDuck’s flying behavior at runtime. Given these goals, let’s look at our second design principle: Program to an interface not an implementation.

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 either came 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 out 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.

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

We have an interface that all flying classes implement. All new flaying classes just need to implement fly method.

Untitled Diagram(1)

Same thing here for the quack behavior; we have an interface that just includes a quack() method that needs to be implemented.

Untitled Diagram(4)

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

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.

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

Untitled Diagram(3)

Programming to an implementation would be:


Dog d = new Dog();

d.bark();

Declaring the object d as type of a concrete implementation of Animal (Dog class here) forces us to code to a concrete implementation.

But Programming to an interface/supertype would be:


Animal animal = new Dog();

animal.makeSound();

We know it is a dog, but now we can use the animal reference polymorphically.

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


animal = getAnimal();

animal.makeSound();

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

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

Untitled Diagram(7)

The behavior variables are declared as the behavior INTERFACE type and hold a reference to a specific behavior at runtime.

2-Now implement performQuack():


Public class Duck {

//Each duck will have this

QuackBehavior quackBehavior;

public void performQuack() {

// Rather than handling the quack behavior itself, the Duck object

//delegates that behavior to the object referenced by quackBehavior

quackBehavior.quack();

}

}

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…

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

public class MallardDuck extends Duck {

public MallardDuck() {

quackBehavior = new Quack();

flyBehavior = new FlyWithWings();

}

public void display() {

System.out.println("I'm a mallard duck");

}

}

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

“Wait a second, didn’t you say we should NOT program to an implementation? But what are we doing in that constructor? Were making a 0 new instance of a concrete Quack implementation class!”

 

Good catch, that’s exactly what we’re doing.. for now. Later 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.

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.

1- Add two methods to the Duck class to set behaviors like setFlyBehavior(FlyBehavior fb) and setQuackBehavior(QuackBehavior qb)

2- Think that we need a new duck type named ModelDuck which has no ability to fly itself till we add a rocket powered system onto it.


public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}

public void display() {
System.out.println("I'm a model duck");
}
}

3- Make a new FlyBhavior Type to support our rocket powered duck


public class FlyRocketPowered implements FlyBehavior {
public void fly() {
System.out.println("I'm flying with a rocket");
}
}

4- Now we are able to give any duck this new ability on the run:


Duck model = new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();

To change the 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.

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:
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 in design patterns.

So we have implemented our very own 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 that will surely needed.
Now that we’ve made you take the long road to apply it, here’s the formal definition of this pattern:
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.

 

 

 

Reklamlar

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s