TDDB84 Design Patterns Lecture 09 State, Prototype, Visitor, - - PowerPoint PPT Presentation

tddb84 design patterns lecture 09 state prototype visitor
SMART_READER_LITE
LIVE PREVIEW

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


slide-1
SLIDE 1

Peter Bunus Department of Computer and Information Science Linköping University, Sweden peter.bunus@liu.se

TDDB84 Design Patterns Lecture 09 State, Prototype, Visitor, Flyweight

slide-2
SLIDE 2

TDDB84 Design Patterns Slide 2

State

slide-3
SLIDE 3

TDDB84 Design Patterns Slide 3

State – Non Software Example

slide-4
SLIDE 4

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

slide-5
SLIDE 5

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

slide-6
SLIDE 6

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

slide-7
SLIDE 7

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

slide-8
SLIDE 8

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); } }

slide-9
SLIDE 9

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.

slide-10
SLIDE 10

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

slide-11
SLIDE 11

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

slide-12
SLIDE 12

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

slide-13
SLIDE 13

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"; } }

slide-14
SLIDE 14

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;

slide-15
SLIDE 15

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; }

}

slide-16
SLIDE 16

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

slide-17
SLIDE 17

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

slide-18
SLIDE 18

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()); } } }

slide-19
SLIDE 19

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"); }

slide-20
SLIDE 20

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

slide-21
SLIDE 21

TDDB82 Design Patterns

21

Prototype

slide-22
SLIDE 22

TDDB82 Design Patterns

22

Prototype – Non Software Example

slide-23
SLIDE 23

TDDB82 Design Patterns

23

Motivational Example

slide-24
SLIDE 24

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

slide-25
SLIDE 25

TDDB82 Design Patterns

25

Motivational Example - Dragons

slide-26
SLIDE 26

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(); };

slide-27
SLIDE 27

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 &copy): DragonPrototype(copy){ numberOfHeads = copy.numberOfHeads; } DragonPrototype* MultipleHeadedDragon::clone() const{ return new MultipleHeadedDragon(*this); } void MultipleHeadedDragon::setNumberOfHeads(int noHeads){ numberOfHeads = noHeads; };

slide-28
SLIDE 28

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

slide-29
SLIDE 29

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"; } };

slide-30
SLIDE 30

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

slide-31
SLIDE 31

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"; } };

slide-32
SLIDE 32

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(); }

slide-33
SLIDE 33

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

slide-34
SLIDE 34

TDDB82 Design Patterns

34

Prototype Pattern – Interaction Diagram

slide-35
SLIDE 35

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

slide-36
SLIDE 36

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.
slide-37
SLIDE 37

TDDB82 Design Patterns

37

Visitor

slide-38
SLIDE 38

TDDB82 Design Patterns

38

Visitor – Non Software Example

slide-39
SLIDE 39

TDDB82 Design Patterns

39

Visitor Pattern Structure

slide-40
SLIDE 40

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

slide-41
SLIDE 41

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; } }

slide-42
SLIDE 42

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

slide-43
SLIDE 43

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

slide-44
SLIDE 44

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

slide-45
SLIDE 45

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.

slide-46
SLIDE 46

TDDB82 Design Patterns

46

Flyweight

slide-47
SLIDE 47

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)

slide-48
SLIDE 48

TDDB82 Design Patterns

50

Flyweight

slide-49
SLIDE 49

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; } }

slide-50
SLIDE 50

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]; } }

slide-51
SLIDE 51

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()); } }

slide-52
SLIDE 52

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

slide-53
SLIDE 53

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

slide-54
SLIDE 54

TDDB82 Design Patterns

56

Antipatterns Sources

slide-55
SLIDE 55

TDDB82 Design Patterns

57

Congratulations: You have now completed TDDB84