美文网首页
设计模式(python实现)--状态模式(State)

设计模式(python实现)--状态模式(State)

作者: 远行_2a22 | 来源:发表于2020-02-01 20:17 被阅读0次

    State

    动机(Motivation)

    • 对象状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
    • 如何在运行时根据对象的状态来透明地改变对象的行为?

    模式定义

    允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。
    ——《设计模式》GoF

    要点总结

    • State模式将所有与一个特定状态相关的行为都放入一个State的子对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
    • 转换是原子性的
    • 与Strategy模式类似

    例子

    # -*- coding:utf-8 -*-unique
    from enum import Enum
    
    
    class AimStateAction(Enum):
        """状态机行为类型,是状态机接受的外部指令"""
        IDLE = 1
        MOVE = 2
        JUMP = 3
        CROUCH = 4
        FIRE = 5
        SHOOT_AIM = 6
    
    
    class AimStateType(Enum):
        """状态类型"""
        IDLE = 1
        MOVE = 2
        JUMP = 3
        CROUCH = 4
        FIRE = 5
        SHOOT_AIM = 6
    
    # 每个状态对应的准星大小
    StateAimSizeMap = {
        AimStateType.IDLE: 0,
        AimStateType.MOVE: 2,
        AimStateType.JUMP: 3,
        AimStateType.CROUCH: -1,
        AimStateType.FIRE: 1,
        AimStateType.SHOOT_AIM: -3,
    }
    
    # 状态机表,行为和状态之间的关系
    AVATAR_ACTION_MAP = {
    
        AimStateAction.IDLE: {'enter': (AimStateType.IDLE,),
                           'break': (AimStateType.MOVE, AimStateType.JUMP, AimStateType.CROUCH, AimStateType.SHOOT_AIM),
                           'cancel': ()},
    
        AimStateAction.MOVE: {'enter': (AimStateType.MOVE,),
                           'break': (),
                           'cancel': ( AimStateType.MOVE,)},
    
        AimStateAction.JUMP: {'enter': (AimStateType.JUMP,),
                           'break': (AimStateType.CROUCH, AimStateType.SHOOT_AIM,),
                           'cancel': ( AimStateType.JUMP,)},
    
        AimStateAction.CROUCH: {'enter': (AimStateType.CROUCH,),
                             'break': (),
                             'cancel': (AimStateType.CROUCH, AimStateType.JUMP,)},
    
        AimStateAction.SHOOT_AIM: {'enter': (AimStateType.SHOOT_AIM,),
                             'break': (),
                             'cancel': (AimStateType.SHOOT_AIM, AimStateType.JUMP,)},
    }
    
    
    class ShootAim(object):
        def __init__(self):
            self.aim_state_map = {}
    
        def handle_action(self, aim_action):
            print('-'*30)
            print('handle_action:%s', aim_action)
    
            if not self.check_aim_action(aim_action):
                return False
    
            for cancel_type in AVATAR_ACTION_MAP[aim_action]['cancel']:
                if self.get_cur_aim_state(cancel_type):
                    self.cancel_action(cancel_type)
    
            for break_type in AVATAR_ACTION_MAP[aim_action]['break']:
                self.break_state(break_type)
    
            for enter_type in AVATAR_ACTION_MAP[aim_action]['enter']:
                self.enter_state(enter_type)
    
                # 状态更新后,更新准星大小
            self._update_aim_size()
    
        def check_aim_action(self, aim_action):
            if aim_action not in AVATAR_ACTION_MAP:
                print('check_aim_action failed: aim_action:%s not in AVATAR_ACTION_MAP')
                return False
            return True
    
        def get_cur_aim_state(self, aim_type):
            return self.aim_state_map.get(aim_type, False)
    
        def enter_state(self, aim_type):
            if self.get_cur_aim_state(aim_type):
                return True
            self.aim_state_map[aim_type] = True
            print('enter state:%s' % aim_type)
            return True
    
        def break_state(self, aim_type):
            if not self.get_cur_aim_state(aim_type):
                return True
            self.aim_state_map[aim_type] = False
            print('break state:%s' % aim_type)
            return True
    
        def cancel_action(self, aim_type):
            print('cancel_action, because can not break state:%s' % aim_type)
    
        def _update_aim_size(self):
            # 根据当前状态计算准星大小
            aim_size = 0
            for state, value in self.aim_state_map.items():
                if value:
                    aim_size += StateAimSizeMap[state]
    
            print('aim_state_map:', self.aim_state_map)
            print('aim_size:', aim_size)
    
    
    if __name__ == '__main__':
        shoot_aim = ShootAim()
        shoot_aim.handle_action(AimStateAction.MOVE)
        shoot_aim.handle_action(AimStateAction.CROUCH)
        shoot_aim.handle_action(AimStateAction.JUMP)
        shoot_aim.handle_action(AimStateAction.SHOOT_AIM)
    
    
    • 射击游戏中,玩家状态会影响射击的准星大小,而准星大小决定子弹的散射范围。
    • 准星大小和玩家的行为相关,比如移动、跳跃、爬、射击等。
    • AVATAR_ACTION_MAP配置了玩家的行为和状态之间的关系
      (1) AVATAR_ACTION_MAP --enter 配置了行为进入哪个状态。比如:跳跃行为AimStateAction. JUMP可以进入跳跃状态AimStateType. JUMP
      (2) AVATAR_ACTION_MAP --break 配置了行为可以打断当前状态。比如:跳跃行为AimStateAction. JUMP可以打断匍匐状态AimStateType. CROUCH
      (3) AVATAR_ACTION_MAP --cancel 配置了行为不能打断的状态。比如:开镜行为AimStateAction. SHOOT_AIM不能打断跳跃状态AimStateType. JUMP。即玩家不能一边跳跃一边开倍镜。
    • 代码中通过接口enter_state()break_state()cancel_action()来处理状态的进入状态的打断状态不能打断时的处理逻辑。
    • 状态可以并存,比如可以在开倍镜后,一边移动,一边开抢
    • 最后根据当前状态计算准星大小

    相关文章

      网友评论

          本文标题:设计模式(python实现)--状态模式(State)

          本文链接:https://www.haomeiwen.com/subject/nxyxxhtx.html