0
点赞
收藏
分享

微信扫一扫

Aha!设计模式(101)-状态模式(3)


实现方法

 

前文说明的状态模式类图只是定义了状态类本身,并没有明确如何进行状态之间的转换。比较直观的做法是由状态本身决定,也就是说当一个状态判断自身处理需要结束时,开始另一个状态的处理。但是这种方式存在一个显而易见的缺点,就是不可避免的发生状态之间的耦合。另一种方式是定义一个表,通过这个表来控制状态之间的迁移。但是这种方法说起来容易,做起来难。因为定义一种广泛适用的表几乎是不可能完成的任务。

 

本文提供另外一种思路:使用UML中关于状态机,状态的定义作为建模的依据构建状态模式的实现代码。因为UML本身的可用性,扩展性已经被证明,因此我们的实现也应该具有同样的可用性和扩展性,至少是具备那样发展的基础。当然也不可能一步实现UML中定义的所有功能,最多只能算作一个起点。先来看实例代码执行的结果:

 

​​动作视频​​

 

正常情况下信号灯的动作次序是:红灯5s->绿灯3s->绿灯闪2s->黄灯3秒->重新从红灯5s开始循环。如果任何情况下按下[Stop]按钮,则需要等到正常回到红灯状态时再进入红灯闪5s之后停止。

 

基础类

 

关于UML语言中状态图的说明,请参照面向对象思考公众号中的相关文章,本文只说明代码。首先说明基础部分,这部分和信号灯没有必然的联系,可以直接运用到其他应用场景。

 

 

class State:    def __init__(self, owner, context):        self.owner = owner        self.context = context        self.transitions = []    def addTransition(self, t):        self.transitions.append(t)    def entry(self):        for t in self.transitions:            t.entry()        if self.owner:            self.owner.active = self    def exit(self):        if self.owner:            self.owner.active = None    def eventHandling(self, e_type, event):        for t in self.transitions:            if t.eventHandling(e_type, event):                return True

这个类主要实现了管理迁移、进入和退出处理以及基础的事件处理。成员onwer指定是下面的StateMachine类。

 

 

from State import *
class StateMachine(State): def __init__(self, owner, context): State.__init__(self, owner, context) self.active = None self.initial = None def entry(self): self.active = self.initial if self.active: self.active.entry()
def eventHandling(self, e_type, event): if self.active: return self.active.eventHandling(e_type, event)
def exit(self): pass

状态机类StateMachine是状态处理的入口。它的active成员管理的是当前处于活动状态的状态对象,其内容在状态类State的entry和exit中被修改。当StateMachine接受到外部的通知时,会调用活动的状态对象的处理方法。

 

class Transition:    def __init__(self, owner, context, s, t):        self.owner = owner        self.context = context        self.source = s        s.addTransition(self)        self.target = t        self.triggers = []     def addTrigger(self, t):        self.triggers.append(t)
def setGuard(self, g): self.guard = g
def entry(self): for t in self.triggers: t.entry()
def eventHandling(self, type_str, event): if self.guard(): for t in self.triggers: if t.check(): self.transition()
def transition(self): self.source.exit() self.target.entry()

 

迁移类Transition是状态图中的迁移线的抽象。这个类会管理若干迁移条件Trigger类的实例。迁移线接受到事件通知时,会对每一个迁移条件进行检查。只要有一个条件满足,就会触发迁移。

 

 

class Trigger:    def __init__(self, context):        self.context = context
def entry(self): pass def check(self): pass

迁移条件类Trigger只一个抽象类,定义了迁移条件的基本接口。

 

 

import timefrom Foundation.Trigger import *
class TimerTrigger(Trigger): def __init__(self, context, timer): Trigger.__init__(self, context) self.start = 0 self.timer = timer
def entry(self): self.start = time.time()
def check(self): return (time.time() - self.start) > self.timer

事件迁移条件TimerTrigger检查从进入开始计算的时长,如果超过了指定的时长则check方法返回真,即条件满足。

 

from Foundation.Trigger import *
class ValueTrigger(Trigger): def __init__(self, context, target, value): Trigger.__init__(self, context) self.value = value self.target = target
def entry(self): pass def check(self): return self.value == eval(self.target)

 

值迁移条件类ValueTriggerr检查给定变量的值,如果和指定的值相等则check方法返回真,即条件满足。

 

实现信号灯

 

从这里开始是信号灯相关的代码。

 

 

