美文网首页
设计模式(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