如何解决实现带有转换的状态模式作为状态机引擎的最佳方法是什么
几年前,我实现了具有过渡功能的演示程序,并尝试使用状态模式。该实现有4个状态机,43个状态和78个转换,但是很难理解,并且没有正确遵循状态模式。我现在需要实施另一个演示,并且一直在重新思考如何做。我提出的方法概述如下,我想征询有关如何最好地实现过渡状态模式的想法和意见。
维基百科在https://en.wikipedia.org/wiki/State_pattern中描述了状态模式。我的解释是,特定状态下的行为被封装为一个类,并且所有此类都具有相同的接口。
有两个问题要解决。首先是如何使回调的目标取决于状态,其次是如何对StateAction类具有单个接口。
- 将StateAction类的接口定义为单个方法
def dispatch(self,trigger,*args,**kwargs)
。 - 创建一组StateAction类,每个类对状态中的行为进行编码。 (请注意,这些不是transition.State类)
- 在模型中,实例化所有这些StateAction类,并以状态名称为键。
- 重写
Machine.resolve_callable
,以便回调以触发器为参数在当前状态下调用dispatch方法 - 每个实例中的dispatch方法在self上调用适当的方法
新的Machine.resolve_callable需要引用模型中实例的字典。通过子类化Machine类并添加actions=xxx
关键字项来提供此功能。
所有回调都将使用适当的触发参数成为当前状态实例上对dispatch方法的调用。例如
transitions = [
{ 'trigger': 'melt','source': 'solid','dest': 'liquid','before': 'make_hissing_noises'},]
将导致对solid.dispatch('make_hissing_noises')
的调用,其中solid是StateAction类的实例。
此方法不支持机器附加到模型的各种便利方法。对我来说这不是问题,因为我的应用程序基于事件循环,并且始终使用对model.trigger(xxx)
的调用来开始状态更改。对于其他人而言,这可能不是问题,因为如果各种方法位于不同的类中,那么人们就不会期望在模型上具有类似的方法。
下面的代码以一个简单的示例说明了该方法,即使该方法存在于两个StateAction实例上,它也会为当前状态调用on_Train_Arrived
方法。
from transitions import Machine
from functools import partial
from six import string_types
import logging
logging.basicConfig(level=logging.WARNING)
class StatePatternMachine(Machine):
"""Machine that simplifies using the State Pattern"""
def __init__(self,**kwargs):
self._actions = kwargs.pop('actions')
super().__init__(*args,**kwargs)
@staticmethod
def resolve_callable(target,event_data):
""" Converts a model's property name into a call to dispatch.
The target must be a string.
Args:
target (str): method name
event_data (EventData): Currently processed event
Returns:
callable function dispatch on current state
"""
if not isinstance(target,string_types):
raise ValueError('%r is not be a string' % target)
else:
try:
func = getattr(event_data.machine._actions[event_data.state.name],'dispatch')
except AttributeError:
raise AttributeError('No dispatch method on %r' % event_data.model.states)
return partial(func,target) # dispatch signature is dispatch(trigger,**kwargs)
class StateAction(object):
"""base class for all classes implementing the State Pattern"""
def dispatch(self,**kwargs):
"""call the appropriate function on self
this is the common interface to all state classes
"""
func = getattr(self,None)
if func:
return func(*args,**kwargs)
else:
print(f'have not got {trigger}')
# simple model for a set of states implementing the State Pattern
class BuyingTicket(StateAction):
_name = 'BuyingTicket'
def on_Train_Arrived(self,**kwargs):
print('Missed Train')
def on_Train_Cancelled(self,**kwargs):
print('on_Train_Cancelled')
class Waiting(StateAction):
_name = 'Waiting'
def on_Train_Arrived(self,**kwargs):
print('Caught Train')
def on_Timeout(self,**kwargs):
print('Timeout')
class Home(StateAction):
_name = 'Home'
journey_states = [
BuyingTicket,Waiting,Home,]
transitions = [
{'trigger': 'Train_Arrived','source': 'BuyingTicket','dest': 'Waiting','before': 'on_Train_Arrived'},{'trigger': 'Train_Cancelled','dest': 'Home','before': 'on_Train_Cancelled'},{'trigger': 'Train_Arrived','source': 'Waiting','before': 'on_Train_Arrived'}
]
class Model(object):
def __init__(self,**kwargs):
super().__init__(*args,**kwargs)
# dict of instances of states
self.states = {s._name: s() for s in journey_states}
self.machine = StatePatternMachine(
model=self,states=[k for k in self.states],transitions=transitions,initial='BuyingTicket',actions=self.states,)
model = Model()
model.trigger('Train_Arrived')
Missed Train
True
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。