模板方法模式

JAVA学习网 2018-01-31 06:51:02

1.模板方法模式是什么

1.百度百科

定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

2.维基百科

In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses.[1] It lets one redefine certain steps of an algorithm without changing the algorithm's structure.[2]

3.lz理解

将算法中不变的部分放到抽象方法中,变化的部分延迟到子类实现。这样可以稳定算法中的整体流程,而变动的地方可以任意修改。

4.核心角色

抽象模板(Abstract Template):抽象模板是一个抽象类。抽象模板定义了若干个方法以表示算法的各个步骤,这些方法中有抽象方法也有非抽象方法,其中的抽象方法称为原语操作(Primitive Operation)。重要的一点是,抽象模板中还定义了一个称之为模板方法的方法,该方法不仅包含有抽象模板中表示算法步骤的方法调用,而且也可以包含有定义在抽象模板中的其他对象的方法调用,即模板方法定义了算法的骨架。

具体模板(Concrete Template):具体模板是抽象模板的子类,实现抽象模板中的原语操作。

2.模板方法模式解决了什么问题

封装不变部分,扩展可变部分 可以通过在抽象模板定义模板方法给出成熟的算法步骤,同时又不限制步骤的细节,具体模板实现细节不会改变整个算法的框架。

行为由父类控制,子类实现 在抽象模板模式中,可以通过钩子方法对某些步骤进行挂钩,具体模板通过钩子可以选择算法骨架中的某些步骤。

3.模板方法模式用法

1.制作饮料

我们就拿泡茶、泡咖啡举例子。我们需要先烧水然后放入咖啡或者茶叶然后泡好之后加入牛奶或者柠檬片。

抽象模板

public abstract class MakeDrink {
    //烧水
    protected void boilWater() {
        System.out.println("开始烧水");
    }
    //放入饮品
    protected abstract void put();
    //泡
    protected void brewing() {
        System.out.println("冲泡10min");
    }
    //添加其他
    protected abstract void addOthrers();
  //制作饮料
    public final void makeSomeDrink() {
        System.out.println("开始制作饮料");
        boilWater();
        put();
        brewing();
        addOthrers();
        System.out.println("饮料制作完成");
    }

}

具体模板

public class MakeTea extends MakeDrink {

    @Override
    protected void put() {
        System.out.println("放入茶叶");

    }

    @Override
    protected void addOthrers() {
        System.out.println("放入柠檬片");
    }

}

public class MakeCoffee extends MakeDrink {

    @Override
    protected void put() {
        System.out.println("放入咖啡粉");

    }

    @Override
    protected void addOthrers() {
        System.out.println("添加牛奶");
    }

}

客户端调用

public class Customer {

    public static void main(String[] args) {
        MakeDrink MakeCoffee = new MakeCoffee();
        MakeCoffee.makeSomeDrink();
        MakeDrink MakeTea = new MakeTea();
        MakeTea.makeSomeDrink();
    }

}

结果

开始制作饮料

开始烧水

放入咖啡粉

冲泡10min

添加牛奶

饮料制作完成


开始制作饮料

开始烧水

放入茶叶

冲泡10min

放入柠檬片

饮料制作完成

这时有个问题出现了,如果有人不想要咖啡里面添加牛奶。或者不想在茶叶里面添加柠檬。那该怎么办呢。

进行如下改造。
在抽象方法中添加一个判断条件,然后子类重写他已到达控制是否添加其他原料的目的。

抽象角色模板

public abstract class MakeDrink {
    //控制是否添加其他的方法
    protected Boolean hasPutOthers() {
        return true;
    }

    protected void boilWater() {
        System.out.println("开始烧水");
    }

    protected abstract void put();

    protected void brewing() {
        System.out.println("冲泡10min");
    }

    protected abstract void addOthrers();

    public final void makeSomeDrink() {
        System.out.println("开始制作饮料");
        boilWater();
        put();
        brewing();
        if(hasPutOthers()) {
            addOthrers();
        }
        System.out.println("饮料制作完成");
    }
}

具体方法

public class MakeCoffee extends MakeDrink {

    private Boolean hasOther = true;

    @Override
    protected Boolean hasPutOthers() {
        return hasOther;
    }

    public void setHasOther(Boolean hasOther) {
        this.hasOther = hasOther;
    }

    @Override
    protected void put() {
        System.out.println("放入咖啡粉");

    }

    @Override
    protected void addOthrers() {
        System.out.println("添加牛奶");
    }

}

客户端调用

public class Customer {

    public static void main(String[] args) {
        MakeCoffee makeCoffee = new MakeCoffee();
        //1号顾客需要加牛奶
        makeCoffee.setHasOther(true);
        makeCoffee.makeSomeDrink();

        //2号顾客不需要加牛奶
        makeCoffee.setHasOther(false);
        makeCoffee.makeSomeDrink();

    }

}

