【问题标题】:State Pattern in PythonPython中的状态模式
【发布时间】:2018-12-09 04:14:11
【问题描述】:

我在用 Python 实现状态设计模式时遇到了一些问题。

我是 Python 新手,并编写了一些代码来尝试回答向我提出的这个问题:

为一个简单的 ATM 编写代码,允许用户插入他们的卡、输入他们的 PIN、请求现金和弹出卡。 将以下对象模型用于显示状态模式使用的系统。您需要弄清楚每个操作要更改为什么状态。

请参阅下面的 UML 图了解更多信息:

ATM Diagram

下面是我的尝试……

import re

class AtmState(object):

    name = "ready"
    allowed = []

    def switch(self, state):
        """ Switch to new state """
        if state.name in self.allowed:
#             print("Current {} => switched to new state {}.".format(self, state.name))
            self.__class__=state
# These print statements show how you switch between states.
#         else:
#             print("Current {} => switched to {} not possible.".format(self, state.name))

    def getState(self):
        print("The current state is {}".format(self.state))

    def __str__(self):
        return self.name

    def __repr__(self):
        return r"The ATM is in a {} state.".format(self.state)

    def insertCard(self, card):
        # Set messages for card format and inserted
        wrong_format = "Please insert your card in the following format: XXXX-XXXX-XXXX-XXXX."
        card_inserted = "Card Inserted: {}"
        card_pattern='^([0-9]{4})(-?|\s)([0-9]{4})(-?|\s)([0-9]{4})(-?|\s)([0-9]{4})$'
        pattern = re.compile(card_pattern)


        if pattern.match(card) and str(self.state) in ["insert", "ready", "no card"]:
            self.state.switch(HasCard)
            print(card_inserted.format(card))
            self.state.switch(HasPin)
        elif pattern.match(card)==False and str(self.state) ["insert", "ready", "no card"]:
            print(wrong_format)
        elif str(self.state) in ["enter_pin", "withdraw"]:
            print("Card already inserted")
        elif str(self.state) in ["no card"]:
            print("Error: No Card Inserted. Please insert card.")


    def ejectCard(self):
        if str(self.state) in ["ready", "insert", "enter_pin", "withdraw"]:
            print("Card Ejected")
            self.state.switch(NoCard)
        else:
            print("Error: Card can't be Ejected - No Card Inserted")

    def requestCash(self, withdrawl):
        if str(self.state)=="withdraw":
            if self.balance >= withdrawl:
                self.balance-= withdrawl
                print("Withdrawing ${}.".format(withdrawl))
                if self.balance == 0:
                    print("Error: Out of Cash")
                else:
                    print("${} remaining in ATM.".format(self.balance))
            else:
                print("Error: Out of Cash")
        elif str(self.state)=="no card":
            print("Error: No Card inserted. Please insert your ATM card.")
        else:
            print("Error: Please enter pin.")

    def insertPin(self, pin):
        if str(self.state) == "enter_pin" and pin.isdigit() and len(pin)>=4:
            print("Pin Entered: {}".format(pin))
            self.state.switch(HasCash)
        elif str(self.state)== "no card":
            print("Error: No Card inserted. Please insert your ATM card.")
        else:
            print("Pin must be numeric and at least 4 digits.")

class HasCard(AtmState):
    name="insert"
    allowed=["no card", "enter_pin", "ready"]

class NoCard(AtmState):
    name="no card"
    allowed=["insert", "ready"]

class HasPin(AtmState):
    name="enter_pin"
    allowed=["no card", "withdraw", "insert"]

class HasCash(AtmState):
    name="withdraw"
    allowed=["no card"]

# This is known as the contect class. It does two main things:
# Defines the base state of the ATM...
# Defines a method to change the state of the ATM.
class Atm(AtmState):
    """A class representing an ATM"""

    def __init__(self, balance=2000):
        self.balance = balance
        # State of ATM - default is ready.
        self.state = NoCard()
        print("ATM has a balance of ${}".format(balance))

    def change(self, state):
        self.state.switch(state)

对我来说最大的困惑是如何使用类来实现它?我能够获得正确的逻辑,但我正在努力使用状态设计模式来实现。

任何指导将不胜感激。

【问题讨论】:

    标签: python state-pattern


    【解决方案1】:

    Atm 不应该从 AtmState 继承,而是从无继承(或从 object,没关系)。它应该只包含:state 变量、change 更改状态的方法以及AtmState 中的每个方法,该方法调用当前state 中的相同命名方法,并带有一个名为例如的附加参数。 atm 包含调用 Atm 对象(上下文)。

    AtmState 应该只包含没有实现的方法(也没有变量),因为它是原始模式中的接口。对于 Python,您应该使其成为具有抽象方法的抽象类,请参阅模块 abc 如何做到这一点。

    派生自AtmState 的具体类现在应该实现这些方法。通常每个类只需要一个或两个方法,其余的应该只是打印一个错误。例如。 NoCard.ejectCard() 方法只是显示一个不存在的卡无法弹出的错误。

    通过从方法之一调用atm.change() 方法(atmAtm 类添加的附加参数)来实现状态之间的切换。

    【讨论】:

      【解决方案2】:

      这是你的问题的简单版本的 python3 中的一个快速而肮脏的实现。

      State 是一个抽象类,使用(由 Mickeal 描述的 abc 包)它充当接口(派生类必须实现抽象方法)。实际状态接收请求并实现功能并执行状态转换这就是我将 atm 对象作为参数方法传递的原因

      import abc
      class State(object,metaclass = abc.ABCMeta):
          @abc.abstractmethod
          def eject(self, atm):
              raise NotImplementedError('')
          @abc.abstractmethod
          def insert(self, atm):
              raise NotImplementedError('')
      
      
      class NoCard(State):
          def eject(self, atm):
              print('Error : no card')
          def insert(self, atm):
              print('ok')
              atm.state  = HasCard()
      
      class HasCard(State):
          def eject(self, atm):
              print('ok')
              atm.state = NoCard()
          def insert(self, atm):
              print('Error : card already present')
      
      
      class ATM:
          def __init__(self):
              self.state = NoCard()
          def insert(self):
              self.state.insert(self)
          def eject(self):
              self.state.eject(self)
      
      if __name__ == "__main__":
          atm = ATM()
          atm.eject() # default state is no card error no card
          atm.insert() # ok state is has card
          atm.insert() # error  card already in
          atm.eject() # ok  state become no card
          atm.eject() # error no card
      

      【讨论】:

        猜你喜欢
        • 2013-02-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-15
        • 2017-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多