1 Dept. of Computer Science & Engineering, York University, Toronto CSE3311 Software Design State...

21
1 Dept. of Computer Science & Engineering, York University, Toronto CSE3311 Software Design State Pattern

Transcript of 1 Dept. of Computer Science & Engineering, York University, Toronto CSE3311 Software Design State...

1Dept. of Computer Science & Engineering, York University, Toronto

CSE3311 Software Design

State Pattern

2

Problem

You are developing a program to keep track of movies for a video store. There are three movie types: regular, children’s, and new releases.

The charge for a movie will depend on the length of the rental and its type. Also, customers receive a number of frequent renter points that depend on the type of the movie rented.

We need the ability to calculate the price and frequent renter points that accrue to a customer renting a movie. Develop a BON diagram demonstrating the classes you might include in this system along with their relationships. Do not be concerned with user interface classes.

3

First Design

class MOVIE1 createmake

feature {NONE} – Initializationmake(n,t: STRING) isdo

name := ntype := t

end

featurename: STRING

type: STRING

4

First Design

price(days_rented: INTEGER): REAL isrequire

positive_days: days_rented >= 0do

if type.is_equal("REG") thenResult := 2if days_rented > 2 then

Result := Result + (days_rented - 2) * 1.5end

elseif type.is_equal("NEW") thenResult := days_rented * 3

elseif type.is_equal("CHI") thenResult := 1.5if days_rented > 3 then

Result := Result + (days_rented - 3) * 1.5end

endend

5

First Designpoints: INTEGER -- frequent renter points do if type.is_equal("REG") then

Result := 100elseif type.is_equal("NEW") then

Result := 200elseif type.is_equal("CHI") then

Result := 150end

end

set_type(t: STRING)do

type := t.twinend

end -- class MOVIE1

How will we change the state from NEW to REG?

6

First Design test class

makelocal

m1, m2, m3: MOVIE1do

-- Design 1: It works but changes are very cumbersome

print("Design1%N")create m1.make("Casablanca", "REG")create m2.make("Bambi", "CHI")create m3.make("The Lord of the Rings", "NEW")print (m1.price(2).out + "%N")print (m2.price(1).out + "%N")print (m3.price(5).out + "%N")print (m3.points.out + " points%N")m3.set_type ("REG")print("The new price for The Lord of the Rings is: ")print (m3.points.out + " points%N%N")

end

7

Critique?

Suppose we need to add a new state ADVENTURE_MOVIE?

price is calculated differently frequent renter points is calculated different

Will need to change multiple places (price and points routine logic)

there may be many more type dependent routines such as special_discount etc.

Single Choice Principle Violated

8

Design Two

price*points*

• Change state (e.g.state ”reg”, “chi”) from class STRING to a class MOVIE2 • Use polymorphism and dynamic binding to delegate calculation of price and points to MOVIE2• Adding a new state is easy• Removes if … elseif … elseif … end statements in class MOVIE1

9

Second Design test class make is

-- Creation procedure.local

m1, m2, m3: MOVIE1m4, m5, m6: MOVIE2

do-- Design 1: It works but changes are very cumbersome-- Design2: Takes advantage of polymorphism

print("Design2%N")create {REGULAR} m4.make("Casablanca")create {CHILDRENS} m5.make("Bambi")create {NEW} m6.make("The Lord of the Rings")print (m4.price(2).out + "%N")print (m5.price(1).out + "%N")print (m6.price(5).out + "%N")print (m6.points.out + " points%N")

print("How to change 'Lord of Rings' to regular???%N%N")end

Position of Single Choice

Delegate price & points to MOVIE2(polymorphism + dynamic binding)

10

Final Design!

price*points*

pricepointsset_type

Delegate price & points to MOVIE_STATE

• Dynamic change of the state• Single choice happens here

11

Final Designclass MOVIE3 create

makefeature {NONE} – Initialization

make(n,t: STRING) isdo name := n; set_type(t) end

featurename: STRINGtype: MOVIE_STATE

set_type (t: STRING)do

if t.is_equal("NEW") then create {NEW_STATE} typeelseif t.is_equal("REG") then create {REGULAR_STATE} typeelseif t.is_equal("CHI") then create {CHILDRENS_STATE} typeend

end

price (days_rented: INTEGER): REAL isdo Result := type.price(days_rented) end

points: INTEGERdo Result := type.points end

end

Single Choice

Delegate price & points to MOVIE_STATE

12

Final Design test class Design 1: It works but changes are very cumbersome Design2: Takes advantage of polymorphism, but what if "The Lord of the Rings" is no longer a NEW movie? Design 3: Polymorphism is now delegated to the state object. Adding a new state is trivial make

localm7, m8, m9: MOVIE3

docreate m7.make("Casablanca", "REG")create m8.make("Bambi", "CHI")create m9.make("The Lord of the Rings", "NEW")print (m7.price(2).out + "%N")print (m8.price(1).out + "%N")print (m9.price(5).out + "%N")print (m9.points.out + " points%N")m9.set_type ("REG")print("The new price for The Lord of the Rings is: ")print (m9.price(5).out + "%N")print (m9.points.out + " points%N")

end

13

State Pattern: allows an object to alter its behaviour when its internal state changes. The object will appear to change its class

By encapulating each state we localize any changes that will need to be made (e.g. price and points) to that state (behaviour)

14

15

16

State vs. Strategy

The class diagrams are similar but they differ in intent

State Behaviours are constantly changing over time and the client

(context) knows very little about how those different behaviours work

Encapsulate behaviours in state objects and set change in the context

Alternative to putting a lot of conditionals in the context

Strategy Client knows quite a lot about what behaviour (state) is most

appropriate e.g. we know that a mallard duck has typical flying behaviour and a decoy duck never flies

Change in state less usual Flexible alternative to subclassing

17

18

19

adding new states

20

21