第一个顾客

开始制作饮料

开始烧水

放入咖啡粉

冲泡10min

添加牛奶

饮料制作完成

第二个顾客

开始制作饮料

开始烧水

放入咖啡粉

冲泡10min

饮料制作完成

写了这么多虚的我们还是需要点实际的东西来印证下

2.互联网金融三方支付网关设计

在互联网金融系统中ip黑名单检查、参数签名验证、参数完整性验证、身份证加密解密等工作都是所有请求通用的,只有到具体的业务操作才会执行不同的业务逻辑.
这时我们建立一个基础服务类将共有的服务抽取出来。

抽象模板角色

public abstract class BaseService {

    public final ResResult<String> service(ResVO vo) {
        // 解密参数
        ResResult<ResVO> res = decrypt(vo);
        if (!res.isSuccess()) {
            return new ResResult<String>(res.getCode(),res.getMsg(),"");
        }
        //解密后的参数
        vo = res.getData();
        //ip黑名单验证
        res = blackListValidate(vo.getIp());
        if(!res.isSuccess()) {
            return new ResResult<String>(res.getCode(),res.getMsg(),"");
        }
        //验证签名
        res = verifySign(vo);
        if(!res.isSuccess()) {
            return new ResResult<String>(res.getCode(),res.getMsg(),"");
        }
        //加密敏感内容
        res = encrypt(vo);
        if(!res.isSuccess()) {
            return new ResResult<String>(res.getCode(),res.getMsg(),"");
        }
        // 执行业务逻辑
        res = doBiz(vo);
        return new ResResult<String>(res.getCode(),res.getMsg(),"");
    }

    /**
     * 业务实现
     */
    protected abstract ResResult<ResVO> doBiz(ResVO vo);

    /**
     * 解密参数
     */
    protected ResResult<ResVO> decrypt(ResVO vo) {
        // ...
        ResResult<ResVO> resResult = new ResResult<ResVO>("0000", "解密完成!", vo);
        return resResult;
    }

    /**
     * 参数验证
     */
    protected ResResult<ResVO> validate(ResVO vo) {
        // ...
        return new ResResult<ResVO>("0000", "验证完成!", null);
    }

    /**
     * 黑名单验证
     */
    protected ResResult<ResVO> blackListValidate(String ip) {
        // ...
        return new ResResult<ResVO>("0000", "不是黑名单", null);
    }

    /**
     * 内容加密
     */
    protected ResResult<ResVO> encrypt(ResVO vo) {
        // ...
        ResResult<ResVO> resResult = new ResResult<ResVO>("0000", "加密完成!", vo);
        return resResult;
    }

    /**
     * 验签
     */
    protected ResResult<ResVO> verifySign(ResVO vo) {
        // ...
        return new ResResult<ResVO>("0000", "验签通过", null);
    }

}

具体模板

public class QuickPayService extends BaseService {

    @Override
    protected ResResult<ResVO> doBiz(ResVO vo) {
        System.out.println("执行快捷支付业务");
        return new ResResult<ResVO>("0000","",vo);
    }

}

实体类

/**
 * 响应结果
 */
public class ResResult<T> {

    private String code;
    private String msg;
    private T data;

    public ResResult (String code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public ResResult (String code,String msg,T data){
        this.code = code;
        this.msg = msg;
        this.data = data;
    }



    public Boolean isSuccess() {
        return "0000".equals(code);
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

}


public class ResVO {

    private String  ip;

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }


}

调用客户端

public class Customer {

    public static void main(String[] args) {
        //创建服务类
        QuickPayService quickPayService = new QuickPayService();
        //传来的参数
        ResVO vo = new ResVO();
        quickPayService.service(vo);
    }
}

4.模板方法模式的问题

模板方法变更影响巨大 模板方法模式最基本的功能是通过模板的制定,把模板方法完全固定下来。事实上模板和子类是非常耦合的,如果要对模板中的模板方法进行变更,可能要求所有相关的子类进行相应的变化。所以抽取模板时应尽量确保是不会变化的部分才放到模板中。

调用反常识 按照设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度。

5.模板方法模式总结

应用场景:
1.计者需要给出一个算法的固定步骤,并将某些步骤的具体实现留给子类来实现。
2.需要对代码进行重构,将各个子类公共行为提取出来集中到一个共同的父类中以避免代码重复。
3.需要控制子类扩展的情况。模板方法模式会在特定的点来调用子类的方法,这样只允许在这些点进行扩展。

如果23种设计模式你只想学习一个,那么就学习模板方法模式吧。这种将算法中变化的部分抽象出来。与不变的部分组成了模板方法。当调用的时候只需自己实现自己变化的部分从而达到共用代码稳定算法结构的目的

阅读(734) 评论(0)