【问题标题】:State design pattern in compliance with Liskov符合 Liskov 的状态设计模式
【发布时间】:2017-02-26 04:24:27
【问题描述】:

我正在设计一个订单系统,状态设计模式似乎很合适,因为订单可以更改其状态,从而更改订单允许的功能。下面是我的基本类图:

我不喜欢这种方法,因为客户看不到某个方法是否受支持并且违反了 Liskov 原则。我在下面创建了一个替代方案:

我更喜欢这个,但客户端仍然必须检查是否支持某个方法。但是他们仍然可以调用不受支持的方法并获得异常。这是否仍然违反 Liskov 原则?

是否有更好的设计符合 Liskov 并防止用户为特定状态调用无效方法?

【问题讨论】:

  • 我不明白您所说的“无法查看是否支持某个方法”是什么意思。具体的 OrderState 都将实现 OrderState 方法。当状态当前不允许操作时,它们中的一些会引发异常。这并不违反 LSP imo。另外,我看不出这有什么不同,而不是直接引发 UnsupportedOpEx,而是首先调用其中一个 can* 方法然后引发它。 Imo,允许 Order 的任何消费者调用 can* 方法更糟糕,因为它将责任移到了 Order 对象之外。
  • 在类图 1 中,客户端无法检测操作是否对特定状态有效。那么就没有办法知道是否应该显示支付按钮或取消按钮。在我对 LSP 的理解中,抛出 notSupportedException 是破坏 LSP 的常用方法。这是因为子类型不做所说的,因此子类型不能替代它的基类型。
  • 具体的OrderStates 都将实现OrderState 接口和方法。所有这些方法都将——如果可能的话,通过接口契约——潜在地引发异常。因此,您可以将 OrderState 的任何具体子类型替换为另一个。因此,您不会违反 Liskov 的替代原则。顺便说一句,您可以使用相同的模式来绘制按钮。只需将按钮渲染器传递给 OrderState 并让它从具体的 OrderState 返回正确的按钮,例如Confirmed 将告诉 ButtonRenderer 呈现支付按钮等。
  • 我查找了 LSP 的定义“如果 S 是 T 的子类型,则类型 T 的对象可以替换为类型 S 的对象(即,类型 S 的对象可以替换类型 T)而不改变该程序的任何所需属性(正确性、执行的任务等)”违反了正确性,因为您在订单上调用 pay 方法并且您希望您正在支付订单。相反,当前状态将永远无法支付。因此这会破坏系统的正确性。
  • 参见programmers.stackexchange.com/questions/181922/…,特别是programmers.stackexchange.com/a/181941/49178 - 如果预期会出现异常,则不会违反正确性。否则,任何引发异常的方法都会违反 LSP。

标签: oop architecture liskov-substitution-principle state-pattern


【解决方案1】:

您展示的不是状态模式。状态模式在其内部状态改变时改变对象的行为。例如,灯开关可以在您切换时打开或关闭灯,具体取决于其状态(同一方法上的不同行为)。

有了这个 Order 接口(4 种差异方法),我看不到引入 State 模式的任何好处。它只会无缘无故地使事情复杂化。但我不知道所有细节,所以接下来该怎么做取决于你。

查看此链接以查看状态模式实现示例https://sourcemaking.com/design_patterns/state

【讨论】:

  • 我在设计中使用状态模式的原因是因为顺序改变了状态,这改变了它的行为。如果我创建分隔订单类,则订单无法在运行时更改其行为。您对不同的模式有什么建议吗?
  • 最简单的解决方案是具有 4 个 diff 方法的单个 Order 模型,当您在对象处于不适当状态时尝试使用方法时会引发异常。通常,当我不知道所有细节时,我会选择最简单的解决方案,这样以后更容易重构。但这一切都取决于您的应用程序/代码中的订单使用情况。例如,如果您有一个带有 proceedToTheNextState 方法的 Order 接口,您可以创建 4 个状态:Confirm、Cancel、Pay、Ship,它们完成与状态相关的所有工作并转换到下一个状态。
  • 此外,您可能需要拆分订单行为逻辑的原因有很多。并且由此产生的解决方案可能不是状态模式的确切实现。如果这一切都是有原因的,那一切都好。如果您遵循子类中的接口和方法定义,则无法破坏 LSP。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-27
  • 1970-01-01
  • 2011-09-03
  • 1970-01-01
  • 1970-01-01
  • 2019-05-28
  • 1970-01-01
相关资源
最近更新 更多