在实际开发中,经常会遇到一项基本功能须要支撑不一样业务的状况。好比订单发货,有普通的整包发货,有分销单的发货,采购单的发货,有多商品的整包或拆包发货等。要想支持这些业务的发货,显然不能在一个通用流程里用一堆的 if-else 来应对。java
遵循“开闭”原则,咱们应当尽可能提供一个可扩展的设计,容许新的业务来覆写部分方法来实现定制的发货。“开闭原则”意味着,咱们老是在原有基础上新增方法,而不是改动原有方法。这能够作到最小化影响。express
使用模板方法设计模式,正是一种应对和加强系统可扩展性的方法。 定义好通用流程, 并设置一系列钩子方法, 而具体业务只要覆写部分钩子方法便可实现本身的需求。下面给出一个简化版的发货可扩展性实现。设计模式
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public interface Express { /** * 通用发货接口 * @param expressParam 发货参数 * @return 发货包裹ID */ int postExpress(ExpressParam expressParam); }
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public class ExpressParam { private String orderNo; // 订单编号 private String exId; // 发货公司ID private String exNo; // 发货单号 public ExpressParam(String orderNo, String exId, String exNo) { this.orderNo = orderNo; this.exId = exId; this.exNo = exNo; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } public String getExId() { return exId; } public void setExId(String exId) { this.exId = exId; } public String getExNo() { return exNo; } public void setExNo(String exNo) { this.exNo = exNo; } @Override public String toString() { return "ExpressParam{" + "orderNo='" + orderNo + '\'' + ", exId='" + exId + '\'' + ", exNo='" + exNo + '\'' + '}'; } }
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public class Order { private String orderNo; private Integer orderType; public Order(String orderNo, Integer orderType) { this.orderNo = orderNo; this.orderType = orderType; } public String getOrderNo() { return orderNo; } public void setOrderNo(String orderNo) { this.orderNo = orderNo; } public Integer getOrderType() { return orderType; } public void setOrderType(Integer orderType) { this.orderType = orderType; } }
默认发货实现是针对普通商品。采用抽象类来实现。 普通发货要检测订单商品是不是分销的,这里简便起见用订单号代替。ide
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. * Provide a default implementation of Express */ public abstract class AbstractExpress implements Express { public int postExpress(ExpressParam expressParam) { checkExpressParam(expressParam); Order order = getOrder(expressParam.getOrderNo()); checkOrder(order); return execute(order, expressParam); } protected void checkExpressParam(ExpressParam expressParam) { // basic express param check, probably not be overriden } protected void checkOrder(Order order) { // check if order can express. may be overriden if (Integer.valueOf(5).equals(order.getOrderType()) || order.getOrderNo().startsWith("F")) { throw new IllegalArgumentException("Fenxiao order can not be expressed by own"); } } protected Order getOrder(String orderNo) { // here is just for creating order , probably not overriden return new Order(orderNo, 0); } /** * 发货的默认实现 * @param order 订单信息 * @param expressParam 发货参数 * @return 发货包裹ID * * Note: Suggest this method be overriden ! */ protected int execute(Order order, ExpressParam expressParam) { System.out.println("success express for normal order: " + expressParam); return 1; } }
普通发货实现直接继承抽象类,不覆写任何方法。post
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public class NormalExpress extends AbstractExpress { }
分销发货要放过度销商品的检测。所以要覆写 checkOrder 方法。此外,也会覆写 execute 方法。测试
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public class FenxiaoExpress extends AbstractExpress { public Order getOrder(String orderNo) { return new Order(orderNo, 5); } protected void checkOrder(Order order) { // let order check pass } protected int execute(Order order, ExpressParam expressParam) { System.out.println("success express for fenxiao order: " + expressParam); return 1; } }
采购单的发货须要推送消息,同步分销单的发货。this
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public class CaigouExpress extends AbstractExpress { protected int execute(Order order, ExpressParam expressParam) { pushMessage(order, expressParam); System.out.println("success express for caigou order: " + expressParam); return 1; } private void pushMessage(Order order, ExpressParam expressParam) { System.out.println("push message to trigger fenxiao order to express"); } }
package zzz.study.patterns.templateMethod.express; /** * Created by shuqin on 17/4/6. */ public class Client { public static void main(String[] args) { ExpressParam expressParam = new ExpressParam("201704062033113366", "1", "666888"); Express normal = new NormalExpress(); normal.postExpress(expressParam); try { ExpressParam expressParamInvalid = new ExpressParam("F201704062033123456", "1", "666888"); normal.postExpress(expressParamInvalid); } catch (Exception ex) { String exInfo = String.format("Failed to post express for %s , Reason: %s", expressParam, ex.getMessage()); System.err.println(exInfo); } Express fenxiao = new FenxiaoExpress(); ExpressParam fenxiaoExpressParam = new ExpressParam("F201704062033123456", "1", "666888"); fenxiao.postExpress(fenxiaoExpressParam); Express caigou = new CaigouExpress(); ExpressParam caigouExpressParam = new ExpressParam("201704062033113366", "1", "666888"); caigou.postExpress(caigouExpressParam); } }
经过模板方法模式,比较优雅地将通用流程及逻辑与定制的部分分离, 新的业务只要覆写相应方法,就能够完成本身的需求,而无需改动核心流程代码。模板方法模式的不足在于:在实际业务中可能对 AbstractExpress 拆分出新的更细的可覆写的业务方法,这会致使各个业务的总体发货逻辑理解起来不够直观。同时,当在 AbstractExpress 中拆分中新的方法时, 须要回归测试来保障原有发货不受影响。设计
实际上,这种实如今 JDK 容器类的实现发挥的淋漓尽致。 接口定义行为,抽象类定义默认实现, 而具体类经过覆写某些方法实现定制化功能。code