想象一下,我有一个代表简单洗衣机的课程。它可以按以下顺序执行以下操作:开启 - >洗 - >离心机 - >关闭。我看到两个基本的选择:
我可以使用方法 turnOn(),wash(int minutes),离心(int revs),turnOff()。这个问题就是接口说没有关于正确的操作顺序。如果客户端在打开机器之前尝试离心,我最多可以抛出InvalidOprationException。我还可以使用separete程序类,它将离心机转速和洗涤时间分别传给WashingMachine,并简化这些方法。
我可以让课程本身处理正确的过渡,并使用单一方法 nextOperation()。另一方面,问题在于语义很差。客户端不知道调用nextOperation()时会发生什么。想象一下,您实现了离心机按钮的单击事件,因此它调用nextOperation()。打开机器后,用户按下离心机按钮!机器开始洗了。我可能需要在我的类上使用一些属性来参数化操作,或者可能需要一个带有washLength和centrifugeRevs字段的单独的Program类,但这不是真正的问题。
哪种替代方案更好?或者也许还有一些我错过的其他更好的选择?
答案 0 :(得分:15)
我有一个'高级'State Machine
类来控制每个州的进入/运行/退出(状态可能是'填充','洗','冲洗','清空' ','旋转干'等)
绘制所需状态的State Transition Diagram,包括(针对每个州)
您可能需要也可能不需要进入/退出条件(例如,您可以在某些情况下强制执行进入/退出操作的条件)。出于安全原因,一些条件可能是好的(例如退出'待机'状态或进入'危险'状态,如旋转干燥)
您还可以创建Transitions
,用于定义状态之间的链接。过渡已经
同样,您可能不需要所有这些,并且许多转换仅指定了“from”和“to”状态。
在这两种情况下,尽量让每个State
和Transition
保持为需要一样简单(尝试将条件/操作放在最有意义的地方) ,显然,由于通用设计,在State
和 a Transition
中定义了这些内容的潜在翻倍)
此时应该非常明显的是,您可以创建一个相当通用的State
类,其中包含所有这些内容的抽象/可重载函数,同样适用于Transition
类。 State Machine
类可以根据请求的转换,根据需要调用每个成员函数。
如果您使其特别通用,那么States
和Transitions
可以在构建时向State Machine
注册,或者您可以只编码State Machine
来包含他们都是。
然后,您可以从在状态中请求转换(即,特定状态可以知道它何时结束,并知道下一个要转到哪个状态),或者您可以拥有一个控制的外部类状态转换(每个状态非常简单,纯粹关注自己的行为,外部类决定转换的顺序和时间)。
根据我对这些事情的经验,将每个动作的故意顺序/时间的高级逻辑与对某些硬件事件的反应的低级逻辑分开是个好主意(例如,当转换到'填充'状态时水位达到了。)
这是一个非常通用的设计,你可以通过一系列不同的方式实现完全相同的功能 - 很少有单 正确的做事方式。
答案 1 :(得分:4)
我认为 turnOn(), wash(), centerfuge()等应该是私有/受保护的方法。公共界面应为 doTheWash(WashMode模式)。洗衣机本身知道它支持的模式以及如何使它们工作,洗衣机的用户不需要参与操作的顺序或持续时间。期望洗衣机类的作者以合理的顺序调用私人方法是合理的。
答案 2 :(得分:1)
大多数西方洗衣机使用计时器从一个循环移动到另一个循环。该计时器可以被认为是各种状态机。然而,重要的是要意识到一台洗衣机本身运行,而不是用户。用户设置初始模式,然后继续开展业务。
在内部,你可能有Wash,Rinse,Spin私有函数,实际的接口是SetCycle()和Start()以及Stop()。您可能还有一些其他属性,如水位,搅拌速度,水温等......
开始导致时间提前,一段时间后进入下一个状态,直到最后完成。
答案 3 :(得分:1)
每台洗衣机都有控制器和程序。对于较旧和简单的机器,程序和控制器被集成到一个相当复杂的旋钮中,现代和更昂贵的机器都有一台计算机。
在任何一种情况下,机器都有其他子系统,控制器通知子系统执行某些操作,例如锁门,加热水直到40° C 或旋转鼓。
控制器本身知道序列,它非常线性,基于老机器的计时器或现代系统的复杂,它们根据传感器数据改变序列。但在任何一种情况下,它都会执行一系列命令,这些命令可能会通知传感器和演员。
如果从这个角度看洗涤系统,你可能想要使用控制器内的命令模式来模拟洗涤程序和观察者用于通知子系统的模式(例如:门开关监听控制器以获取锁定门的信号)。
答案 4 :(得分:0)
第一个正是我要做的。 wash
和centrifuge
将检查跟踪机器是否打开的标志,如果不是,则检查异常,但是没有强制的操作顺序;如果您确实感到需要,可以在centrifuge
之前致电wash
答案 5 :(得分:0)
我甚至会看着代表们。因此,对于特定的洗涤循环类型,起伏,轻盈,沉重,经济等我会为每个人说一个界面。
然后,使用泛型,我会创建我想要的合适的洗涤类型。
洗涤类型将有一个洗涤事件,按顺序委托执行的循环。
答案 6 :(得分:0)
洗衣机应该知道如何正确运行负载。也许在清洗负载之前,预计会设置某些配置项(默认值,如果未设置),例如什么类型的负载(灯光与黑暗)或大小(小,中,大)。用户(另一个类)应该知道如何正确配置要运行的负载,但是为了清洗负载,洗衣机应该只需要暴露一种方法,例如washLoad
。在内部,它可以管理什么时候需要调用洗衣机。
或者,你仍然可以暴露公共功能以打开和关闭洗衣机,然后如果在洗衣机关闭时调用washLoad
,则抛出异常是合适的。此外,在这种情况下,我可能会通过单个方法setLoadOptions
进行洗衣机设置,这将使另一个类类似于LoadOptions
并定义您想要配置的任何特征。如果洗衣机没有打开,我会想这个方法也会抛出异常。
答案 7 :(得分:0)
在我的洗衣机上,您可以使用两个旋钮。我对你的情况有什么限制感到有些困惑。
当您“激活”一个循环(通过旋转旋钮)时,如果您将机器转到中间或开始循环,机器将开始运行。
这是一个非常基本的例子,说明如何将其翻译成一个类:
class WashingMachine
{
public void Activate(Cycle c, LoadSize s);
...
}
答案 8 :(得分:0)
最近我一直在功能性并使用 Martin Fowler / JQuery链接流畅风格的不可变对象。
代码:
public class Washer {
public void exampleRun() {
new On()
.wash(30)
.spin(100)
.turnOff();
}
public abstract static class State {}
public static class On extends State {
public Wash wash(int minutes) {
return new Wash(minutes);
}
}
public static class Wash extends State {
private int minutes;
public Wash(int minutes) {
super();
this.minutes = minutes;
}
public Spin spin(int revs) {
return new Spin(revs);
}
}
public static class Spin extends State {
private int revs;
public Spin(int revs) {
super();
this.revs = revs;
}
public Off turnOff() {
return new Off();
}
}
public static class Off extends State{}
}
答案 9 :(得分:0)
选项A类似于手动清洗器,而选项B类似于自动清洗器。这两个选项对其定位用户都很有用:
选项A的用户可以享受手动编程整个过程(例如加倍洗涤循环,或者如果你在离心圆圈时关闭机器那么教小一点)。
选项B的用户可以享受简单并写一个虚拟连续剧 - 在21秒内教你自洗: - )