状态模式-订单应用

今天翻了翻《领域驱动设计与模式实战》,里面详细讲解了“状态模式”,说来我对它并不陌生,几年前,我在看《Java与模式》的时候就仔细研究过,不过这么多年来却从没在实战中应用过,并不是没有遇到合适的场景,即便在前几天结束的一个项目中,还涉及订单状态来着,可我却是用最俗的if/else编码的,书算是白看了,为了不让悲剧重演,我决定重新温习一下状态模式,加深一下印象。

无图无真相,下面看看订单在生命周期里的状态迁移:



如图所示:订单状态有New Order,Registered,Granted,Shipped,Invoiced,Cancelled,相当复杂,在不同的状态执行操作时会产生不同的影响,比如说我们要执行AddOrderLine的话,要判断订单状态,如果是Registered或Granted状态的话,订单状态会变成New Order,如果是New Order状态的话,则状态保持不变,如果是其它状态的话,则不允许AddOrderLine操作。

如果我们不使用状态模式的话,那么代码里免不了充斥着if/else,所有涉及状态的操作都会被拖累,这还不算,一旦要是加入新的状态(比如说加入一个退货状态),你不得不修改原有的代码,挨个加上elseif,方法代码会变得越来越冗长,开闭原则算是没戏了,维护这样的代码,早晚有一天会崩溃的。

下面看看状态模式是如何解决此类问题的,见UML图:




引入OrderState对象,原有的SalesOrder对象中的方法,只要涉及状态的,如:AddOrderLine等,都委派给具体的OrderState对象处理,从而避免了if/else的坏味道,这也是多态的威力所在。说到这里,有些读者可能会把状态模式和策略模式搞混了,确实,它们很相像,策略模式同样可以替换掉代码里if/else的坏味道,至于二者的区别,主要在于状态模式中,对象有明显的状态迁移,比如说用户有登录状态,在登陆前是未登录状态,在登陆后是登录状态,这里有明显的状态迁移;至于策略模式,则主要是算法的分离,而不存在状态的迁移,比如说下馆子结账时,满100元九折,满200元八折,就是策略模式,这里两个打折策略只能选一个,非此即彼,不存在迁移的可能性,有时候饭馆会进行累计消费的优惠,可能这次打九折,下次打八折,看似状态迁移了,但顾客的多次就餐行为通常是独立的,所以这是两个对象生命周期中的状态,而非一个对象生命周期中的两次状态迁移。

回到前面的订单例子,看看当执行AddOrderLine时,订单如何从Registered状态迁移到NewOrder状态:

代码(at pastebin.com):

01 class SalesOrder
02 {
03     protected $state;
04
05     public function __construct()
06     {
07         $this->state = new Registered($this);
08     }
09
10     public function setState($state)
11     {
12         $this->state = $state;
13     }
14
15     public function AddOrderLine()
16     {
17         $this->state->AddOrderLine();
18     }
19
20     // ...
21 }
22
23 abstract class OrderState
24 {
25     protected $order;
26
27     public function __construct($order)
28     {
29         $this->order = $order;
30     }
31
32     public function AddOrderLine()
33     {
34         // ...
35     }
36
37     // ...
38 }
39
40 class NewOrder extends OrderState
41 {
42     public function AddOrderLine()
43     {
44         // ...
45     }
46 }
47
48 class Registered extends OrderState
49 {
50     public function AddOrderLine()
51     {
52         $this->order->setState(new NewOrder($this->order));
53     }
54
55     // ...
56 }
57
58 $salesOrder = new SalesOrder();
59
60 var_dump($salesOrder);
61
62 $salesOrder->AddOrderLine();
63
64 var_dump($salesOrder);

说明:订单状态对象的实例化必须在订单对象中完成,这是因为订单状态对象如果单独存在的话是没有意义的,它的生命周期依赖于订单对象。

如果有兴趣的可以继续参考”A Head Start on Domain-Driven Design Patterns“。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
状态模式是一种行为型设计模式,它允许对象在其内部状态改变时改变其行为。状态模式将对象的行为封装在不同状态类中,使得对象在不同状态下具有不同的行为。状态模式的核心思想是将状态的控制转移到状态类中,从而简化了对象的复杂度,使得对象的状态转换更加清晰。 状态模式应用场景包括: 1. 对象的行为取决于其状态:当一个对象的行为取决于其状态,并且其行为随着状态的改变而改变时,可以使用状态模式。例如,一个订单不同状态下的处理方式不同。 2. 代码中包含大量的条件语句:当代码中包含大量的条件语句,并且这些条件语句表示对象的不同状态时,可以使用状态模式状态模式可以将不同状态的处理分离出来,使得代码更加清晰。 3. 对象的状态转换规则复杂:当对象的状态转换规则非常复杂时,可以使用状态模式状态模式可以将状态转换规则封装在状态类中,使得状态转换更加灵活、可扩展。 4. 需要增加新的状态:当需要增加新的状态时,可以使用状态模式。通过增加新的状态类,可以很容易地扩展状态模式。 总之,状态模式是一种非常实用的设计模式,它可以将对象的状态转换封装在状态类中,使得对象的状态转换更加灵活、可扩展,降低了对象的复杂度,提高了代码的可读性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值