状态模式-订单应用

今天翻了翻《领域驱动设计与模式实战》,里面详细讲解了“状态模式”,说来我对它并不陌生,几年前,我在看《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“。

<think>好的,我现在需要回答用户关于Java中状态模式的设计与实现的问题,并且根据提供的引用内容来支持回答。首先,我要确保理解状态模式的基本概念和应用场景。 用户提供的引用中,状态模式被定义为一种行为型设计模式,允许对象在内部状态改变时改变其行为,而无需修改其代码。引用[2]提到状态模式通过将行为封装到不同状态类中,使对象在不同状态下有不同的行为。引用[4]也指出,状态模式在对象内部状态改变时改变其行为,并将行为封装在不同状态对象中。这些信息帮助我确认状态模式的核心思想是状态与行为的分离。 接下来,用户需要具体的Java示例。引用[3]提到了订单状态的处理,这可能是一个典型的应用场景,比如电商系统中的订单状态机。结合引用[1]中的电子商务项目实例,我可以设计一个订单状态的例子,展示不同状态下的行为变化。 我需要构建一个结构清晰的示例,包括上下文类(Context)和具体的状态类。根据引用中的信息,状态模式的结构通常包含一个抽象状态接口或抽象类,具体状态类实现该接口,上下文类持有当前状态并委托行为给状态对象。 此外,要确保代码符合Java规范,使用合适的设计,比如将状态转换逻辑放在具体状态类中,或者由上下文类管理状态转换。根据引用[3]中的状态机业务实现,可能状态转换由状态类自身触发,比如处理完一个操作后切换到下一个状态。 在编写示例时,需要注意状态之间的转换条件和行为。例如,订单从“待支付”变为“已支付”,再变为“已发货”等。每个状态处理的操作(如支付、发货)会导致状态变更,这需要在具体状态类中实现。 最后,根据用户提供的引用内容,我需要正确标注引用来源。例如,在解释状态模式定义时引用[2]和[4],在提到电商应用时引用[1]和[3]。 现在,我需要将这些思路整合成一个结构清晰的回答,包含定义、结构、实现步骤、示例代码和应用场景,并在适当位置添加引用标注。同时,生成相关问题以帮助用户进一步理解。</think>### Java 状态模式的设计与实现 #### 1. 定义与核心思想 状态模式(State Pattern)是一种行为型设计模式,允许对象在内部状态改变时改变其行为。其核心是将**对象的行为封装到不同状态类**中,通过切换状态对象实现行为的动态调整,避免大量条件判断语句[^2][^4]。 #### 2. 模式结构 - **上下文类(Context)**:持有当前状态对象,定义客户端调用的接口。 - **抽象状态类(State)**:定义状态行为的接口。 - **具体状态类(ConcreteState)**:实现抽象状态类的行为逻辑,可能触发状态转换。 #### 3. 实现步骤(以电商订单状态为例) ##### 步骤1:定义状态接口 ```java public interface OrderState { void pay(OrderContext context); // 支付操作 void ship(OrderContext context); // 发货操作 } ``` ##### 步骤2:实现具体状态- **待支付状态** ```java public class UnpaidState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("支付成功"); context.setState(new PaidState()); // 状态切换 } @Override public void ship(OrderContext context) { System.out.println("错误:尚未支付,无法发货"); } } ``` - **已支付状态** ```java public class PaidState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("错误:订单已支付"); } @Override public void ship(OrderContext context) { System.out.println("发货成功"); context.setState(new ShippedState()); // 状态切换 } } ``` ##### 步骤3:定义上下文类 ```java public class OrderContext { private OrderState currentState; public OrderContext() { this.currentState = new UnpaidState(); // 初始状态 } public void setState(OrderState state) { this.currentState = state; } public void pay() { currentState.pay(this); // 委托给当前状态 } public void ship() { currentState.ship(this); } } ``` #### 4. 使用示例 ```java public class Client { public static void main(String[] args) { OrderContext order = new OrderContext(); order.ship(); // 输出:错误:尚未支付,无法发货 order.pay(); // 输出:支付成功 order.ship(); // 输出:发货成功 } } ``` #### 5. 应用场景 - **订单状态流转**:如电商系统中订单的“待支付→已支付→已发货”流程[^3]。 - **游戏角色状态**:角色“站立→移动→攻击”状态切换。 - **硬件设备控制**:打印机“空闲→打印→缺纸”状态管理。 #### 6. 优势与注意事项 - **优势**:消除复杂条件分支、符合开闭原则、状态转换逻辑集中管理[^1][^2]。 - **注意事项**:避免状态类膨胀(需合理拆分粒度)、上下文类与状态类的职责边界需清晰。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值