// Purpose. State design pattern lab. "Case statement considered harmful." // 1. Create a State class hierarchy // 2. The State base class has a single method - // void pull( Chain wrapper ) // The method can be abstract, or, it can contain default behavior // 3. Create a State derived class for each condition in the if-then-else statement // 4. Move each println() statement to the pull() method of its appropriate class // 5. Make the Chain class a "wrapper" class that models the state machine abstraction // 6. The Chain class maintains a "current" State object // 7. All client requests [i.e. pull()] are simply delegated to the current state // object and the wrapper object's "this" pointer is passed // 8. The Chain class needs a constructor that initializes its current state // 9. It also needs a setState() method that the State derived classes can use to // change the state of the state machine // 10. Call the setState() method as necessary in each of the State derived classes' // pull() methods import java.io.*; class Chain { private int state; public Chain() { state = 0; } public void pull() { if (state == 0) { state = 1; System.out.println( " low speed" ); } else if (state == 1) { state = 2; System.out.println( " medium speed" ); } else if (state == 2) { state = 3; System.out.println( " high speed" ); } else { state = 0; System.out.println( " turning off" ); } } } public class StateDisc { public static void main( String[] args ) throws IOException { InputStreamReader is = new InputStreamReader( System.in ); int ch; Chain chain = new Chain(); while (true) { System.out.print( "Press " ); ch = is.read(); ch = is.read(); chain.pull(); } } } // Press // low speed // Press // medium speed // Press // high speed // Press // turning off // Press // low speed // Press // medium speed // Press // high speed // Press // turning off // Purpose. State design pattern implementation import java.io.*; class Chain { private State current; public Chain() { current = new Off(); } public void setState( State s ) { current = s; } public void pull() { current.pull( this ); } } abstract class State { public void pull( Chain wrapper ) { wrapper.setState( new Off() ); System.out.println( " turning off" ); } } class Off extends State { public void pull( Chain wrapper ) { wrapper.setState( new Low() ); System.out.println( " low speed" ); } } class Low extends State { public void pull( Chain wrapper ) { wrapper.setState( new Medium() ); System.out.println( " medium speed" ); } } class Medium extends State { public void pull( Chain wrapper ) { wrapper.setState( new High() ); System.out.println( " high speed" ); } } class High extends State { } public class StateDisc { public static void main( String[] args ) throws IOException { InputStreamReader is = new InputStreamReader( System.in ); int ch; Chain chain = new Chain(); while (true) { System.out.print( "Press " ); ch = is.read(); ch = is.read(); chain.pull(); } } } // Purpose. State design pattern - an FSM with two states and one event // (distributed transition logic - logic in the derived state classes) // 1. Create a "wrapper" class that models the state machine // 2. The wrapper class maintains a "current" state object // 3. All client requests are simply delegated to the current state object and // the wrapper object's "this" pointer is passed // 4. Create a state base class that makes the concrete states interchangeable // 5. The State base class specifies any useful "default" behavior // 6. The State derived classes only override the messages they need to o/r // 7. The State methods will change the "current" state in the "wrapper" import java.io.*; // Event - push ///// 1. The "wrapper" class ///// // State ---- class Button { // ON OFF ///// 2. The "current" state object ///// // OFF ON private State current; public Button() { current = OFF.instance(); } public void setCurrent( State s ) { current = s; } ///// 3. The "wrapper" always delegates to the "wrappee" ///// public void push() { current.push( this ); } } ///// 4. The "wrappee" hierarchy ///// class State { public void push( Button b ) { // 5. Default behavior b.setCurrent( OFF.instance() ); // can go in the base System.out.println( " turning OFF" ); // class } } class ON extends State { private static ON inst = new ON(); private ON() { } public static State instance() { return inst; } } class OFF extends State { private static OFF inst = new OFF(); private OFF() { } public static State instance() { return inst; } ///// 6. Override only the necessary methods ///// public void push( Button b ) { ///// 7. The "wrappee" may callback to the "wrapper" ///// b.setCurrent( ON.instance() ); System.out.println( " turning ON" ); } } public class StateToggle { public static void main( String[] args ) throws IOException { InputStreamReader is = new InputStreamReader( System.in ); int ch; Button btn = new Button(); while (true) { System.out.print( "Press " ); ch = is.read(); ch = is.read(); btn.push(); } } } // D:\Java> java StateToggle // Press // turning ON // Press // turning OFF // Press // turning ON // Press // turning OFF // Purpose. State design pattern (centralized transition logic) // 1. Create a "wrapper" class that models the state machine // 2. The wrapper class contains an array of state concrete objects // 3. The wrapper class contains an index to its "current" state // 4. The wrapper class contains a state transition table // 5. All client requests are simply delegated to the current state object // 6. Create a state base class that makes the concrete states interchangeable // 7. The State base class specifies default behavior for all messages // 8. The State derived classes only override the messages they need to // 1. Create a "wrapper" class that models the state machine class FSM { private State[] states = { new A(), new B(), new C() }; // 2. states private int[][] transition = { {2,1,0}, {0,2,1}, {1,2,2} }; // 4. transitions private int current = 0; // 3. current private void next( int msg ) { current = transition[current][msg]; } // 5. All client requests are simply delegated to the current state object public void on() { states[current].on(); next( 0 ); } public void off() { states[current].off(); next( 1 ); } public void ack() { states[current].ack(); next( 2 ); } } // 6. Create a state base class that makes the concrete states interchangeable abstract class State { public void on() { System.out.println( "error" ); } // 7. The State base public void off() { System.out.println( "error" ); } // class specifies public void ack() { System.out.println( "error" ); } // default behavior } class A extends State { public void on() { System.out.println( "A + on = C" ); } public void off() { System.out.println( "A + off = B" ); } public void ack() { System.out.println( "A + ack = A" ); } } class B extends State { public void on() { System.out.println( "B + on = A" ); } public void off() { System.out.println( "B + off = C" ); } } class C extends State { // 8. The State derived classes only override the messages they need to public void on() { System.out.println( "C + on = B" ); } } public class StateDemo { public static void main( String[] args ) { FSM fsm = new FSM(); int[] msgs = { 2, 1, 2, 1, 0, 2, 0, 0 }; for (int i=0; i < msgs.length; i++) if (msgs[i] == 0) fsm.on(); else if (msgs[i] == 1) fsm.off(); else if (msgs[i] == 2) fsm.ack(); } } // A + ack = A on off ack // A + off = B +---+---+---+ // error A | C | B | A | // B + off = C +---+---+---+ // C + on = B B | A | C | | // error +---+---+---+ // B + on = A C | B | | | // A + on = C +---+---+---+ // Purpose. State design pattern (distributed transition logic) // 1. Create a "wrapper" class that models the state machine // 2. The wrapper class contains an array of state concrete objects // 3. The wrapper class contains an index to its "current" state // 4. Client requests are delegated to the current state and "this" is passed // 5. Create a state base class that makes the concrete states interchangeable // 6. The State base class specifies default behavior for all messages // 7. The State derived classes only override the messages they need to // 8. The derived classes "call back to" the wrapper class to change its current class FSM { // 1. The "wrapper" private State[] states = { new A(), new B(), new C() }; // 2. States array private int current = 0; // 3. Current state public void on() { states[current].on( this ); } // 4. Delegation public void off() { states[current].off( this ); } // and pass the public void ack() { states[current].ack( this ); } // this pointer public void changeState( int index ) { current = index; } } abstract class State { // 5. The State base class public void on( FSM fsm ) { System.out.println( "error" ); } // 6. Default public void off( FSM fsm ) { System.out.println( "error" ); } // behavior public void ack( FSM fsm ) { System.out.println( "error" ); } } class A extends State { public void on( FSM fsm ) { System.out.println( "A + on = C" ); fsm.changeState( 2 ); } public void off( FSM fsm ) { System.out.println( "A + off = B" ); fsm.changeState( 1 ); } public void ack( FSM fsm ) { System.out.println( "A + ack = A" ); fsm.changeState( 0 ); } } class B extends State { public void on( FSM fsm ) { System.out.println( "B + on = A" ); fsm.changeState( 0 ); } public void off( FSM fsm ) { System.out.println( "B + off = C" ); fsm.changeState( 2 ); } } class C extends State { // 7. Only override some messages public void on( FSM fsm ) { System.out.println( "C + on = B" ); fsm.changeState( 1 ); } // 8. "call back to" the wrapper class } public class StateDemo2 { public static void main( String[] args ) { FSM fsm = new FSM(); int[] msgs = { 2, 1, 2, 1, 0, 2, 0, 0 }; for (int i=0; i < msgs.length; i++) if (msgs[i] == 0) fsm.on(); else if (msgs[i] == 1) fsm.off(); else if (msgs[i] == 2) fsm.ack(); } } // A + ack = A // A + off = B // error // B + off = C // C + on = B // error // B + on = A // A + on = C // Purpose. The beginning of a case statement has taken seed in Property.landOnBy(). // This can be remedied with the State design pattern. class Player { private String name; private int money; public Player( String n, int m ) { name = n; money = m; } public String getName() { return name; } public int getWorth() { return money; } public void debit( int m ) { money -= m; } public void credit( int m ) { money += m; } } class Property { private String name; private int price; private int rent; private Player owner; public Property( String n ) { name = n; price = 100; rent = 10; } public String getName() { return name; } public int getPrice() { return price; } public int getRent() { return rent; } public Player getOwner() { return owner; } public void setOwner( Player p ) { owner = p; } void landOnBy( Player p ) { System.out.print( p.getName() + " landed on " + name ); if (getOwner() == null) { System.out.print( " - not owned\n" + p.getName() ); if (p.getWorth() < getPrice()) { System.out.println( " does not have enough money to purchase" ); } else { p.debit( getPrice() ); setOwner( p ); System.out.println( " bought " + getName() ); } } else { System.out.println( " - owned by " + getOwner().getName() ); if (p != getOwner()) { p.debit( getRent() ); getOwner().credit( getRent() ); System.out.println( getOwner().getName() + " now has " + getOwner().getWorth() + " dollars" ); } } System.out.println( p.getName() + " has " + p.getWorth() + " dollars" ); } } public class StateDisc { public static void main( String[] args ) { Player p1 = new Player( "Tom", 50 ); Player p2 = new Player( " Dick", 500 ); Property prop = new Property( "Boardwalk" ); prop.landOnBy( p1 ); prop.landOnBy( p2 ); prop.landOnBy( p1 ); prop.landOnBy( p2 ); prop.landOnBy( p1 ); } } // Tom landed on Boardwalk - not owned // Tom does not have enough money to purchase // Tom has 50 dollars // Dick landed on Boardwalk - not owned // Dick bought Boardwalk // Dick has 400 dollars // Tom landed on Boardwalk - owned by Dick // Dick now has 410 dollars // Tom has 40 dollars // Dick landed on Boardwalk - owned by Dick // Dick has 410 dollars // Tom landed on Boardwalk - owned by Dick // Dick now has 420 dollars // Tom has 30 dollars // Purpose. Refactored the behavior of "ownership" to a second dimension - // the PropertyOwnershipState hierarchy class Player { private String name; private int money; public Player( String n, int m ) { name = n; money = m; } public String getName() { return name; } public int getWorth() { return money; } public void debit( int m ) { money -= m; } public void credit( int m ) { money += m; } } class Property { private String name; private int price; private int rent; private Player owner; private PropertyOwnershipState currentState; public Property( String n ) { name = n; price = 100; rent = 10; currentState = UnownedState.instance(); } public String getName() { return name; } public int getPrice() { return price; } public int getRent() { return rent; } public Player getOwner() { return owner; } public void setOwner( Player p ) { owner = p; } public void setCurrentState( PropertyOwnershipState s ) { currentState = s; } void landOnBy( Player p ) { System.out.print( p.getName() + " landed on " + name ); currentState.landOnBy( p, this ); System.out.println( p.getName() + " has " + p.getWorth() + " dollars" ); } } abstract class PropertyOwnershipState { public abstract void landOnBy( Player pl, Property pr ); } class OwnedState extends PropertyOwnershipState { private static OwnedState theInstance = null; private OwnedState() { } public static OwnedState instance() { if (theInstance == null) theInstance = new OwnedState(); return theInstance; } public void landOnBy( Player p, Property wrapper ) { System.out.println( " - owned by " + wrapper.getOwner().getName() ); if (p != wrapper.getOwner()) { p.debit( wrapper.getRent() ); wrapper.getOwner().credit( wrapper.getRent() ); System.out.println( wrapper.getOwner().getName() + " now has " + wrapper.getOwner().getWorth() + " dollars" ); } } } class UnownedState extends PropertyOwnershipState { private static UnownedState theInstance = null; private UnownedState() { } public static UnownedState instance() { if (theInstance == null) theInstance = new UnownedState(); return theInstance; } public void landOnBy( Player p, Property wrapper ) { System.out.print( " - not owned\n" + p.getName() ); if (p.getWorth() < wrapper.getPrice()) System.out.println( " does not have enough money to purchase" ); else { p.debit( wrapper.getPrice() ); wrapper.setOwner( p ); wrapper.setCurrentState( OwnedState.instance() ); System.out.println( " bought " + wrapper.getName() ); } } } public class StateDisc { public static void main( String[] args ) { Player p1 = new Player( "Tom", 50 ); Player p2 = new Player( " Dick", 500 ); Property prop = new Property( "Boardwalk" ); prop.landOnBy( p1 ); prop.landOnBy( p2 ); prop.landOnBy( p1 ); prop.landOnBy( p2 ); prop.landOnBy( p1 ); } } // Tom landed on Boardwalk - not owned // Tom does not have enough money to purchase // Tom has 50 dollars // Dick landed on Boardwalk - not owned // Dick bought Boardwalk // Dick has 400 dollars // Tom landed on Boardwalk - owned by Dick // Dick now has 410 dollars // Tom has 40 dollars // Dick landed on Boardwalk - owned by Dick // Dick has 410 dollars // Tom landed on Boardwalk - owned by Dick // Dick now has 420 dollars // Tom has 30 dollars /******************************* Player Property PropertyOwnershipState | landOnBy( pla ) | | |------------------------------->| | UN-OWNED | | | | | landOnBy( pla, pro ) | | |--------------------------------->| | | | | | getWorth() | |<------------------------------------------------------------------| | | | | | getPrice() | | |<---------------------------------| | | | | | debit() | |<------------------------------------------------------------------| | | | | | setOwner() | | |<---------------------------------| | | | | | setCurrentState() | | |<---------------------------------| | | | | | | | | | | landOnBy( pla ) | | |------------------------------->| | OWNED | | | | | landOnBy( pla, pro ) | | |--------------------------------->| | | | | | getOwner() | | |<---------------------------------| | | | | | getRent() | | |<---------------------------------| | | | | | debit() | |<------------------------------------------------------------------| | | | | | getOwner() | | |<---------------------------------| | | | | | credit() | | <---------------------------------------------------| | | | | | | *******************************/