Peter Bunus Department of Computer and Information Science Linköping University, Sweden peter.bunus@liu.se
TDDB84 Design Patterns Lecture 09 State, Prototype, Visitor, - - PowerPoint PPT Presentation
TDDB84 Design Patterns Lecture 09 State, Prototype, Visitor, - - PowerPoint PPT Presentation
TDDB84 Design Patterns Lecture 09 State, Prototype, Visitor, Flyweight Peter Bunus Department of Computer and Information Science Linkping University, Sweden peter.bunus@liu.se State TDDB84 Design Patterns Slide 2 State Non Software
TDDB84 Design Patterns Slide 2
State
TDDB84 Design Patterns Slide 3
State – Non Software Example
TDDB84 Design Patterns Slide 4
The Gumball Vending Machine
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs
TDDB84 Design Patterns Slide 5
Writting Code
public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; final static int HAS_QUARTER = 2; final static int SOLD = 3; int state = SOLD_OUT; int count = 0; public GumballMachine(int count) { this.count = count; if (count > 0) { state = NO_QUARTER; } }
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs
TDDB84 Design Patterns Slide 6
public void insertQuarter() { if (state == HAS_QUARTER) { System.out.println("You can't insert another quarter"); } else if (state == NO_QUARTER) { state = HAS_QUARTER; System.out.println("You inserted a quarter"); } else if (state == SOLD_OUT) { System.out.println("You can't insert a quarter, the machine is sold out"); } else if (state == SOLD) { System.out.println("Please wait, we're already giving you a gumball"); } }
Writting the Code
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs
TDDB84 Design Patterns Slide 7
public void ejectQuarter() { if (state == HAS_QUARTER) { System.out.println("Quarter returned"); state = NO_QUARTER; } else if (state == NO_QUARTER) { System.out.println("You haven't inserted a quarter"); } else if (state == SOLD) { System.out.println("Sorry, you already turned the crank"); } else if (state == SOLD_OUT) { System.out.println("You can't eject, you haven't inserted a quarter yet"); } }
Writting the Code
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs
TDDB84 Design Patterns Slide 8
Testing the GumBall Machine
public class GumballMachineTestDrive { public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(5); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.ejectQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); } }
TDDB84 Design Patterns Slide 9
You expected it. Isn’t it?
Joe, we need to reward somehow customers that are constantly buying gums from our machines. Let’s implement reward system that gives you a free gum if you already purchased 10 gums.
TDDB84 Design Patterns Slide 10
What we need to do
public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; final static int HAS_QUARTER = 2; final static int SOLD = 3; int state = SOLD_OUT; int count = 0; public void insertQuarter(){ //insert quarter code here } public void ejectQuarter(){ //insert quarter code here } public void turnCrank(){ //insert quarter code here } public void dispense(){ //insert quarter code here } }
You have to add a new WINNER state here ...but then, you’d have to add a new conditional in every single method to handle the WINNER state, that’s a lot of code to modify turnCrank() will get especially messy, because you’d have to add code to check to see whether you’ve got a winner and then switch to either the WINNER state or SOLD state
TDDB84 Design Patterns Slide 11
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs
+insertQuarter() +ejectQuarter() +turnCrank() +dispense() «interface» State +insertQuarter() +ejectQuarter() +turnCrank() +dispense() SoldState +insertQuarter() +ejectQuarter() +turnCrank() +dispense() SoldOutState +insertQuarter() +ejectQuarter() +turnCrank() +dispense() NoQuarterState +insertQuarter() +ejectQuarter() +turnCrank() +dispense() HasQuarterState
Redesigning the Gumball Machine
TDDB84 Design Patterns Slide 12
+insertQuarter() +ejectQuarter() +turnCrank() +dispense() NoQuarterState
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs
Go to HasQuarterState Tell the customer: “You haven’t inserted a quarter” Tell the customer: “You turned, but there's no quarter” Tell the customer: “You need to pay first”
NoQuarterState
TDDB84 Design Patterns Slide 13
NoQuarterState
public class NoQuarterState implements State { GumballMachine gumballMachine; public NoQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("You inserted a quarter"); gumballMachine.setState(gumballMachine.getHasQuarterState()); } public void ejectQuarter() { System.out.println("You haven't inserted a quarter"); } public void turnCrank() { System.out.println("You turned, but there's no quarter"); } public void dispense() { System.out.println("You need to pay first"); } public String toString() { return "waiting for quarter"; } }
TDDB84 Design Patterns Slide 14
The State Gumball Machine
public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; final static int HAS_QUARTER = 2; final static int SOLD = 3; int state = SOLD_OUT; int count = 0; public class GumballMachine { State soldOutState; State noQuarterState; State hasQuarterState; State soldState; State state = soldOutState; int count = 0;
TDDB84 Design Patterns Slide 15
The State Gumball Machine
public class GumballMachine { State soldOutState; State noQuarterState; State hasQuarterState; State soldState; State state = soldOutState; int count = 0; public GumballMachine(int numberGumballs) { soldOutState = new SoldOutState(this); noQuarterState = new NoQuarterState(this); hasQuarterState = new HasQuarterState(this); soldState = new SoldState(this); this.count = numberGumballs; if (numberGumballs > 0) { state = noQuarterState; } } public void insertQuarter() { state.insertQuarter(); } public void ejectQuarter() { state.ejectQuarter(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void setState(State state) { this.state = state; } void releaseBall() { System.out.println("A gumball comes rolling out the slot..."); if (count != 0) { count = count - 1; }
}
TDDB84 Design Patterns Slide 16
The State Design Pattern
The Context is the class that can have a number
- f internal states.
The State interface defines common interface for all concrete states; the states all implement the same interface, so they are interchangeable Whenever the request() is made on the Context it is delegated to the state to handle Each concrete class provides its own implementation for a request. In this way, when the Context changes state, its behavior changes as well
TDDB84 Design Patterns Slide 17
How about my extra gumball?
No Quarter Has Quarter Inserts quarter ejects quarter Gumball sold Out of gumballs gumballs=0 gumballs>0 turns crank dispense gumballs Winner
winner=0 && gumballs>1 turns crank
gumballs=0 gumballs>0 dispense 2 gumballs
TDDB84 Design Patterns Slide 18
The Winner State
public class WinnerState implements State { GumballMachine gumballMachine; public WinnerState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("Please wait, we're already giving you a Gumball"); } public void ejectQuarter() { System.out.println("Please wait, we're already giving you a Gumball"); } public void turnCrank() { System.out.println("Turning again doesn't get you another gumball!"); } public void dispense() { System.out.println("YOU'RE A WINNER! You get two gumballs for your quarter"); gumballMachine.releaseBall(); if (gumballMachine.getCount() == 0) { gumballMachine.setState(gumballMachine.getSoldOutState()); } else { gumballMachine.releaseBall(); if (gumballMachine.getCount() > 0) { gumballMachine.setState(gumballMachine.getNoQuarterState()); } else { System.out.println("Oops, out of gumballs!"); gumballMachine.setState(gumballMachine.getSoldOutState()); } } }
TDDB84 Design Patterns Slide 19
HasQuarterState
public class HasQuarterState implements State { Random randomWinner = new Random(System.currentTimeMillis()); GumballMachine gumballMachine; public HasQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("You can't insert another quarter"); } public void ejectQuarter() { System.out.println("Quarter returned"); gumballMachine.setState(gumballMachine.getNoQuarterState()); } public void turnCrank() { System.out.println("You turned..."); int winner = randomWinner.nextInt(10); if ((winner == 0) && (gumballMachine.getCount() > 1)) { gumballMachine.setState(gumballMachine.getWinnerState()); } else { gumballMachine.setState(gumballMachine.getSoldState()); } } public void dispense() { System.out.println("No gumball dispensed"); }
TDDB84 Design Patterns Slide 20
Gumball Machine TestDrive
public class GumballMachineTestDrive { public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(10); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); .... } }
TDDB82 Design Patterns
21
Prototype
TDDB82 Design Patterns
22
Prototype – Non Software Example
TDDB82 Design Patterns
23
Motivational Example
TDDB82 Design Patterns
24
The Prototype Pattern
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype
TDDB82 Design Patterns
25
Motivational Example - Dragons
TDDB82 Design Patterns
26
The Dragon Classes
class DragonPrototype{ protected: string name; public: DragonPrototype(){}; DragonPrototype(const DragonPrototype& copy); ~DragonPrototype(){}; virtual DragonPrototype* clone() const; void setName(string name); string getName() const; }; class MultipleHeadedDragon : public DragonPrototype{ protected: int numberOfHeads; public: MultipleHeadedDragon(){}; MultipleHeadedDragon(const MultipleHeadedDragon& copy); ~MultipleHeadedDragon(); virtual DragonPrototype* clone() const; void setNumberOfHeads(int noHeads); int getNumberofHeads(); };
TDDB82 Design Patterns
27
The Dragon Classes
DragonPrototype::DragonPrototype(const DragonPrototype& copy){ name = copy.name; } void DragonPrototype::setName(string p_name){ name = p_name; } DragonPrototype* DragonPrototype::clone() const{ return new DragonPrototype(*this); }
MultipleHeadedDragon::MultipleHeadedDragon(const MultipleHeadedDragon ©): DragonPrototype(copy){ numberOfHeads = copy.numberOfHeads; } DragonPrototype* MultipleHeadedDragon::clone() const{ return new MultipleHeadedDragon(*this); } void MultipleHeadedDragon::setNumberOfHeads(int noHeads){ numberOfHeads = noHeads; };
TDDB82 Design Patterns
28
The Dragon Client
cout << "Testing the dragon prototypes" << endl;
MultipleHeadedDragon *dragonMH = new MultipleHeadedDragon(); dragonMH->setName("Ziggy"); DragonPrototype *anotherDragon = dragonMH->clone(); anotherDragon->setName("Zippy"); dragonMH->setNumberOfHeads(7);
TDDB82 Design Patterns
29
The Tree Stooges
class Stooge { public: virtual void slap_stick() = 0; };
class Larry: public Stooge { public: void slap_stick(){ cout << "Larry: poke eyes\n"; } }; class Moe: public Stooge{ public: void slap_stick(){ cout << "Moe: slap head\n"; } }; class Curly: public Stooge{ public: void slap_stick(){ cout << "Curly: suffer abuse\n"; } };
TDDB82 Design Patterns
30
The Tree Stooges
int main(void) { vector roles; int choice; while (true) { cout << "Larry(1) Moe(2) Curly(3) Go(0): "; cin >> choice; if (choice == 0) break; else if (choice == 1) roles.push_back(new Larry); else if (choice == 2) roles.push_back(new Moe); else roles.push_back(new Curly); } for (int i = 0; i < roles.size(); i++) roles[i]->slap_stick(); for (int i = 0; i < roles.size(); i++) delete roles[i]; } Larry(1) Moe(2) Curly(3) Go(0): 2 Larry(1) Moe(2) Curly(3) Go(0): 1 Larry(1) Moe(2) Curly(3) Go(0): 3 Larry(1) Moe(2) Curly(3) Go(0): 0 Moe: slap head Larry: poke eyes Curly: suffer abuse
TDDB82 Design Patterns
31
The Tree Stooges – Prototype Variant
class Stooge { public: virtual Stooge* clone() = 0; virtual void slap_stick() = 0; }; class Larry: public Stooge { public: Stooge* clone() {return new Larry;} void slap_stick(){ cout << "Larry: poke eyes\n"; } }; class Moe: public Stooge{ public: Stooge* clone() { return new Moe(*this);} void slap_stick(){ cout << "Moe: slap head\n"; } }; class Curly: public Stooge{ public: Stooge* clone() {return new Curly;} void slap_stick(){ cout << "Curly: suffer abuse\n"; } };
TDDB82 Design Patterns
32
The Tree Stooges – Prototype Variant
class Factory { public: static Stooge* make_stooge( int choice ); private: static Stooge* s_prototypes[4]; }; Stooge* Factory::s_prototypes[] = { 0, new Larry, new Moe, new Curly }; Stooge* Factory::make_stooge( int choice ) { return s_prototypes[choice]->clone(); }
TDDB82 Design Patterns
33
The Tree Stooges – Prototype Variant
int main( void ) { vector roles; int choice; while (true) { cout << "Larry(1) Moe(2) Curly(3) Go(0): "; cin >> choice; if (choice == 0) break; roles.push_back( Factory::make_stooge( choice ) ); } for (int i=0; i < roles.size(); ++i) roles[i]->slap_stick(); for (int i=0; i < roles.size(); ++i) delete roles[i]; } Larry(1) Moe(2) Curly(3) Go(0): 2 Larry(1) Moe(2) Curly(3) Go(0): 1 Larry(1) Moe(2) Curly(3) Go(0): 3 Larry(1) Moe(2) Curly(3) Go(0): 0 Moe: slap head Larry: poke eyes Curly: suffer abuse
TDDB82 Design Patterns
34
Prototype Pattern – Interaction Diagram
TDDB82 Design Patterns
35
- The prototype pattern is used when a system should be independent of
how its products are created, composed, and represented; and
- when the classes to instantiate are specified at run-time; for example, the dynamic
loading
- to avoid building a class hierarchy of factories that parallels the class hierarchy of
products; or
- when instances of a class can have one of only a few different combinations of state
Applicability
TDDB82 Design Patterns
36
Consequences
- Isolating concrete product classes from the client.
- Dynamically adding and removing products at run-time.
- Specifying new objects by varying values.
- Specifying new objects by varying structure.
- Reducing the need for sub-classing.
- Configuring an application with classes dynamically.
- Main liability: Clone() needed.
TDDB82 Design Patterns
37
Visitor
TDDB82 Design Patterns
38
Visitor – Non Software Example
TDDB82 Design Patterns
39
Visitor Pattern Structure
TDDB82 Design Patterns
40
Motivational Example
- Asssume that we have a binary tree with an integer in each node and
each leaf
- We want:
- To print all the numbers
- Compute their sum using separate methods
5 3 2 7 5
TDDB82 Design Patterns
41
Java Tree Implementation
- n : int
Tree Leaf Node 1
abstract class Tree { protected int n; abstract void print(); abstract int sum(); } class Node extends Tree { private Tree left, right; void print() { left.print(), System.out.print(n); right.print(); } int sum() { return left.sum() + n + right.sum(); } } class Leaf extends Tree { void print() { System.out.print(n); } int sum() { return n; } }
TDDB82 Design Patterns
42
Tree Visitor
class Leaf extends Tree { void accept(TreeVisitor visitor) { visitor.visit(this); } } interface TreeVisitor { void visit(Node node); void visit(Leaf leaf); } abstract class Tree { protected int n; abstract void accept(TreeVisitor visitor); } class Node extends Tree { private Tree left, right; void accept(TreeVisitor visitor) { left.accept(visitor), visitor.visit(this); right.accept(visitor); } }
Each tree class must have an accept method
TDDB82 Design Patterns
43
Tree Print Visitor
interface TreeVisitor { void visit(Node node); void visit(Leaf leaf); } class PrintVisitor extends TreeVisitor { public void visit(Node node) { System.out.print(node.getN()); } public void visit(Leaf leaf) { // Usually the visit methods differ. System.out.print(leaf.getN()); } } TreeVisitor visitor = new PrintVisitor(); Tree tree = ... tree.accept(visitor);
The Client
TDDB82 Design Patterns
44
The Sum Visitor
interface TreeVisitor { void visit(Node node); void visit(Leaf leaf); } class SumVisitor implements TreeVisitor { int sum = 0; public void visit(Node node) { sum += node.getN(); } public void visit(Leaf leaf) { sum += leaf.getN(); } TreeVisitor v = new SumVisitor(); Tree tree = ... tree.accept(visitor);
The Client
TDDB82 Design Patterns
45
Visitor – Benefits/Drawbacks
- Allows you to add operations to a Composite structure without changing
the structure itself
- Adding new operations is relativelly easy
- The code for operations performed by the Visitor is centralized
- The Composite classes’ encapsulation is broken when the Visitor is
used
- Because the traversal function is involved, changes to Composite are
more difficult.
TDDB82 Design Patterns
46
Flyweight
TDDB82 Design Patterns
48
The FlyWeight Design Pattern
- The Flyweight pattern describes how to share objects to allow their use at
fine granularities without prohibitive cost.
- A flyweight" object has
– state-dependent (extrinsic) part (stored or computed by client objects) – state-independent (intrinsic) part (stored in the Flyweight)
TDDB82 Design Patterns
50
Flyweight
TDDB82 Design Patterns
51
Flyweight Example
public interface IAlien { string Shape { get; } //intrinsic state Color GetColor(int madLevel); //extrinsic state } public class LargeAlien : IAlien { private string shape = "Large Shape"; //intrinsic state string IAlien.Shape{ get { return shape; } } Color IAlien.GetColor(int madLevel) //extrinsic state { if (madLevel == 0) return Color.Green; else if (madLevel == 1) return Color.Red; else return Color.Blue; } } public class LittleAlien : IAlien { private string shape = "Little Shape"; //intrinsic state string IAlien.Shape{ get { return shape; } } Color IAlien.GetColor(int madLevel) //extrinsic state { if (madLevel == 0) return Color.Red; else if (madLevel == 1) return Color.Blue; else return Color.Green; } }
TDDB82 Design Patterns
52
Flyweight Example
public class AlienFactory { private Dictionary<int,> list = new Dictionary<int,>(); public void SaveAlien(int index, IAlien alien){ list.Add(index, alien); } public IAlien GetAlien(int index){ return list[index]; } }
TDDB82 Design Patterns
53 class Program { static void Main(string[] args) { //create Aliens and store in factory AlienFactory factory = new AlienFactory(); factory.SaveAlien(0, new LargeAlien()); factory.SaveAlien(1, new LittleAlien()); //now access the flyweight objects IAlien a = factory.GetAlien(0); IAlien b = factory.GetAlien(1); //show intrinsic states, all accessed in memory without calculations Console.WriteLine("Showing intrinsic states..."); Console.WriteLine("Alien of type 0 is " + a.Shape); Console.WriteLine("Alien of type 1 is " + b.Shape); //show extrinsic states, need calculations Console.WriteLine("Showing extrinsic states..."); Console.WriteLine("Alien of type 0 is " + a.GetColor(0).ToString()); Console.WriteLine("Alien of type 0 is " + a.GetColor(1).ToString()); Console.WriteLine("Alien of type 1 is " + b.GetColor(0).ToString()); Console.WriteLine("Alien of type 1 is " + b.GetColor(1).ToString()); } }
TDDB82 Design Patterns
54
The Constitution of Software Architects
Encapsulate what varies Program through an interface not to an implementation Favor Composition over Inheritance Classes should be open for extension but closed for modification Don’t call us, we’ll call you A Class should have only one reason to change Depend upon abstractions. Do not depend upon concrete classes. Strive for loosely coupled designs between
- bjects that interact
Only talk to your friends
TDDB82 Design Patterns
55
Seven Layers of Architecture
Objects Micro-Architecture Macro-Architecture Application-Architecture System-Architecture Enterprise-Architecture Global-Architecture Design-Patterns OO Programming Frameworks Subsystem OO Architecture
TDDB82 Design Patterns
56
Antipatterns Sources
TDDB82 Design Patterns
57