[go: up one dir, main page]

0% found this document useful (0 votes)
27 views50 pages

01 IntroToDesigPatterns

The document discusses the Strategy Pattern in software engineering, focusing on designing flexible applications through behavior encapsulation. It illustrates the issues with inheritance in a Duck application and proposes using interfaces and composition to manage behaviors like flying and quacking dynamically. The document emphasizes the benefits of the Strategy Pattern, including eliminating conditional statements and allowing for interchangeable algorithms.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views50 pages

01 IntroToDesigPatterns

The document discusses the Strategy Pattern in software engineering, focusing on designing flexible applications through behavior encapsulation. It illustrates the issues with inheritance in a Duck application and proposes using interfaces and composition to manage behaviors like flying and quacking dynamically. The document emphasizes the benefits of the Strategy Pattern, including eliminating conditional statements and allowing for interchangeable algorithms.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 50

Software Engineering II

01- Introduction to Design Patterns


Strategy Pattern
How to design for flexibility?
Existing Duck application
All ducks quack and swim.
The superclass takes care Duck
of the implementation code quack()
swim() The display() method is
abstract, since all duck
display()
subtypes look different
//other duck-like methods…

Each duck subtype is


responsible for implementing
its own display() method

MallardDuck RedHeadDuck
Other duck types inherit
display() { display() { from the Duck class
// looks like a mallard} // looks like a redhead } ...

2
Testing Mallard, RedHeadDuck classes

3
Changing Requirment
• No sweat!
• Add a method fly() in Duck
• Continue to use inheritance
Duck
quack()
Add a method fly() in Duck swim()
display()

All subclasses fly()


inherit fly() //other duck-like methods…

MallardDuck RedHeadDuck

display() { display() {

// looks like a mallard} // looks like a redhead }


4
Executing

5
Something seriously wrong!

Duck
quack()
swim()
display()
All duck types now
can fly including fly()
RubberDuck
//other duck-like methods…

RubberDuck
MallardDuck ReadHeadDuck quack() {
display() { // overridden to Squeak }
display() {
display() {
// looks like a mallard} // looks like a redhead }
// looks like a rubberduck
}
Strategy Pattern 6
Executing … What?

7
Root cause?

• Applying inheritance to achieve re-use


• Poor solution for maintenance

How do we fix this?


• Using inheritance as before
• Override the fly() method in rubber duck as in quack()

8
Executing

Strategy Pattern 9
Is the problem solved?
• Any new problems?

Wait a minute
• How about new duck types?
• Decoy duck?
• Can’t quack
• Can’t fly
• How do we solve it?

10
Summary

• What have we done so far?


• What problems have we solved?

• What problems have we introduced in solving the problems?

• Is there a better way of doing things?

11
How about Interface?
• Take the fly() method out of Duck superclass
• And make a Flyable() interface
• Only those ducks that fly are required to implement the interface
• Make a Quackable interface too

interface Flyable Interface Quackable class Duck


quack() swim()
fly()
display()

//other duck-like methods…

MallardDuck RedHeadDuck RubberDuck


display() display() {
display()
fly() fly() quack()

quack() quack()
12
But

• You shoot yourself in the foot by duplicating code for every duck type
that can fly and quack!

• And we have a lot of duck types

• We have to be careful about the properties – we cannot just call the


methods blindly
• We have created a maintenance nightmare!

13
Re-thinking:

• Inheritance has not worked well because


• Duck behavior keeps changing
• Not suitable for all subclasses to have those properties

• Interface was at first promising, but


• No code re-use
• Tedious
• Every time a behavior is changed, you must track down and change it in
all the subclasses where it is defined
• Error prone

14
#1 Design Principle

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

• So what are variable in the Duck class?


• Flying behavior
• Quacking behavior

• Pull these duck behaviors out of the Duck class


• Create new classes for these behaviors

15
How do we design the classes to implement the fly and
quack behaviors?

• Goal: to keep things flexible


• Want to assign behaviors to instances of Duck
• Instantiate a new MallardDuck instance
• Initialize it with a specific type of flying
• Be able to change the behavior dynamically

16
#2 Design Principle
• Program to a supertype, not an implementation
• Use a supertype to represent each behavior
• FlyBehavior and QuackBehavior
• Each implementation of a behavior will implement one of
these supertypes
• In the past, we rely on an implementation
• In superclass Duck, or
• A specialized implementation in the subclass
• Now: Duck subclass will use a behavior represented in a supertype.

17
Strategy Pattern 18
classes in code
public interface FlyBehavior {
public void fly();
}

public class FlyWithWings implements


FlyBehavior { public void fly() {
System.out.println("I'm flying!!");
}
}

public class FlyNoWay implements


FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
} 19
public interface QuackBehavior {
public void quack();
}

20
Specific behaviors by implementing interface QuackBehavior
public class Quack implements QuackBehavior { public void quack() {
System.out.println("Quack");
}
}

public class Squeak implements QuackBehavior { public void quack()


{
System.out.println("Squeak");
}
}

public class MuteQuack implements QuackBehavior { public void quack()


{
System.out.println("<< Silence >>");
}
} 21
22
Integrating the Duck Behavior
1. Add 2 instance variables:
Behavior Duck Instance
variables are variables hold
declared as the FlyBehavior flyBehavior a reference to
behavior QuackBehavior quackBehavior a specific
SUPERTYPE behavior at
performQuack() runtime
Swim()
These general
methods Display()
replace fly() and performFly()
quack()
//OTHER duck-like methods

Strategy Pattern

23
2. Implement performQuack()

public abstract class Duck {


// Declare two reference variables for the behavior interface types
FlyBehavior flyBehavior;
QuackBehavior quackBehavior; // All duck subclasses inherit these
// etc

public Duck(FlyBehavior f, QuackBehavior q) {


}

public Duck() {
}

public void performQuack() {


quackBehavior.quack(); // Delegate to the behavior class
}

24
25
3. How to set the quackBehavior variable & flyBehavior variable

public class MallardDuck extends Duck {

public MallardDuck() {

quackBehavior = new Quack();


// A MallardDuck uses the Quack class to handle its quack,
// so when performQuack is called, the responsibility for the quack
// is delegated to the Quack object and we get a real quack

flyBehavior = new FlyWithWings();


// And it uses flyWithWings as its flyBehavior type

public void display() {


System.out.println("I'm a real Mallard duck");
}
Strategy Pattern 26
Strategy Pattern 27
Testing the Duck code

Type and compile:

• Duck class and the MallardDuck class

• FlyBehavior interface and the two behavior implementation




classes (FlyWithwings.java and flyNoWay.java)
QuackBehavior interface and 3 behavior implementation classes
Test class (MiniDuckSimulator.java)

Strategy Pattern 28
// 1. Duck class
public abstract class Duck {
// Reference variables for the behavior interface types
FlyBehavior flyBehavior;
QuackBehavior quackBehavior; // All duck subclasses inherit these

public Duck() { }
Is it possible to manage
abstract void display(); all duck’s sub-object
with this super type?
public void performFly() {
flyBehavior.fly(); // Delegate to the behavior class
}

public void performQuack() {


quackBehavior.quack(); // Delegate to the behavior class
}

public void swim() {


System.out.println("All ducks float, even decoys!");
}
29
2. FlyBehavior and two behavior implementation classes

public interface FlyBehavior {


public void fly();
}

public class FlyWithWings implements FlyBehavior {


public void fly() {
System.out.println("I'm flying!!");
}
}

public class FlyNoWay implements FlyBehavior {


public void fly() {
System.out.println("I can't fly");
}
}

30
// 3. QuackBehavior interface and 3 behavior implementation classes

public interface QuackBehavior { public void quack();


}

public class Quack implements QuackBehavior { public void quack() {


System.out.println("Quack");
}
}

public class Squeak implements QuackBehavior { public void quack() {


System.out.println("Squeak");
}
}

public class MuteQuack implements QuackBehavior { public void quack() {


System.out.println("<< Silence >>");
}
}
31
4. Type and compile the test class
(MiniDuckSimulator.java)
public class MiniDuckSimulator {

public static void main(String[] args) {

Duck mallard = new MallardDuck();


mallard.performQuack();
// This calls the MallardDuck's inherited performQuack() method,
// which then delegates to the object's QuackBehavior
// (i.e. calls quack() on the duck's inherited quackBehavior
// reference)
mallard.performFly();
// Then we do the same thing with MallardDuck's inherited
// performFly() method.
}
}
Strategy Pattern 32
At the end: Strategy project

33
Check-in
• We have built dynamic behavior in ducks e.g. a MallardDuck
• The dynamic behavior is instantiated in the duck’s constructor
• How can we change the duck’s behavior after instantiation?

Changing a duck’s behavior after instantiation


• Set the duck’s behavior type through a mutator method on the duck’s subclass

34
How to set behavior dynamically?
1. Add new methods to the Duck class
public void setFlyBehavior (FlyBehavior fb) {
flyBehavior = fb;
}

public void setQuackBehavior(QuackBehavior qb) {


quackBehavior = qb;
}

Strategy Pattern 35
2. Make a new Duck type (ModelDuck.java)
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay();
// Model duck has no way to fly
quackBehavior = new Quack();
}

public void display() {


System.out.println("I'm a model duck");
}
}
Strategy Pattern 36
Enabling ModelDuck to fly

• Use a mutator (setter) method to enable ModelDuck to fly 37


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

public class FlyRocketPowered implements FlyBehavior {

public void fly() {


System.out.println("I'm flying with a rocket");
}
}

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

Duck model = new ModelDuck();


model.performFly();
// call to performFly() delegates to the flyBehavior
// object set in ModelDuck's constructor
model.setFlyBehavior(new FlyRocketPowered());
// change the duck's behavior at runtime by
// invoking the model's inherited behavior setter
// method
model.performFly();
39
40
Big Picture on encapsulated behaviors
Reworked class structure
Encapsulated fly behavior

Duck
FlyBehavior flyBehavior
QuackBehavior quackBehavior
Swim()
Display()
performQuack()
performFly()
setFlyBehavior()

setQuackbehavior()
Encapsulated quack behavior
//OTHER duck-like methods

MallardDuck RedHeadDuck RubberDuck


display() display() display()

Strategy Pattern 42
HAS-A can be better than IS-A

• Each duck has a FlyBehavior and a QuackBehavior to which it


delegates flying and quacking.
• Composition at work
• Instead of inheriting behavior, ducks get their behavior by being
composed with the right behavior object.

43
#3 Design Principle

• Favor composition over inheritance


• More flexibility
• Encapsulate a family of algorithms into their own set of classes
• Able to change behavior at runtime

Strategy Pattern 44
Strategy

In a Strategy design pattern, you will:

• Define a family of algorithms

• Encapsulate each one

• Make them interchangeable

45
You should use Strategy when:

• You have code with a lot of algorithms


• You want to use these algorithms at different times

• You have algorithm(s) that use data the client should not know about

46
Strategy Class Diagram

Context Strategy
contextInterface() algorithmInterface()

ConcreteStrategyA ConcreteStrategyB ConcreteStrategyC


algorithmInterface() algorithmInterface() algorithmInterface()

47
Strategy makes this easy!

StrategyX
functionX()

Class
functionX()
functionY()
...
StrategyY
functionY()

...
48
Benefits of Strategy
• Eliminates conditional statements
• Can be more efficient than case statements
• Choice of implementation
• Client can choose among different implementations with different space
and time trade-offs
• Families of related algorithms
• Alternative to subclassing
• This lets you vary the algorithm dynamically, which makes it easier to
change and extend
• You also avoid complex inheritance structures
49
Strategy Pattern

• The strategy Pattern


• Defines a family of algorithms,
• Encapsulates each one,
• Makes them interchangeable.

• Strategy lets the algorithm vary independently from clients that use it

50

You might also like