23种设计模式(七)-状态设计模式
一. 什么是状态模式?
状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。例如:淘宝下单,订单有待付款,已付款待发货,待收货,待评价, 已完成等状态。每个状态对应的行为都是不同的, 一个状态完成会流转到下一个状态。
通常对有状态的对象进行编程,我们的解决方案是:思考可能存在的所有状态,然后使用 if-else 或 switch-case 语句来进行状态判断,然后再根据不同的状态进行不同的处理。如上面的案例–淘宝下单:
public class ClientApplication {
public static void main(String[] args) {
String status = "待付款";
if ("待付款".equals(status)) {
// 执行付款逻辑
} else if("待发货".equals(status)) {
// 执行发货逻辑
} else if("待收货".equals(status)) {
// 执行收货逻辑
} else if("待评价".equals(status)) {
// 执行评价逻辑
} else if("待收货".equals(status)) {
// 执行收获逻辑
}
}
}
大量的if…else的缺点很明显
- 违背开闭原则: 当增加一种状态的时候, 需要修改原来的逻辑
- 当状态很多的时候, 代码段很长, 臃肿, 不容易维护, 可扩展性差.
状态模式可以很好地解决这个问题。
状态模式的思想:当是条件语句表示一个对象状态转换过于复杂时,可以把条件判断中的“判断逻辑”提取出来,放在单独的类中,当前上下文处于那种状态,直接用相应的状态类对象进行处理,这样的好处是:能把原来复杂的判断逻辑简单化,消除了 if-else、switch-case 等条件判断语句,代码更有层次性,且具备良好的扩展力, 可维护性。
二. 状态模式的结构
状态模式把因环境改变而变化的对象行为包装在不同的状态类中,其目的保证对象的状态变化时, 其行为也随之改变。接下来, 我们来看看状态模式的结构。
从中我们可以看出有四个组成部分:
1. 环境类
环境类也是上下文类, 状态的变化依赖于环境, 环境记录当前的状态. 环境类拥有多种状态的对象. 环境类的状态存在多样性, 不同状态下对象的行为有所不同, 因此将状态独立出去形成单独的状态类。
> 1) 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
> 2) 在环境类中定义所有状态执行的方法.
package com.lxl.www.designPatterns.statePattern.context;
/**
* 上下文环境类
*/
public class Context {
// 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
private State state;
public Context() {
this.state = new ConcreteStateA(this);
}
public void setState(State state) {
this.state = state;
}
/**
* 在环境类中定义所有状态执行的方法.
*/
public void handler1() {
this.state.handler1();
}
public void handler2() {
this.state.handler2();
}
}
2. State抽象状态类
抽象环境中声明一个环境角色,提供各个状态类自行访问,并且提供所有状态的抽象行为,由各个实现类实现。
/**
* 状态抽象类
*/
public abstract class State {
/**
* 环境上下文
*/
public Context context;
public State(Context context) {
this.context = context;
}
/**
* 定义了所有状态的抽象方法
*/
abstract void handler1();
abstract void handler2();
}
3. 具体状态
具体状态实现,这里以定义ConcreteStateA、ConcreteStateB、ConcreteStateC三个具体状态类。具体状态类继承自State抽象类, 并实现抽象方法。
package com.lxl.www.designPatterns.statePattern.context;
public class ConcreteStateA extends State{
public ConcreteStateA(Context context) {
super(context);
}
@Override
void handler1() {
System.out.println("执行ConcreteStateA中handler1的逻辑");
this.context.setState(new ConcreteStateB(context));
}
@Override
void handler2() {
System.out.println("执行ConcreteStateA中handler2的逻辑");
this.context.setState(new ConcreteStateC(context));
}
}
在这里, 我们重写了状态类的状态方法。当执行完方法一的业务逻辑后, 将状态变更为 ConcreteStateB. 执行完handler2的业务逻辑后, 将状态变更为ConcreteStateC。
这种自动变更状态, 在调用方是无感知的。
4. Client客户端
构建Context环境上下文类的实例对象,初始化时设置初始状态是ConcreteStateA,执行行为观察结果。
package com.lxl.www.designPatterns.statePattern.context;
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.handler1();
context.handler1();
context.handler1();
context.handler2();
}
}
全部源码:
package com.lxl.www.designPatterns.statePattern.context;
/**
* 上下文环境类
*/
public class Context {
// 在环境类中维护一个抽象状态State的实例, 这个实例存储当前状态。
private State state;
public Context() {
this.state = new ConcreteStateA(this);
}
public void setState(State state) {
this.state = state;
}
/**
* 在环境类中定义所有状态执行的方法.
*/
public void handler1() {
this.state.handler1();
}
public void handler2() {
this.state.handler2();
}
}
/**
* 状态抽象类
*/
public abstract class State {
/**
* 环境上下文
*/
public Context context;
public State(Context context) {
this.context = context;
}
/**
* 定义了所有状态的抽象方法
*/
abstract void handler1();
abstract void handler2();
}
public class ConcreteStateA extends State{
public ConcreteStateA(Context context) {
super(context);
}
@Override
void handler1() {
System.out.println("执行ConcreteStateA中handler1的逻辑");
this.context.setState(new ConcreteStateB(context));
}
@Override
void handler2() {
System.out.println("执行ConcreteStateA中handler2的逻辑");
this.context.setState(new ConcreteStateC(context));
}
}
public class ConcreteStateB extends State{
public ConcreteStateB(Context context) {
super(context);
}
@Override
void handler1() {
System.out.println("执行ConcreteStateB中handler1的逻辑");
this.context.setState(new ConcreteStateC(context));
}
@Override
void handler2() {
System.out.println("执行ConcreteStateB中handler2的逻辑");
this.context.setState(new ConcreteStateA(context));
}
}
public class ConcreteStateC extends State{
public ConcreteStateC(Context context) {
super(context);
}
@Override
void handler1() {
System.out.println("执行ConcreteStateC中handler1的逻辑");
this.context.setState(new ConcreteStateA(context));
}
@Override
void handler2() {
System.out.println("执行ConcreteStateC中handler2的逻辑");
this.context.setState(new ConcreteStateB(context));
}
}
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.handler1();
context.handler1();
context.handler1();
context.handler1();
}
}
客户端直接结果分析:
- Context实例花的时候, 状态是 ConcreteStateA
- 第一次执行context.handler1();将状态切换到了ConcreteStateB
- 第二次执行context.handler1();将状态切换到了ConcreteStateC
- 第三次执行context.handler1();将状态切换到了ConcreteStateA
所以, 最后的运行结果是:
执行ConcreteStateA中handler1的逻辑
执行ConcreteStateB中handler1的逻辑
执行ConcreteStateC中handler1的逻辑
执行ConcreteStateA中handler1的逻辑
三. 状态模式的应用实例
下面以商品下单为例,来看看状态模式的应用。
在电商系统购物的过程中,订单一般有这几种状态,待付款,待出库,待收货, 待评价,完成。不同的状态下,用户看到的行为是不同的。
我们由浅入深, 来看看几种状态模式的实现。
第一种: 状态是一条线的实现逻辑
先来看看这种方法的流程:
这种状态的特点是: 上一个状态流转到下一个状态, 下一个流转到下下个,以此类推。直到最后一个没有状态流转了。来看看源码:
第一步:定义上下文环境类。
用orderStatus来记录当前的状态。并定义了所有状态的方法。
package com.lxl.www.designPatterns.statePattern.order;
/**
* 订单上下文
*/
public class OrderContext {
/**在上下文类中记录订单状态*/
private IOrderStatus orderStatus;
public OrderContext() {
// 最开始是待付款状态
this.orderStatus = new Pending();
}
public void setOrderStatus(IOrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
/**
* 业务逻辑操作
*/
public void businessHandler() {
this.orderStatus.businessHandler(this);
}
/**
* 打印当前业务
*/
public void printInfo() {
this.orderStatus.printInfo();
}
}
第二步: 定义状态抽象类。
里面定义所有状态类要实现的方法。
package com.lxl.www.designPatterns.statePattern.order;
/**
* 订单状态
*/
public interface IOrderStatus {
void businessHandler(OrderContext context);
void printInfo();
}
这里面就有两个方法, 一个是状态要执行的业务逻辑, 另一个是打印当前状态。
第三步:定义状态具体类。
这里定义了5个具体类,分别是:待付款,待出库,待收货, 待评价,完成
package com.lxl.www.designPatterns.statePattern.order;
/**
* 待付款
*/
public class Pending implements IOrderStatus{
@Override
public void businessHandler(OrderContext context) {
//执行业务, 完成付款, 进入到下一个状态
System.out.println("付款完成");
context.setOrderStatus(new WaitOut());
}
@Override
public void printInfo() {
System.out.println("当前状态等::待付款");
}
}
package com.lxl.www.designPatterns.statePattern.order;
/**
* 待出库
*/
public class WaitOut implements IOrderStatus{
@Override
public void businessHandler(OrderContext context) {
//执行业务, 完成付款, 进入到下一个状态
System.out.println("货物已出库");
context.setOrderStatus(new WaitReceive());
}
@Override
public void printInfo() {
System.out.println("当前状态等::待出库");
}
}
package com.lxl.www.designPatterns.statePattern.order;
/**
* 待收货
*/
public class WaitReceive implements IOrderStatus {
@Override
public void businessHandler(OrderContext context) {
//执行业务, 完成付款, 进入到下一个状态
System.out.println("已经收货");
context.setOrderStatus(new OrderEvaluation());
}
@Override
public void printInfo() {
System.out.println("当前状态等::待收货");
}
}
package com.lxl.www.designPatterns.statePattern.order;
/**
* 订单评价
*/
public class OrderEvaluation implements IOrderStatus{
@Override
public void businessHandler(OrderContext context) {
//执行业务, 完成付款, 进入到下一个状态
System.out.println("已经评价");
context.setOrderStatus(new Finish());
}
@Override
public void printInfo() {
System.out.println("当前状态等::待评价");
}
}
package com.lxl.www.designPatterns.statePattern.order;
/**
* 订单完成
*/
public class Finish implements IOrderStatus{
@Override
public void businessHandler(OrderContext context) {
// 执行业务, 完成付款, 进入到下一个状态
System.out.println("完成工作处理完毕");
}
@Override
public void printInfo() {
System.out.println("当前状态::订单完成");
}
}
第四步: 定义客户端类, 模拟下单的流程
package com.lxl.www.designPatterns.statePattern.order;
public class OrderClient {
public static void main(String[] args) {
OrderContext orderContext = new OrderContext();
// 开始下单
System.out.println("==========开始下单==========");
orderContext.printInfo();
// 付款
System.out.println("==========付款==========");
orderContext.businessHandler();
orderContext.printInfo();
// 货物出库
System.out.println("==========货物出库==========");
orderContext.businessHandler();
orderContext.printInfo();
// 收货
System.out.println("==========收货==========");
orderContext.businessHandler();
orderContext.printInfo();
// 评价订单
System.out.println("==========评价订单==========");
orderContext.businessHandler();
orderContext.printInfo();
// 订单完成
System.out.println("==========订单完成==========");
orderContext.businessHandler();
}
}
接下来我们来看看运行效果:
开始下单
当前状态等::待付款
付款
付款完成
当前状态等::待出库
货物出库
货物已出库
当前状态等::待收货
收货
已经收货
当前状态等::待评价
评价订单
已经评价
当前状态::订单完成
订单完成
完成工作处理完毕
第二种: 带有取消付款和退货退款状态。
退货退款最开始只能是收到货以后才能退。
这种方式和第一个有所不同, 待付款和确认收货不止有一个状态流转,当有多个状态流转,甚至是更多的状态是,我们应该如何处理呢?
第一步: 定义上下文环境类。
这里环境上下文定义了所有的状态方法。
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 订单上下文
*/
public class OrderContext {
/**在上下文类中记录订单状态*/
private IOrderStatus orderStatus;
public OrderContext() {
System.out.println("开始购物");
// 最开始是待付款状态
this.orderStatus = new Pending();
}
public void setOrderStatus(IOrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
/**
* 付款完成
*/
public void pending() {
this.orderStatus.pending(this);
}
public void waitOut() {
this.orderStatus.waitOut(this);
}
public void waitReceive() {
this.orderStatus.waitReceive(this);
}
public void confirmReceived() {
this.orderStatus.confirmReceived(this);
}
public void orderEvaluation() {
this.orderStatus.orderEvaluation(this);
}
public void finish() {
this.orderStatus.finish(this);
}
public void cancelPay() {
this.orderStatus.cancelPay(this);
}
public void refunds() {
this.orderStatus.refunds(this);
}
}
第二步:定义抽象状态类
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 订单状态
*/
public interface IOrderStatus {
/*
* 待付款
*/
void pending(OrderContext context);
/*
* 取消付款
*/
void cancelPay(OrderContext context);
/*
* 待出库
*/
void waitOut(OrderContext context);
/*
* 退货退款
*/
void refunds(OrderContext context);
/*
* 待收货
*/
void waitReceive(OrderContext context);
/*
* 确认收货
*/
void confirmReceived(OrderContext context);
/*
* 订单评价
*/
void orderEvaluation(OrderContext context);
/*
* 订单完成
*/
void finish(OrderContext context);
}
第三步:定义具体状态类。
我们这里定义了待付款,待出库,待收货, 确认收货,订单评价,订单完成,取消付款,退货退款一共8个状态。
待付款
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 待付款
*/
public class Pending implements IOrderStatus {
public Pending() {
System.out.println("当前状态::待付款");
}
@Override
public void pending(OrderContext context) {
System.out.println("付款完成了, 待出库");
context.setOrderStatus(new WaitOut());
}
/*
* 取消付款
*/
public void cancelPay(OrderContext context) {
System.out.println("取消付款");
context.setOrderStatus(new Finish());
}
@Override
public void waitOut(OrderContext context) {
System.out.println("去付款-->付款完成,待出库");
context.setOrderStatus(new WaitOut());
}
/*
* 退货退款
*/
public void refunds(OrderContext context) {
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
}
}
待出库
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 待出库
*/
public class WaitOut implements IOrderStatus {
public WaitOut() {
System.out.println("当前状态::待出库");
}
@Override
public void pending(OrderContext context) {
}
@Override
public void cancelPay(OrderContext context) {
}
/*
* 退货退款
*/
public void refunds(OrderContext context) {
System.out.println("申请退货");
context.setOrderStatus(new Refunds());
}
@Override
public void waitOut(OrderContext context) {
System.out.println("出库完成, 待收货");
context.setOrderStatus(new WaitReceive());
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
}
}
待收货
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 待收货
*/
public class WaitReceive implements IOrderStatus {
public WaitReceive() {
System.out.println("当前状态 :: 待收货");
}
@Override
public void pending(OrderContext context) {
}
@Override
public void cancelPay(OrderContext context) {
}
@Override
public void waitOut(OrderContext context) {
}
/*
* 退货退款
*/
public void refunds(OrderContext context) {
System.out.println("申请退货");
context.setOrderStatus(new Refunds());
}
@Override
public void waitReceive(OrderContext context) {
System.out.println("进行收货, 完成收货动作");
context.setOrderStatus(new Confirm());
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
}
}
确认收货
package com.lxl.www.designPatterns.statePattern.order2;
public class Confirm implements IOrderStatus{
public Confirm() {
System.out.println("当前状态 :: 确认收货");
}
@Override
public void pending(OrderContext context) {
}
@Override
public void cancelPay(OrderContext context) {
}
@Override
public void waitOut(OrderContext context) {
}
@Override
public void refunds(OrderContext context) {
System.out.println("申请退款");
context.setOrderStatus(new Refunds());
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
System.out.println("确认收货了");
context.setOrderStatus(new OrderEvaluation());
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
}
}
订单评价
package com.lxl.www.designPatterns.statePattern.order2;
import com.sun.tools.corba.se.idl.constExpr.Or;
/**
* 订单评价
*/
public class OrderEvaluation implements IOrderStatus {
public OrderEvaluation() {
System.out.println("当前状态 :: 订单待评价");
}
@Override
public void pending(OrderContext context) {
}
@Override
public void cancelPay(OrderContext context) {
}
@Override
public void waitOut(OrderContext context) {
}
@Override
public void refunds(OrderContext context) {
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
System.out.println("订单评价完了");
context.setOrderStatus(new Finish());
}
@Override
public void finish(OrderContext context) {
}
}
订单完成
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 订单完成
*/
public class Finish implements IOrderStatus {
@Override
public void pending(OrderContext context) {
}
@Override
public void cancelPay(OrderContext context) {
}
@Override
public void waitOut(OrderContext context) {
}
/*
* 退货退款
*/
public void refunds(OrderContext context) {
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
System.out.println("订单完成");
}
}
取消付款
package com.lxl.www.designPatterns.statePattern.order2;
/**
* 取消付款
*/
public class CancelPay implements IOrderStatus {
@Override
public void pending(OrderContext context) {
}
/*
* 取消付款
*/
public void cancelPay(OrderContext context) {
}
@Override
public void waitOut(OrderContext context) {
}
/*
* 退货退款
*/
public void refunds(OrderContext context) {
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
}
}
退货退款
package com.lxl.www.designPatterns.statePattern.order2;
public class Refunds implements IOrderStatus{
public Refunds() {
System.out.println("当前状态 :: 退货退款");
}
@Override
public void pending(OrderContext context) {
}
@Override
public void cancelPay(OrderContext context) {
}
@Override
public void waitOut(OrderContext context) {
}
@Override
public void refunds(OrderContext context) {
System.out.println("退款完成");
context.setOrderStatus(new Finish());
}
@Override
public void waitReceive(OrderContext context) {
}
@Override
public void confirmReceived(OrderContext context) {
}
@Override
public void orderEvaluation(OrderContext context) {
}
@Override
public void finish(OrderContext context) {
}
}
我们看得出来, 这些状态子类,继承了父类所有的方法,但是却没有实现所有的方法。而是只实现了和自己有关系的一部分方法。这也是我们后面需要优化的地方
第四步: 客户端调用
package com.lxl.www.designPatterns.statePattern.order2;
public class OrderClient {
public static void main(String[] args) {
System.out.println("==========张三 开始下单==========");
OrderContext o1 = new OrderContext();
System.out.println("===========取消付款==============");
o1.cancelPay();
o1.finish();
System.out.println();
System.out.println();
System.out.println("==========李四 开始下单==========");
OrderContext o2 = new OrderContext();
System.out.println("===========付款==============");
o2.pending();
System.out.println("===========退货退款==============");
o2.refunds();
o2.refunds();
o2.finish();
System.out.println();
System.out.println();
System.out.println("==========王五 开始下单==========");
OrderContext o3 = new OrderContext();
System.out.println("===========付款==============");
o3.pending();
System.out.println("===========出库==============");
o3.waitOut();
System.out.println("===========收货==============");
o3.waitReceive();
System.out.println("===========确认收货==============");
o3.confirmReceived();
System.out.println("===========订单评价==============");
o3.orderEvaluation();
System.out.println("===========订单完成==============");
o3.finish();
}
}
来看看运行效果:
张三 开始下单
开始购物
当前状态::待付款
=取消付款====
取消付款
订单完成
李四 开始下单
开始购物
当前状态::待付款
=付款
付款完成了, 待出库
当前状态::待出库
=退货退款
申请退货
当前状态 :: 退货退款
退款完成
订单完成
王五 开始下单
开始购物
当前状态::待付款
=付款
付款完成了, 待出库
当前状态::待出库
=出库
出库完成, 待收货
当前状态 :: 待收货
=收货
进行收货, 完成收货动作
当前状态 :: 确认收货
=确认收货
确认收货了
当前状态 :: 订单待评价
=订单评价
订单评价完了
=订单完成
订单完成
第三种:完善方案二
方案二违背了接口隔离原则。胖接口,导致很多类其实都不需要重写方法,但是不得不继承。实际工作中,我们可以将胖接口拆分,这样状态子类只需要实现自己需要的接口就可以了。
今天这里还有一种特别的解决方案,也可以很巧妙的解决这个问题。
第一步: 定义环境上下文类
观察一下,这次的环境上下文类和方案二的有什么区别?
对每一个状态进行的强转。这样做的优点后面就可以看出来了
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 订单上下文
*/
public class OrderContext {
/**在上下文类中记录订单状态*/
private IOrderStatus orderStatus;
public OrderContext() {
System.out.println("开始购物");
// 最开始是待付款状态
this.orderStatus = new Pending();
}
public void setOrderStatus(IOrderStatus orderStatus) {
this.orderStatus = orderStatus;
}
/**
* 去付款
*/
public void toPay() {
((Pending)this.orderStatus).toPay(this);
}
/**
* 取消付款
*/
public void toCancelPay() {
((Pending)this.orderStatus).toCancelPay(this);
}
/**
* 取消付款
*/
public void cancelPay() {
((CancelPay)this.orderStatus).cancelPay(this);
}
/**
* 去发货
*/
public void toSendProduct() {
((WaitOut)this.orderStatus).toSendProduct(this);
}
/**
* 申请退款
*/
public void toRefunds() {
((Confirm)this.orderStatus).toRefunds(this);
}
/**
* 已退款
*/
public void refunded() {
((Refunds)this.orderStatus).refunded(this);
}
public void toReceiveProduct() {
((WaitReceive)this.orderStatus).toReceiveProduct(this);
}
/**
* 点击确认收货按钮
*/
public void toConfirmReceived() {
((Confirm)this.orderStatus).toConfirmReceived(this);
}
/**
* 去评价订单
*/
public void toOrderEvaluation() {
((OrderEvaluation)this.orderStatus).toOrderEvaluation(this);
}
/**
* 已完成
*/
public void finish() {
((Finish)this.orderStatus).finish(this);
}
}
第二步: 定义抽象状态类
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 订单状态
*/
public interface IOrderStatus {
}
抽象状态类里面并没有方法
第三步: 定义具体状态实现
待付款
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 待付款
*/
public class Pending implements IOrderStatus {
public Pending() {
System.out.println("当前状态::待付款");
}
public void toPay(OrderContext context) {
System.out.println("付款完成了, 待出库");
context.setOrderStatus(new WaitOut());
}
/*
* 取消付款
*/
public void toCancelPay(OrderContext context) {
System.out.println("不想要了, 要取消付款");
context.setOrderStatus(new CancelPay());
}
}
待出库
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 待出库
*/
public class WaitOut implements IOrderStatus {
public WaitOut() {
System.out.println("当前状态::待出库");
}
/*
* 退货退款
*/
public void toRefunds(OrderContext context) {
System.out.println("不想要了, 申请退货退款");
context.setOrderStatus(new Refunds());
}
/**
* 去发货
* @param context
*/
public void toSendProduct(OrderContext context) {
System.out.println("出库完成, 待收货");
context.setOrderStatus(new WaitReceive());
}
}
待收货
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 待收货
*/
public class WaitReceive implements IOrderStatus {
public WaitReceive() {
System.out.println("当前状态 :: 待收货");
}
/*
* 退货退款
*/
public void refunds(OrderContext context) {
System.out.println("申请退货");
context.setOrderStatus(new Refunds());
}
/**
* 去收获
* @param context
*/
public void toReceiveProduct(OrderContext context) {
System.out.println("执行收货逻辑, 完成收货操作");
context.setOrderStatus(new Confirm());
}
}
确认收货
package com.lxl.www.designPatterns.statePattern.order3;
public class Confirm implements IOrderStatus {
public Confirm() {
System.out.println("当前状态 :: 确认收货");
}
public void toRefunds(OrderContext context) {
System.out.println("不想要了, 想退款");
context.setOrderStatus(new Refunds());
}
public void toConfirmReceived(OrderContext context) {
System.out.println("执行确认收货逻辑");
context.setOrderStatus(new OrderEvaluation());
}
}
订单评价
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 订单评价
*/
public class OrderEvaluation implements IOrderStatus {
public OrderEvaluation() {
System.out.println("当前状态 :: 订单待评价");
}
public void toOrderEvaluation(OrderContext context) {
System.out.println("订单评价完了");
context.setOrderStatus(new Finish());
}
}
订单完成
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 订单完成
*/
public class Finish implements IOrderStatus {
public void finish(OrderContext context) {
System.out.println("订单完成");
}
}
取消付款
package com.lxl.www.designPatterns.statePattern.order3;
/**
* 取消付款
*/
public class CancelPay implements IOrderStatus {
/*
* 取消付款
*/
public void cancelPay(OrderContext context) {
System.out.println("执行取消付款逻辑");
context.setOrderStatus(new Finish());
}
}
退货退款
package com.lxl.www.designPatterns.statePattern.order3;
public class Refunds implements IOrderStatus {
public Refunds() {
System.out.println("当前状态 :: 退货退款");
}
public void refunded(OrderContext context) {
System.out.println("完成退款");
context.setOrderStatus(new Finish());
}
}
第四步:客户端调用
package com.lxl.www.designPatterns.statePattern.order3;
public class OrderClient {
public static void main(String[] args) {
System.out.println("==========张三 开始下单==========");
OrderContext o1 = new OrderContext();
System.out.println("===========取消付款==============");
o1.toCancelPay();
o1.cancelPay();
System.out.println("");
System.out.println("");
System.out.println("==========李四 开始下单 收货后退款==========");
OrderContext o2 = new OrderContext();
System.out.println("===========付款==============");
o2.toPay();
System.out.println("===========发货==============");
o2.toSendProduct();
System.out.println("===========收获==============");
o2.toReceiveProduct();
System.out.println("===========退货==============");
o2.toRefunds();
o2.refunded();
System.out.println("===========完成==============");
o2.finish();
System.out.println();
System.out.println();
System.out.println("==========王五 开始下单走完全流程==========");
OrderContext o3 = new OrderContext();
System.out.println("===========付款==============");
o3.toPay();
System.out.println("===========出库==============");
o3.toSendProduct();
System.out.println("===========收货==============");
o3.toReceiveProduct();
System.out.println("===========确认收货==============");
o3.toConfirmReceived();
System.out.println("===========订单评价==============");
o3.toOrderEvaluation();
System.out.println("===========订单完成==============");
o3.finish();
}
}
运行结果:
张三 开始下单
开始购物
当前状态::待付款
=取消付款====
不想要了, 要取消付款
执行取消付款逻辑
李四 开始下单 收货后退款
开始购物
当前状态::待付款
=付款
付款完成了, 待出库
当前状态::待出库
=发货
出库完成, 待收货
当前状态 :: 待收货
=收获
执行收货逻辑, 完成收货操作
当前状态 :: 确认收货
=退货
不想要了, 想退款
当前状态 :: 退货退款
完成退款
=完成====
订单完成
王五 开始下单走完全流程
开始购物
当前状态::待付款
=付款
付款完成了, 待出库
当前状态::待出库
=出库
出库完成, 待收货
当前状态 :: 待收货
=收货
执行收货逻辑, 完成收货操作
当前状态 :: 确认收货
=确认收货
执行确认收货逻辑
当前状态 :: 订单待评价
=订单评价
订单评价完了
=订单完成
订单完成
运行结果和方案二是一样的
四、状态设计模式的优缺点
优点:
- 结构清晰,避免了过多的switch…case或if…else语句的使用
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
- 封装性非常好,状态变化放置到了类的内部来实现,外部调用不需要知道类内部如何实现状态和行为的变换
缺点:
-
会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维
护难度
-
违背开闭原则:增加一个状态,除了要增加状态子类,还需要修改原来的环境类。
五、应用场景
当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式
- 电梯,有运行状态、开门状态、闭门状态、停止状态等
- 一日从早到晚自身的状态,比如工作状态、学习状态、睡觉状态等等
- 运动员可以有正常状态、非正常状态和超长状态
六、注意事项
- 在行为受状态约束的情况下可以使用状态模式,使用时对象的状态最好不要超过5个。因为状态越多,逻辑越负责,后期维护成本越高。
七、状态模式对六大原则的使用分析
- 单一职责原则: 符合单一职责原则,一个状态类只做一件事
- 里式替换原则:父类出现的地方都可以使用子类替换。— 状态模式父类是抽象类或者接口。方案一重写了父类的构造方法
- 依赖倒置原则:依赖于抽象,而不是依赖于具体。—符合
- 接口隔离原则:依赖于最小接口—方案二不符合
- 迪米特法则:最小知识原则-之和朋友交流,减少和朋友的沟通 — 符合
- 开闭原则:对扩展开放,对修改关闭—-增加状态需要修改原来的环境类—不符合