Python中的状态模式

Python中的状态模式,python,state-pattern,Python,State Pattern,在Python中实现状态设计模式时,我遇到了一些问题 我是Python新手,编写了一些代码来尝试回答向我提出的问题: 为一个简单的ATM机编写代码,允许用户插入他们的卡,输入他们的PIN,请求现金和弹出卡。 对显示状态模式使用的系统使用以下对象模型。您需要确定每个操作要更改为什么状态 有关更多信息,请参见下面的UML图: 下面是我的尝试 import re class AtmState(object): name = "ready" allowed = [] de

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

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

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

有关更多信息,请参见下面的UML图:

下面是我的尝试

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)
我最大的困惑是如何使用类实现它?我能够获得正确的逻辑,但我正在努力使用状态设计模式实现


任何指导都将不胜感激

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

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

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


通过从其中一个方法调用
atm.change()
方法(
atm
atm
类添加的附加参数),可以在状态之间进行切换。

这里是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