Both Proxy and State
provide a surrogate class that you use in your code; the real class that does
the work is hidden behind this surrogate class. When you call a method in the
surrogate, it simply turns around and calls the method in the implementing
class. These two patterns are so similar that the Proxy is simply a
special case of State. One is tempted to just lump the two together into
a pattern called Surrogate, but the term proxy has a
long-standing and specialized meaning, which probably explains the reason for
the two different patterns.
Add Comment
The basic idea is simple: from a base
class, the surrogate is derived along with the class or classes that provide the
actual implementation:
Add Comment
When a surrogate object is created, it is
given an implementation to which to send all of the method calls.
Add Comment
Structurally, the difference between
Proxy and State is simple: a Proxy has only one
implementation, while State has more than one. The application of the
patterns is considered (in Design Patterns) to be distinct: Proxy
is used to control access to its implementation, while State allows you
to change the implementation dynamically. However, if you expand your notion of
controlling access to implementation then the two fit neatly
together.
Add Comment
If we implement Proxy by following
the above diagram, it looks like this:
Add Comment
#: c04:ProxyDemo.py # Simple demonstration of the Proxy pattern. class Implementation: def f(self): print "Implementation.f()" def g(self): print "Implementation.g()" def h(self): print "Implementation.h()" class Proxy: def __init__(self): self.__implementation = Implementation() # Pass method calls to the implementation: def f(self): self.__implementation.f() def g(self): self.__implementation.g() def h(self): self.__implementation.h() p = Proxy() p.f(); p.g(); p.h() #:~
It isnt necessary that
Implementation have the same interface as Proxy; as long as
Proxy is somehow speaking for the class that it is
referring method calls to then the basic idea is satisfied (note that this
statement is at odds with the definition for Proxy in GoF). However, it is
convenient to have a common interface so that Implementation is forced to
fulfill all the methods that Proxy needs to call.
Add Comment
Of course, in Python we have a delegation
mechanism built in, so it makes the Proxy even simpler to implement:
Add Comment
#: c04:ProxyDemo2.py # Simple demonstration of the Proxy pattern. class Implementation2: def f(self): print "Implementation.f()" def g(self): print "Implementation.g()" def h(self): print "Implementation.h()" class Proxy2: def __init__(self): self.__implementation = Implementation2() def __getattr__(self, name): return getattr(self.__implementation, name) p = Proxy2() p.f(); p.g(); p.h(); #:~
The beauty of using
__getattr__( ) is that Proxy2 is completely generic, and not
tied to any particular implementation (in Java, a rather complicated
dynamic proxy has been invented to accomplish this same thing).
Add Comment
The State pattern adds more
implementations to Proxy, along with a way to switch from one
implementation to another during the lifetime of the surrogate:
Add Comment
#: c04:StateDemo.py # Simple demonstration of the State pattern. class State_d: def __init__(self, imp): self.__implementation = imp def changeImp(self, newImp): self.__implementation = newImp # Delegate calls to the implementation: def __getattr__(self, name): return getattr(self.__implementation, name) class Implementation1: def f(self): print "Fiddle de dum, Fiddle de dee," def g(self): print "Eric the half a bee." def h(self): print "Ho ho ho, tee hee hee," class Implementation2: def f(self): print "We're Knights of the Round Table." def g(self): print "We dance whene'er we're able." def h(self): print "We do routines and chorus scenes" def run(b): b.f() b.g() b.h() b.g() b = State_d(Implementation1()) run(b) b.changeImp(Implementation2()) run(b) #:~
You can see that the first
implementation is used for a bit, then the second implementation is swapped in
and that is used.
Add Comment
The difference between Proxy and
State is in the problems that are solved. The common uses for
Proxy as described in Design Patterns are:
Add Comment
You
could look at a Python reference as a kind of protection proxy, since it
controls access to the actual object on the heap (and ensures, for example, that
you dont use a null reference).
Add Comment
[[ Rewrite this: In Design
Patterns, Proxy and State are not seen as related to each
other because the two are given (what I consider arbitrarily) different
structures. State, in particular, uses a separate implementation
hierarchy but this seems to me to be unnecessary unless you have decided that
the implementation is not under your control (certainly a possibility, but if
you own all the code there seems to be no reason not to benefit from the
elegance and helpfulness of the single base class). In addition, Proxy
need not use the same base class for its implementation, as long as the proxy
object is controlling access to the object it fronting for.
Regardless of the specifics, in both Proxy and State a surrogate
is passing method calls through to an implementation object.]]]
Add Comment
While State has a way to allow the
client programmer to change the implementation, StateMachine imposes a
structure to automatically change the implementation from one object to the
next. The current implementation represents the state that a system is in, and
the system behaves differently from one state to the next (because it uses
State). Basically, this is a state machine using objects.
Add Comment
The code that moves the system from one
state to the next is often a Template Method, as seen in the following
framework for a basic state machine.
Add Comment
Each state can be run( ) to
perform its behavior, and (in this design) you can also pass it an
input object so it can tell you what new state to move to based on
that input. The key distinction between this design and the next
is that here, each State object decides what other states it can move to,
based on the input, whereas in the subsequent design all of the
state transitions are held in a single table. Another way to put it is that
here, each State object has its own little State table, and in the
subsequent design there is a single master state transition table for the whole
system.
Add Comment
#: c04:statemachine:State.py # A State has an operation, and can be moved # into the next State given an Input: class State: def run(self): assert 1, "run not implemented" def next(self, input): assert 1, "next not implemented" #:~
This class is clearly
unnecessary, but it allows us to say that something is a State object in
code, and provide a slightly different error message when all the methods are
not implemented. We could have gotten basically the same effect by saying:
Add Comment
class State: pass
because we
would still get exceptions if run( ) or next( ) were
called for a derived type, and they hadnt been implemented.
Add Comment
The StateMachine keeps track of
the current state, which is initialized by the constructor. The
runAll( ) method takes a list of Input objects. This method
not only moves to the next state, but it also calls run( ) for each
state object thus you can see its an expansion of the idea of the
State pattern, since run( ) does something different
depending on the state that the system is in.
Add Comment
#: c04:statemachine:StateMachine.py # Takes a list of Inputs to move from State to # State using a template method. class StateMachine: def __init__(self, initialState): self.currentState = initialState self.currentState.run() # Template method: def runAll(self, inputs): for i in inputs: print i self.currentState = self.currentState.next(i) self.currentState.run() #:~
Ive also treated
runAll( ) as a template method. This is typical, but certainly not
required you could concievably want to override it, but typically the
behavior change will occur in States run( ) instead.
Add Comment
At this point the basic framework for
this style of StateMachine (where each state decides the next states) is
complete. As an example, Ill use a fancy mousetrap that can move through
several states in the process of trapping a
mouse[12]. The
mouse classes and information are stored in the mouse package, including
a class representing all the possible moves that a mouse can make, which will be
the inputs to the state machine:
Add Comment
#: c04:mouse:MouseAction.py class MouseAction: def __init__(self, action): self.action = action def __str__(self): return self.action def __cmp__(self, other): return cmp(self.action, other.action) # Necessary when __cmp__ or __eq__ is defined # in order to make this class usable as a # dictionary key: def __hash__(self): return hash(self.action) # Static fields; an enumeration of instances: MouseAction.appears = MouseAction("mouse appears") MouseAction.runsAway = MouseAction("mouse runs away") MouseAction.enters = MouseAction("mouse enters trap") MouseAction.escapes = MouseAction("mouse escapes") MouseAction.trapped = MouseAction("mouse trapped") MouseAction.removed = MouseAction("mouse removed") #:~
Youll note that
__cmp__( ) has been overidden to implement a comparison between
action values. Also, each possible move by a mouse is enumerated as a
MouseAction object, all of which are static fields in MouseAction.
Add Comment
For creating test code, a sequence of
mouse inputs is provided from a text file:
Add Comment
#:! c04:mouse:MouseMoves.txt mouse appears mouse runs away mouse appears mouse enters trap mouse escapes mouse appears mouse enters trap mouse trapped mouse removed mouse appears mouse runs away mouse appears mouse enters trap mouse trapped mouse removed #:~
With these tools in place,
its now possible to create the first version of the mousetrap program.
Each State subclass defines its run( ) behavior, and also
establishes its next state with an if-else clause:
Add Comment
#: c04:mousetrap1:MouseTrapTest.py # State Machine pattern using 'if' statements # to determine the next state. import string, sys sys.path += ['../statemachine', '../mouse'] from State import State from StateMachine import StateMachine from MouseAction import MouseAction # A different subclass for each state: class Waiting(State): def run(self): print "Waiting: Broadcasting cheese smell" def next(self, input): if input == MouseAction.appears: return MouseTrap.luring return MouseTrap.waiting class Luring(State): def run(self): print "Luring: Presenting Cheese, door open" def next(self, input): if input == MouseAction.runsAway: return MouseTrap.waiting if input == MouseAction.enters: return MouseTrap.trapping return MouseTrap.luring class Trapping(State): def run(self): print "Trapping: Closing door" def next(self, input): if input == MouseAction.escapes: return MouseTrap.waiting if input == MouseAction.trapped: return MouseTrap.holding return MouseTrap.trapping class Holding(State): def run(self): print "Holding: Mouse caught" def next(self, input): if input == MouseAction.removed: return MouseTrap.waiting return MouseTrap.holding class MouseTrap(StateMachine): def __init__(self): # Initial state StateMachine.__init__(self, MouseTrap.waiting) # Static variable initialization: MouseTrap.waiting = Waiting() MouseTrap.luring = Luring() MouseTrap.trapping = Trapping() MouseTrap.holding = Holding() moves = map(string.strip, open("../mouse/MouseMoves.txt").readlines()) MouseTrap().runAll(map(MouseAction, moves)) #:~
The StateMachine class
simply defines all the possible states as static objects, and also sets up the
initial state. The UnitTest creates a MouseTrap and then tests it
with all the inputs from a MouseMoveList.
Add Comment
While the use
of if statements inside the next( ) methods is perfectly
reasonable, managing a large number of these could become difficult. Another
approach is to create tables inside each State object defining the
various next states based on the input.
Add Comment
Initially, this seems like it ought to be
quite simple. You should be able to define a static table in each State
subclass that defines the transitions in terms of the other State
objects. However, it turns out that this approach generates cyclic
initialization dependencies. To solve the problem, Ive had to delay the
initialization of the tables until the first time that the next( )
method is called for a particular State object. Initially, the
next( ) methods can appear a little strange because of this.
Add Comment
The StateT class is an
implementation of State (so that the same StateMachine class can
be used from the previous example) that adds a Map and a method to
initialize the map from a two-dimensional array. The next( ) method
has a base-class implementation which must be called from the overridden derived
class next( ) methods after they test for a null Map (and
initialize it if its null):
Add Comment
#: c04:mousetrap2:MouseTrap2Test.py # A better mousetrap using tables import string, sys sys.path += ['../statemachine', '../mouse'] from State import State from StateMachine import StateMachine from MouseAction import MouseAction class StateT(State): def __init__(self): self.transitions = None def next(self, input): if self.transitions.has_key(input): return self.transitions[input] else: raise "Input not supported for current state" class Waiting(StateT): def run(self): print "Waiting: Broadcasting cheese smell" def next(self, input): # Lazy initialization: if not self.transitions: self.transitions = { MouseAction.appears : MouseTrap.luring } return StateT.next(self, input) class Luring(StateT): def run(self): print "Luring: Presenting Cheese, door open" def next(self, input): # Lazy initialization: if not self.transitions: self.transitions = { MouseAction.enters : MouseTrap.trapping, MouseAction.runsAway : MouseTrap.waiting } return StateT.next(self, input) class Trapping(StateT): def run(self): print "Trapping: Closing door" def next(self, input): # Lazy initialization: if not self.transitions: self.transitions = { MouseAction.escapes : MouseTrap.waiting, MouseAction.trapped : MouseTrap.holding } return StateT.next(self, input) class Holding(StateT): def run(self): print "Holding: Mouse caught" def next(self, input): # Lazy initialization: if not self.transitions: self.transitions = { MouseAction.removed : MouseTrap.waiting } return StateT.next(self, input) class MouseTrap(StateMachine): def __init__(self): # Initial state StateMachine.__init__(self, MouseTrap.waiting) # Static variable initialization: MouseTrap.waiting = Waiting() MouseTrap.luring = Luring() MouseTrap.trapping = Trapping() MouseTrap.holding = Holding() moves = map(string.strip, open("../mouse/MouseMoves.txt").readlines()) mouseMoves = map(MouseAction, moves) MouseTrap().runAll(mouseMoves) #:~
The rest of the code is
identical the difference is in the next( ) methods and the
StateT class.
Add Comment
If you have to create and maintain a lot
of State classes, this approach is an improvement, since its
easier to quickly read and understand the state transitions from looking at the
table.
Add Comment
The advantage of the previous design is
that all the information about a state, including the state transition
information, is located within the state class itself. This is generally a good
design principle.
Add Comment
However, in a pure state machine, the
machine can be completely represented by a single state-transition table. This
has the advantage of locating all the information about the state machine in a
single place, which means that you can more easily create and maintain the table
based on a classic state-transition diagram.
Add Comment
The classic state-transition diagram uses
a circle to represent each state, and lines from the state pointing to all
states that state can transition into. Each transition line is annotated with
conditions for transition and an action during transition. Heres what it
looks like:
Add Comment
(Simple State Machine Diagram)
Add Comment
Goals:
Observations:
Example:
Add Comment
The State class is distinctly
different from before, since it is really just a placeholder with a name. Thus
it is not inherited from previous State classes:
Add Comment
# c04:statemachine2:State.py class State: def __init__(self, name): self.name = name def __str__(self): return self.name # :~
In the state transition diagram, an input
is tested to see if it meets the condition necessary to transfer to the state
under question. As before, the Input is just a tagging interface:
Add Comment
# c04:statemachine2:Input.py # Inputs to a state machine class Input: pass # :~
The Condition evaluates
the Input to decide whether this row in the table is the correct
transition:
Add Comment
# c04:statemachine2:Condition.py # Condition function object for state machine class Condition: boolean condition(input) : assert 1, "condition() not implemented" # :~
If the Condition returns
true, then the transition to a new state is made, and as that transition
is made some kind of action occurs (in the previous state machine design, this
was the run( ) method):
Add Comment
# c04:statemachine2:Transition.py # Transition function object for state machine class Transition: def transition(self, input): assert 1, "transition() not implemented" # :~"_Toc533816237">
With these classes in place, we can set
up a 3-dimensional table where each row completely
describes a state. The first element in the row is the current state, and the
rest of the elements are each a row indicating what the type of the input
can be, the condition that must be satisfied in order for this state change to
be the correct one, the action that happens during transition, and the new state
to move into. Note that the Input object is not just used for its type,
it is also a Messenger object that carries information to the
Condition and Transition objects:
Add Comment
{(CurrentState, InputA) : (ConditionA, TransitionA, NextA), (CurrentState, InputB) : (ConditionB, TransitionB, NextB), (CurrentState, InputC) : (ConditionC, TransitionC, NextC), ... }
# c04:statemachine2:StateMachine.py # A table-driven state machine class StateMachine: def __init__(self, initialState, tranTable): self.state = initialState self.transitionTable = tranTable def nextState(self, input): Iterator it=((List)map.get(state)).iterator() while(it.hasNext()): Object[] tran = (Object[])it.next() if(input == tran[0] || input.getClass() == tran[0]): if(tran[1] != null): Condition c = (Condition)tran[1] if(!c.condition(input)) continue # Failed test if(tran[2] != null) ((Transition)tran[2]).transition(input) state = (State)tran[3] return throw RuntimeException( "Input not supported for current state") # :~
# c04:vendingmachine:VendingMachine.py # Demonstrates use of StateMachine.py import sys sys.path += ['../statemachine2'] import StateMachine class State: def __init__(self, name): self.name = name def __str__(self): return self.name State.quiescent = State("Quiesecent") State.collecting = State("Collecting") State.selecting = State("Selecting") State.unavailable = State("Unavailable") State.wantMore = State("Want More?") State.noChange = State("Use Exact Change Only") State.makesChange = State("Machine makes change") class HasChange: def __init__(self, name): self.name = name def __str__(self): return self.name HasChange.yes = HasChange("Has change") HasChange.no = HasChange("Cannot make change") class ChangeAvailable(StateMachine): def __init__(self): StateMachine.__init__(State.makesChange, { # Current state, input (State.makesChange, HasChange.no) : # test, transition, next state: (null, null, State.noChange), (State.noChange, HasChange.yes) : (null, null, State.noChange) }) class Money: def __init__(self, name, value): self.name = name self.value = value def __str__(self): return self.name def getValue(self): return self.value Money.quarter = Money("Quarter", 25) Money.dollar = Money("Dollar", 100) class Quit: def __str__(self): return "Quit" Quit.quit = Quit() class Digit: def __init__(self, name, value): self.name = name self.value = value def __str__(self): return self.name def getValue(self): return self.value class FirstDigit(Digit): pass FirstDigit.A = FirstDigit("A", 0) FirstDigit.B = FirstDigit("B", 1) FirstDigit.C = FirstDigit("C", 2) FirstDigit.D = FirstDigit("D", 3) class SecondDigit(Digit): pass SecondDigit.one = SecondDigit("one", 0) SecondDigit.two = SecondDigit("two", 1) SecondDigit.three = SecondDigit("three", 2) SecondDigit.four = SecondDigit("four", 3) class ItemSlot: id = 0 def __init__(self, price, quantity): self.price = price self.quantity = quantity def __str__(self): return ´ItemSlot.id´ def getPrice(self): return self.price def getQuantity(self): return self.quantity def decrQuantity(self): self.quantity -= 1 class VendingMachine(StateMachine): changeAvailable = ChangeAvailable() amount = 0 FirstDigit first = null ItemSlot[][] items = ItemSlot[4][4] # Conditions: def notEnough(self, input): i1 = first.getValue() i2 = input.getValue() return items[i1][i2].getPrice() > amount def itemAvailable(self, input): i1 = first.getValue() i2 = input.getValue() return items[i1][i2].getQuantity() > 0 def itemNotAvailable(self, input): return !itemAvailable.condition(input) #i1 = first.getValue() #i2 = input.getValue() #return items[i1][i2].getQuantity() == 0 # Transitions: def clearSelection(self, input): i1 = first.getValue() i2 = input.getValue() ItemSlot is = items[i1][i2] print ( "Clearing selection: item " + is + " costs " + is.getPrice() + " and has quantity " + is.getQuantity()) first = null def dispense(self, input): i1 = first.getValue() i2 = input.getValue() ItemSlot is = items[i1][i2] print ("Dispensing item " + is + " costs " + is.getPrice() + " and has quantity " + is.getQuantity()) items[i1][i2].decrQuantity() print ("Quantity " + is.getQuantity()) amount -= is.getPrice() print("Amount remaining " + amount) def showTotal(self, input): amount += ((Money)input).getValue() print "Total amount = " + amount def returnChange(self, input): print "Returning " + amount amount = 0 def showDigit(self, input): first = (FirstDigit)input print "First Digit= "+ first def __init__(self): StateMachine.__init__(self, State.quiescent) for(int i = 0 i < items.length i++) for(int j = 0 j < items[i].length j++) items[i][j] = ItemSlot((j+1)*25, 5) items[3][0] = ItemSlot(25, 0) buildTable(Object[][][]{ ::State.quiescent, # Current state # Input, test, transition, next state: :Money.class, null, showTotal, State.collecting, ::State.collecting, # Current state # Input, test, transition, next state: :Quit.quit, null, returnChange, State.quiescent, :Money.class, null, showTotal, State.collecting, :FirstDigit.class, null, showDigit, State.selecting, ::State.selecting, # Current state # Input, test, transition, next state: :Quit.quit, null, returnChange, State.quiescent, :SecondDigit.class, notEnough, clearSelection, State.collecting, :SecondDigit.class, itemNotAvailable, clearSelection, State.unavailable, :SecondDigit.class, itemAvailable, dispense, State.wantMore, ::State.unavailable, # Current state # Input, test, transition, next state: :Quit.quit, null, returnChange, State.quiescent, :FirstDigit.class, null, showDigit, State.selecting, ::State.wantMore, # Current state # Input, test, transition, next state: :Quit.quit, null, returnChange, State.quiescent, :FirstDigit.class, null, showDigit, State.selecting, ) # :~
# c04:vendingmachine:VendingMachineTest.py # Demonstrates use of StateMachine.py vm = VendingMachine() for input in [ Money.quarter, Money.quarter, Money.dollar, FirstDigit.A, SecondDigit.two, FirstDigit.A, SecondDigit.two, FirstDigit.C, SecondDigit.three, FirstDigit.D, SecondDigit.one, Quit.quit]: vm.nextState(input) # :~
Another approach, as your state machine
gets bigger, is to use an automation tool whereby you configure a table and let
the tool generate the state machine code for you. This can be created yourself
using a language like Python, but there are also free, open-source tools such as
Libero, at http://www.imatix.com.
Add Comment
[12] No
mice were harmed in the creation of this example.