第二种: 带有取消付款和退货退款状态。
退货退款最开始只能是收到货以后才能退。
这种方式和第一个有所不同, 待付款和确认收货不止有一个状态流转,当有多个状态流转,甚至是更多的状态是,我们应该如何处理呢?
第一步: 定义上下文环境类。
这里环境上下文定义了所有的状态方法。
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个。因为状态越多,逻辑越负责,后期维护成本越高。
七、状态模式对六大原则的使用分析
- 单一职责原则: 符合单一职责原则,一个状态类只做一件事
- 里式替换原则:父类出现的地方都可以使用子类替换。--- 状态模式父类是抽象类或者接口。方案一重写了父类的构造方法
- 依赖倒置原则:依赖于抽象,而不是依赖于具体。---符合
- 接口隔离原则:依赖于最小接口---方案二不符合
- 迪米特法则:最小知识原则-之和朋友交流,减少和朋友的沟通 --- 符合
- 开闭原则:对扩展开放,对修改关闭----增加状态需要修改原来的环境类---不符合