如何用飞镖增强的枚举应用状态模式
DART 2.17现在有了增强的枚举。 matt Carroll在他的评论中提到的您可以使用新的枚举来实现状态模式。我对此有麻烦,因为我在状态模式和增强的枚举方面都很弱。
这是状态模式的一个示例,没有这个youtube视频我转换为DART:
// https://www.youtube.com/watch?v=MGEx35FjBuo&t=12s
void main() {
final atmMachine = AtmMachine();
atmMachine.insertCard();
atmMachine.ejectCard();
atmMachine.insertCard();
atmMachine.insertPin(1234);
atmMachine.requestCash(2000);
atmMachine.insertCard();
atmMachine.insertPin(1234);
}
abstract class AtmState {
void insertCard();
void ejectCard();
void insertPin(int pin);
void requestCash(int cashToWithdraw);
}
class AtmMachine {
AtmMachine() {
_hasCard = HasCard(this);
_noCard = NoCard(this);
_hasPin = HasPin(this);
_outOfMoney = NoCash(this);
atmState = _noCard;
if (cashInMachine <= 0) {
atmState = _outOfMoney;
}
}
late AtmState _hasCard;
late AtmState _noCard;
late AtmState _hasPin;
late AtmState _outOfMoney;
late AtmState atmState;
int cashInMachine = 2000;
bool correctPinEntered = false;
void setNewAtmState(AtmState value) {
atmState = value;
}
void setCashInMachine(int value) {
cashInMachine = value;
}
void insertCard() {
atmState.insertCard();
}
void ejectCard() {
atmState.ejectCard();
}
void requestCash(int amount) {
atmState.requestCash(amount);
}
void insertPin(int pin) {
atmState.insertPin(pin);
}
AtmState get hasCardState => _hasCard;
AtmState get noCardState => _noCard;
AtmState get hasPinState => _hasPin;
AtmState get outOfMoneyState => _outOfMoney;
}
class HasCard implements AtmState {
HasCard(this.atmMachine);
final AtmMachine atmMachine;
@override
void ejectCard() {
print('Card ejected');
atmMachine.setNewAtmState(atmMachine.noCardState);
}
@override
void insertCard() {
print('You cannot enter more than one card.');
}
@override
void insertPin(int pin) {
if (pin == 1234) {
print('Correct pin');
atmMachine.correctPinEntered = true;
atmMachine.setNewAtmState(atmMachine.hasPinState);
} else {
print('Wrong pin');
atmMachine.correctPinEntered = false;
print('Card ejected');
atmMachine.setNewAtmState(atmMachine.noCardState);
}
}
@override
void requestCash(int cashToWithdraw) {
print('Enter pin first');
}
}
class NoCard implements AtmState {
NoCard(this.atmMachine);
final AtmMachine atmMachine;
@override
void ejectCard() {
print('Please enter a card first.');
}
@override
void insertCard() {
print('Please enter a pin.');
atmMachine.setNewAtmState(atmMachine.hasCardState);
}
@override
void insertPin(int pin) {
print('Please enter a card first.');
}
@override
void requestCash(int cashToWithdraw) {
print('Please enter a card first.');
}
}
class HasPin implements AtmState {
HasPin(this.atmMachine);
final AtmMachine atmMachine;
@override
void ejectCard() {
print('Card ejected');
atmMachine.setNewAtmState(atmMachine.noCardState);
}
@override
void insertCard() {
print('You cannot enter more than one card.');
}
@override
void insertPin(int pin) {
print('Already entered pin');
}
@override
void requestCash(int cashToWithdraw) {
if (cashToWithdraw > atmMachine.cashInMachine) {
print('There is not enough cash in this machine');
ejectCard();
} else {
print('You got $cashToWithdraw dollars');
atmMachine.setCashInMachine(atmMachine.cashInMachine - cashToWithdraw);
ejectCard();
if (atmMachine.cashInMachine <= 0) {
atmMachine.setNewAtmState(atmMachine.outOfMoneyState);
}
}
}
}
class NoCash implements AtmState {
NoCash(this.atmMachine);
final AtmMachine atmMachine;
@override
void ejectCard() {
print('We do not have money');
}
@override
void insertCard() {
print('We do not have money');
}
@override
void insertPin(int pin) {
print('We do not have money');
}
@override
void requestCash(int cashToWithdraw) {
print('We do not have money');
}
}
我的下一步将是将状态类转换为枚举,但这是我遇到的困难:
- 我无法将
atmmachine
上下文传递给枚举constructor因为我需要为每个枚举状态都有一个初始值,并且我无法使用atmmachine()
,因为构造函数需要为const
。 - 我想我可以将对
atmmachine
的引用传递到每个方法中,然后对状态进行开关,但是该状态模式应该如何工作?状态模式不应该避免大量的开关语句吗?还是在州以外?
有没有人使用DART的增强枚举来实施状态模式?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
状态模式
对于我的普通开发人员来说,模式通常会产生误导。您的问题使我将“状态模式”与实现“有限状态机”相关联。这并不是状态模式的目的。相反,它允许根据其为状态修改对象的行为。这意味着在对象上调用相同的功能将具有不同的效果/结果/结果,具体取决于其当前状态。
在您的示例中,ATM有限状态机看起来有点像这样:
这不是完整的,也不是100%准确,只是为了说明这一点。您还需要多个操作,因此您需要根据每个状态处理每个操作。与往常一样,根据用例,您应该避免尝试以任何代价应用模式(“我可以吗?”与“我应该这样做吗?”)。
此QA 对枚举与状态模式有一些有趣的见解。
第一个说明是,当您从当前状态有多个过渡时,无论您使用哪种语言或模式,都将有一个条件检查以确定下一个状态。在这种情况下,, switches ,
bools
等。当过渡从一个状态到另一个状态唯一时,状态模式将避免这些模式(请参见上面的Java文档)。这是DART中的类似示例:dart中的状态模式
增强的枚举
这些基本上是成员的枚举。显然已经有其他语言可用(例如Java),现在已经在飞镖中实现。在规格中,我们可以看到枚举如何将“ desugars”“纳入一类”。重要的是要注意:在写作时仍有一个限制,可以防止向枚举成员添加函数:
https://github.com/dart-lang/lang/lang/langyage/lange/sissues/2241
https://github.com/dart-lang/lang/lange/lange/lange/lange/lange/sissues/1048
但是,可以通过解决方法进行操作(这是我在下面的代码中所做的;请参阅上述问题2241的评论: https://github.com/dart-lang/lang/lange/lange/sissues/2241#issuecomment-1126322956 )。
在状态模式中使用
为了说明这一点,我在上面的Dart(Lightswitch)中以示例模式进行了示例模式,并用枚举代替了州类。代码的其余部分几乎相同。我删除了一些东西,例如
toString()
替代,因为这很容易理解:作为参考,如果存储库消失,则这些是已被示例中的枚举替换的状态类:
State pattern
To the average developer that I am, patterns are often misleading. Your question made me associate the "State pattern" with a way to implement a "Finite state machine". This is not exactly the purpose of the state pattern. Instead, it allows to modify the behaviour of an object based on the state it is. This means calling the same function on the object will have a different effect/result/consequence depending on which is its current state.
Basic explanation (Java)
In your example, the ATM finite state machine looks a bit like this:
This is not complete nor 100% accurate, it's only to illustrate the point. You also have more than one operation, so you need to handle each operation based on each state. As always, depending on the use case, you should avoid trying to apply a pattern at any cost ("can I do it?" vs. "should I do it?").
This QA has some interesting insight about enums vs state pattern.
A first remark is that when you have more than one transitions possible from your current state, no matter which language or pattern you use, there will be a condition to be checked to determine what will be the next state. In this case
ifs
,switches
,bools
etc. will be required anyway. The State pattern will avoid these when the transitions are unique from one state to the other (see Java document above). Here's a similar example in Dart:State pattern in Dart
Enhanced enums
These are basically enums with members. Apparently already available in other languages (ex. Java), this has now been implemented in Dart. In the specs, we can see how the enum "desugars" into a class. Important to note: there is at the time of writting still a limitation that prevents adding a function to an enum member:
https://github.com/dart-lang/language/issues/2241
https://github.com/dart-lang/language/issues/1048
However, it is possible to do it with a workaround (this is what I did in my code below; see this comment on issue 2241 above: https://github.com/dart-lang/language/issues/2241#issuecomment-1126322956).
Usage in State pattern
To illustrate this, I took the example pattern in Dart above (Lightswitch) and replaced the State classes with an enum. The rest of the code is pretty much the same. I removed some stuff like the
toString()
override as this is simple to understand:As reference in case the repo disappears, these are the State classes that have been replaced with the enum in the example: