在用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)
对我来说最大的困惑是如何使用类实现它?我能够使用正确的逻辑,但是我在使用状态设计模式进行实施时遇到了麻烦。
任何指导将不胜感激。
答案 0 :(得分:0)
Atm
不应从AtmState
继承,而不能继承(或从object
继承)。它仅应包含:state
变量,用于更改状态的change
方法,并且对于AtmState
中的每个方法,一个方法将调用当前state
中相同的命名方法,并附加一个参数名为atm
包含正在调用的Atm
对象(上下文)。
AtmState
应该只包含没有实现(且没有变量)的方法,因为它是原始模式中的接口。对于Python,您应该使其成为具有抽象方法的抽象类,请参见模块abc
的用法。
从AtmState
派生的具体类现在应该实现这些方法。通常每个类实际上只需要一个或两个方法,其余的应该只打印一个错误。例如。 NoCard.ejectCard()
方法仅显示一个错误,指出无法弹出卡。
通过从其中一种方法调用atm.change()
方法(atm
类是Atm
类添加的附加参数)之间进行状态切换。
答案 1 :(得分:0)
这是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