Decorator for use with objects following the state pattern.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | def statemethod(method):
def call_statemethod(self, *args, **kwargs):
# Use self.state.<method> if available, else method itself.
real_method = getattr(self.state, method.func_name, method)
return real_method(self, *args, **kwargs)
call_statemethod.default = method
return call_statemethod
# Sample usage:
class State(object):
"""Base State class, direct parent to non-instantiated states.
Useful when you have lots of base objects and don't need to store
per-state data."""
@classmethod
def new(cls):
"""Create a new Base object with this as the initial state."""
return Base(cls.get_state())
@classmethod
def get_state(cls):
"""Get the state, for use with an existing Base object"""
return cls
class InstantiatedState(State):
"""InstantiatedState creates a new object every time get_state is called.
This allows for independant per-state data storage by multiple base
objects."""
@classmethod
def get_state(cls):
"""Get a state object, for use with an existing Base object"""
return cls()
class Base(object):
def __init__(self, initial_state):
self.state = initial_state
def ordinary_method(self):
print "This method is ordinary."
@statemethod
def default_method(self):
print "This is a default method that has not been overridden."
@statemethod
def overridden_method(self):
print "You shouldn't see this."
assert False
class SimpleState(State):
@staticmethod
def overridden_method(base):
print "The method on %r has been overridden by SimpleState." % base
class DataState(InstantiatedState):
message = "Awesome."
def overridden_method(self, base):
print "This method on %r has been overridden by DataState. %s" \
% (base, self.message)
print "Base A"
print "======"
base_a = SimpleState.new()
print "Calling default_method:"
base_a.default_method()
print "Calling overridden_method:"
base_a.overridden_method()
print "Switching to DataState."
base_a.state = DataState.get_state()
print "Calling overridden_method:"
base_a.overridden_method()
print "Changing message."
base_a.state.message = "Excellent."
print "Calling overridden_method:"
base_a.overridden_method()
print
print "Base B"
print "======"
base_b = DataState.new()
print "Calling default_method:"
base_b.default_method()
print "Calling overridden_method:"
base_b.overridden_method()
|
The state pattern solves the problem of having one object that, depending on its current state, can act in several different ways. Conceptually, this might be seen as a parent class with several subclasses that the object switches between.
The decorator makes it easy to place a function under the state's control. The original method acts as a default, but if the current state has a method with the same name, it's used instead.
To call the default method after overriding it, use:
base.<method>.default(...)
This is analogous to a super() call in a subclass:
super(Subclass, self).<method>(...)