State | slide # 1 |
...Intent | slide # 1 |
...Applicability | slide # 1 |
...Consequences | slide # 1 |
...Implementation | slide # 2 |
...Example - SPOP | slide # 3 |
......Outline of the State Pattern Implementation | slide # 7 |
......Issue: Who defines the state transitions? | slide # 10 |
......Issue: Sharing State Objects | slide # 12 |
......Storing Instance Variables Elsewhere | slide # 12 |
...Issue: Creating and Destroying State Objects | slide # 14 |
Strategy | slide # 15 |
...Intent | slide # 15 |
...Example: STL Container Adaptors | slide # 15 |
...Applicability | slide # 18 |
...Consequences | slide # 18 |
...Implementation | slide # 19 |
An object's behavior depends on its state, and it must change its behavior at run-time depending on that state.
Operations have large, multipart conditional statements that depend on the object's state. Often, several operations will contain this same conditional structure.
2. It makes state transitions explicit
3. State objects can be shared
2. A table-based alternative
3. Creating and destroying State object.
4. Using dynamic inheritance
SPOP supports the following command:
USER with a username must come first
PASS with a password or QUIT must come after USER
If the username and password are valid, then the user can use other commands
Returns: size of message in octets
If it contains an optional message number then returns the size of that message
Otherwise return size of all mail messages in the mail box
Returns: the mail message indicated by the number
Updates mail box to reflect transactions taken during the transaction state, then logs user out
If session ends by any method except the QUIT command, the updates are not done
class SPop { static final int QUIT = 1; static final int HAVE_USER_NAME = 2; static final int START = 3; static final int AUTHORIZED = 4; private int state = START; String userName; String password; public void user( String userName ) { switch (state) { case START: { this.userName = userName; state = HAVE_USER_NAME; break; } default: { // invalid command sendErrorMessageOrWhatEver(); endLastSessionWithoutUpdate(); userName = null; password = null; state = START; } } }
public void pass( String password ) { switch (state) { case HAVE_USER_NAME: { this.password = password; if ( validateUser() ) state = AUTHORIZED; else { sendErrorMessageOrWhatEver(); userName = null; password = null; state = START; } } default: { // invalid command sendErrorMessageOrWhatEver(); endLastSessionWithoutUpdate(); state = START; } } } etc. }
class SPop { private SPopState state = new Start(); public void user( String userName ) { state = state.user( userName ); } public void pass( String password ) { state = state.pass( password ); } public void list( int messageNumber ) { state = state.list( massageNumber ); } etc.
abstract class SPopState { public SPopState user( String userName ) { put default action here } public SPopState pass( String password ) { put default action here } public SPopState list( int massageNumber ) { put default action here } public SPopState retr( int massageNumber ) { put default action here } public SPopState quit( ) { put default action here } }
class Start extends SPopState { public SPopState user( String userName ) { return new HaveUserName( userName ); } } class HaveUserName extends SPopState { String userName; public HaveUserName( String userName ) { this.userName = userName; } public SPopState pass( String password ) { if ( validateUser( userName, password ) return new Authorized( userName ); else return new Start(); } }
class SPop { private SPopState state = new Start(); public void user( String userName ) { state.user( userName ); state = new HaveUserName( userName ); } public void pass( String password ) { if ( state.pass( password ) ) state = new Authorized( ); else state = new Start(); }
class SPop { private SPopState state = new Start(); protected changeState( SPopState nextState ) { state = nextState; } public void user( String userName ) {state.user( userName, this ); } etc } class Start extends SPopState { public SPopState user( String userName, SPop mailServer ) { mailServer.changeState( HaveUserName( userName ) ); } }Note: Text places a changeState operation in SPopState, and subclasses call it
A state object can have no instance variables if:
class SPop { private SPopState state = new Start(); String userName; String password; public void user( String newName ) { this.userName = newName; state.user( newName ); } public void pass( String password ) { state.pass( userName , password ); }Storing Instance Variables Elsewhere
class SPop { private SPopState state = new Start(); String userName; String password; public String userName() { return userName; } public String password() { return password; } public void user( String newName ) { this.userName = newName ; state.user( this ); } etc.
class HaveUserName extends SPopState { public SPopState pass( SPop mailServer ) { String useName = mailServer.userName(); etc. } }
1. Create state object when needed, destroy it when it is no longer needed
2. Create states once, never destroy them, use as needed
Strategy lets the algorithm vary independently from clients that use it
STL has three basic container classes:
Vector
One can implement a Stack with either a Vector, linked-list or a deque
Under different situations each can be better than the other two
class Stack { Container myStorage; public Stack( Container storage ) { myStorage = storage; } public void push( Object item ) { myStorage.append( item ); } public Object push( Object item ) { return myStorage.removeEnd( ); } }
abstract class Container { public append( Object item ); public Object removeEnd(); etc. } class Vector extends Container { public append( Object item ); { add the code here } public Object removeEnd() { add the code here } }
Alternative to subclassing
Eliminates conditional statements
Gives a choice of implementations
Clients must be aware of different Strategies
Communication overhead between Strategy and Context
Increase number of objects
Strategies as template parameters
Making Strategy objects optional