class LightState(State):    def __init__(self, owner, color, context, blink=False):        State.__init__(self, owner, context)        self.color = color        self.blink = blink        self.counter = 0    def entry(self):        State.entry(self)        self.context.light_on(self.color)        self.counter = 0    def exit(self):        State.exit(self)        self.context.light_off(self.color)
def eventHandling(self, e_type, event): if self.blink: self.counter = self.counter + 1 if self.counter >= 5: self.context.toggle(self.color) self.counter = 0 State.eventHandling(self, e_type, event)

 

信号灯状态类LightState继承自State,它的功能是实现信号灯的状态。对于信号灯状态类来讲,context就是信号灯设备。color参数用于指定本状态管理的灯的颜色,而blink用来是定是否闪烁。

 

 

class TrafficLight:    def __init__(self, parent):        self.canvas = Canvas(parent, bg='#FFFFFF', relief=SUNKEN)        self.canvas.config(width=300, height=100)        self.canvas.pack()        self.light = dict()        self.light['red'] = \            self.canvas.create_oval(10, 10, 90, 90, fill='#FF0000', disabledfill='#770000')        self.light['yellow'] = \            self.canvas.create_oval(110, 10, 190, 90, fill='#FFFF00', disabledfill='#777700')        self.light['green'] = \            self.canvas.create_oval(210, 10, 290, 90, fill='#00FF00', disabledfill='#007700')
self.light_off('red') self.light_off('yellow') self.light_off('green') self.run = False def light_on(self, light): self.canvas.itemconfig(self.light[light], state=NORMAL)
def light_off(self, light): self.canvas.itemconfig(self.light[light], state=DISABLED)
def toggle(self, light): if self.canvas.itemcget(self.light[light], 'state') == NORMAL: self.light_off(light) else: self.light_on(light)
def stop(self): self.light_off('red') self.light_off('yellow') self.light_off('green')

 

信号灯类作为状态类的上下文,欢迎Python的tkinter库提供了亮灯,灭灯和切换灯状态的功能给LightState类使用。

 

 

class TrafficLightMachine(StateMachine):    def __init__(self, context):        StateMachine.__init__(self, None, context)
# 初始状态 self.initial = InitialState(self, context) # 红灯 red = LightState(self, 'red', context) # 绿灯 green = LightState(self, 'green', context) # 绿灯闪烁 green5s= LightState(self, 'green', context, True) # 黄灯 1s间隔闪烁 yellow = LightState(self, 'yellow', context) # 红灯 1s闪烁 red5s = LightState(self, 'red', context, True) # 终止状态 final = FinalState(self, context)
# 初始状态-红灯 无条件迁移 i2r = Transition(self, context, self.initial, red)
# 红灯-绿灯 r2g = Transition(self, context, red, green) #5s迁移 r2g.addTrigger(TimerTrigger(context, 5))
# 绿灯-绿灯闪 g2g5 = Transition(self, context, green, green5s) # 5s迁移 g2g5.addTrigger(TimerTrigger(context, 3))
# 绿灯闪-黄灯 g52y = Transition(self, context, green5s, yellow) #5s迁移 g52y.addTrigger(TimerTrigger(context, 2))
# 黄灯-红灯 y2r = Transition(self, context, yellow, red) # 3s迁移 y2r.addTrigger(TimerTrigger(context, 3))
# 红灯-红灯闪 r2f = Transition(self, context, red, red5s) # 监控context.run是否为False r2f.addTrigger(ValueTrigger(context, 'self.context.run', False))
# 红灯闪-终了 r5s2f = Transition(self, context, red5s, final) # 5s r5s2f.addTrigger(TimerTrigger(context, 5))
def stop(self): self.context.run = False def entry(self): StateMachine.entry(self) self.context.run = True def exit(self): self.context.stop()

 

信号灯状态机类TrafficLightMachine定义了信号灯的状态,个状态之间的迁移和迁移条件。

 

 

if __name__ == '__main__':    root = Tk()    tl = TrafficLight(root)    tlm = TrafficLightMachine(tl)    Button(root, text='Start', command=tlm.entry).pack(side=LEFT, fill=X, expand=1)    Button(root, text='Stop', command=tlm.stop).pack(side=LEFT, fill=X, expand=1)    t = Timer(root, 100, lambda:tlm.eventHandling('on_timer', ''))    t.start()    root.mainloop()

 

接下来就简单了:定义一个简单的画面,启动个给状态机发事件的Timer就可以了。

 

觉得本文有帮助?请分享给更多人。

阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】

举报

相关推荐

0 条评论