概述###

在实际开发中,常常会遇到一项基本功能需要支撑不同业务的情况。比如订单发货,有普通的整包发货,有分销单的发货,采购单的发货,有多商品的整包或拆包发货等。要想支持这些业务的发货,显然不能在一个通用流程里用一堆的 if-else 来应对。

遵循“开闭”原则,我们应当尽量提供一个可扩展的设计,允许新的业务来覆写部分方法来实现定制的发货。“开闭原则”意味着,我们总是在原有基础上新增方法,而不是改动原有方法。这可以做到最小化影响。

使用模板方法设计模式,正是一种应对和增强系统可扩展性的方法。 定义好通用流程, 并设置一系列钩子方法, 而具体业务只要覆写部分钩子方法即可实现自己的需求。下面给出一个简化版的发货可扩展性实现。

代码示例###

定义发货接口####

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;
}
}

定义默认发货实现####

默认发货实现是针对普通商品。采用抽象类来实现。 普通发货要检测订单商品是否是分销的,这里简便起见用订单号代替。

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;
} }

普通发货实现####

普通发货实现直接继承抽象类,不覆写任何方法。

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;
} }

采购单的发货####

采购单的发货需要推送消息,同步分销单的发货。

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 容器类的实现发挥的淋漓尽致。 接口定义行为,抽象类定义默认实现, 而具体类通过覆写某些方法实现定制化功能。

设计模式之模板方法模式:实现可扩展性设计(Java示例)的更多相关文章

  1. 折腾Java设计模式之模板方法模式

    博客原文地址:折腾Java设计模式之模板方法模式 模板方法模式 Define the skeleton of an algorithm in an operation, deferring some ...

  2. 乐在其中设计模式(C#) - 模板方法模式(Template Method Pattern)

    原文:乐在其中设计模式(C#) - 模板方法模式(Template Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 模板方法模式(Template Method ...

  3. js设计模式——6.模板方法模式与职责链模式

    js设计模式——6.模板方法模式与职责链模式 职责链模式

  4. 软件设计模式之模板方法模式(JAVA)

    什么是模板方法模式? 定义一个操作中算法的骨架,而将这些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤. 好抽象的概念啊,文绉绉的东西就是不讨人喜欢,下面我 ...

  5. Java设计模式应用——模板方法模式

    所谓模板方法模式,就是在一组方法结构一致,只有部分逻辑不一样时,使用抽象类制作一个逻辑模板,具体是实现类仅仅实现特殊逻辑就行了.类似科举制度八股文,文章结构相同,仅仅具体语句有差异,我们只需要按照八股 ...

  6. Java设计模式之模板方法模式(Template Method)

    一.含义 定义一个算法中的操作框架,而将一些步骤延迟到子类中.使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤,不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现. 二 ...

  7. Java设计模式之模板方法模式(Template)

    前言: 我们在开发中有很多固定的流程,这些流程有很多步凑是固定的,比如JDBC中获取连接,关闭连接这些流程是固定不变的,变动的只有设置参数,解析结果集这些是根据不同的实体对象“来做调整”,针对这种拥有 ...

  8. java设计模式之模板方法模式

    模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.通俗的说的就是有很多相同的步骤的,在某一些地方可能有一些差 ...

  9. Java中类的继承,属性和方法的四种修饰符的作用范围,final关键字,java的三大特点中的2个:封装和多态,以及多态的一个设计模式,模板方法模式(template method)

    (一)Java中的继承: 关于继承,在Java中类的继承只能是单继承,不像C+++那样灵活,可以多继承,多继承的后果就是各种关系乱套,就相当于一个孩子有2个母亲一样,社会关系的复杂,不利于程序后期的开 ...

随机推荐

  1. 【Python全栈-后端开发】数据库进阶

    数据库进阶 python关于mysql的API---pymysql模块 pymsql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同. 模块安装 pip install ...

  2. cookie 和 token 的理解

    HTTP协议本身是无状态的,所以需要一个标志来对用户身份进行验证 1.cookie 用户登录成功后,会在服务器存一个session,同时发送给客户端一个 cookie 数据需要客户端和服务器同时存储 ...

  3. 使用监听器解决路径问题,例如在jsp页面引入js,css的web应用路径

    使用监听器解决路径问题,例如在jsp页面引入js,css的web应用路径 经常地,我们要在jsp等页面引入像js,css这样的文件,但是在服务器来访问的时候,这时间就有关到相对路径与绝对路径了.像网页 ...

  4. system.out.printf()的使用方法

    package com.lzc.test; public class Main { public static void main(String[] args) { // 定义一些变量,用来格式化输出 ...

  5. Maven中groupId和artifactId的含义

    groupId和artifactId被统称为“坐标”是为了保证项目唯一性而提出的,如果你要把你项目弄到maven仓库去,你想要找到你的项目就必须根据这两个id去查找.groupId是项目组织唯一的标识 ...

  6. 线上MYSQL同步报错故障处理方法总结

    前言 在发生故障切换后,经常遇到的问题就是同步报错,下面是最近收集的报错信息. 记录删除失败 在master上删除一条记录,而slave上找不到 Last_SQL_Error: Could not e ...

  7. 给Access数据库文件减肥

    原理:数据文件和普通文件在硬盘上的存放方式不一样,你清空了表里的数据,但数据库里数据没了,但该数据的位置还在.就好比一个班里的学生都离开了教室,教室没有人了,但学生的座位还在一样(哈哈,这个比喻不是很 ...

  8. PHP 操作 Redis 的手册

    转:https://www.cnblogs.com/jackluo/p/5708024.html String 类型操作 string是redis最基本的类型,而且string类型是二进制安全的.意思 ...

  9. 创建genil component

    1: 创建一个类继承 CL_WCF_GENIL_ABSTR_COMPONENT 2:创建 genil _ editor 创建 component, 填入该实现类. 3: genil component ...

  10. Spark MLlib之水塘抽样算法(Reservoir Sampling)

    1.理解 问题定义可以简化如下:在不知道文件总行数的情况下,如何从文件中随机的抽取一行? 首先想到的是我们做过类似的题目吗?当然,在知道文件行数的情况下,我们可以很容易的用C运行库的rand函数随机的 ...