模板方法模式详解

模板方法模式(Template Method Pattern)

定义

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。——《HeadFirst设计模式》

通俗地说,就是在超类中定义一系列方法,其中一个方法(骨架方法)顺序调用其它方法,子类则按需要重写部分方法(所谓的延迟到子类)

模式总结过程

公司要做一个电商系统中的货物入库模块,有四个步骤

  1. 接收货物信息
  2. 拼接请求报文
  3. 调用入库接口
  4. 记录日志

假设现在有三类货物,电器、衣服、家具

  1. 初步的代码如下
// 电器
public class Electric {

    // 算法骨架
    public void main() {
        step1();
        step2();
        step3();
        step4();
    }

    public void step1() {
        System.out.println("接收电器信息");
    }

    public void step2() {
        System.out.println("拼接电器请求报文");
    }

    public void step3() {
        System.out.println("调用入库接口");
    }

    public void step4() {
        System.out.println("记录日志");
    }
}

// 衣服
public class Clothes {

    // 算法骨架
    public void main() {
        step1();
        step2();
        step3();
        step4();
    }

    public void step1() {
        System.out.println("接收衣服信息");
    }

    public void step2() {
        System.out.println("拼接衣服请求报文");
    }

    public void step3() {
        System.out.println("调用入库接口");
    }

    public void step4() {
        System.out.println("记录日志");
    }
}

// 家具
public class Furniture {

    // 算法骨架
    public void main() {
        step1();
        step2();
        step3();
        step4();
    }

    public void step1() {
        System.out.println("接收家具信息");
    }

    public void step2() {
        System.out.println("拼接家具请求报文");
    }

    public void step3() {
        System.out.println("调用入库接口");
    }

    public void step4() {
        System.out.println("记录日志");
    }
}
  1. 其中1、2是每类货物都不一样的,而3、4是一样的
  2. 货物属性不一样,货物信息肯定不一样,那请求的接口报文也就不一样、
  1. 可以发现,代码中有很多相似和重复的部分,把它抽取出来成为抽象基类
public abstract class Goods {

    // 算法骨架
    public final void main() {
        step1();
        step2();
        step3();
        step4();
    }

    public abstract void step1();

    public abstract void step2();

    public void step3() {
        System.out.println("调用入库接口");
    }

    public void step4() {
        System.out.println("记录日志");
    }
}

// 电器
public class Electric extends Goods {

    @Override
    public void step1() {
        System.out.println("接收电器信息");
    }

    @Override
    public void step2() {
        System.out.println("拼接电器请求报文");
    }
}

// 衣服
public class Clothes extends Goods {


    @Override
    public void step1() {
        System.out.println("接收衣服信息");
    }

    @Override
    public void step2() {
        System.out.println("拼接衣服请求报文");
    }
}

// 家具
public class Furniture extends Goods {

    @Override
    public void step1() {
        System.out.println("接收家具信息");
    }

    @Override
    public void step2() {
        System.out.println("拼接家具请求报文");
    }
}
  1. 基类中,main方法加了final,这是因为我们要统一控制步骤,不能让子类重写
  2. 这就是模板方法模式了,下面是一些扩展
  1. 如果货物入库后,希望针对指定货物对客户进行短信提醒功能
public abstract class Goods {

    // 算法骨架
    public final void main() {
        step1();
        step2();
        step3();
        step4();
        if (remind()) {
            step5();
        }
    }

    public abstract void step1();

    public abstract void step2();

    public void step3() {
        System.out.println("调用入库接口");
    }

    public void step4() {
        System.out.println("记录日志");
    }

    public void step5() {
        System.out.println("提醒客户");
    }

    // 默认为false,即不提醒客户,子类根据需要重写
    public boolean remind() {
        return false;
    }
}

// 家具
public class Furniture extends Goods {

    @Override
    public void step1() {
        System.out.println("接收家具信息");
    }

    @Override
    public void step2() {
        System.out.println("拼接家具请求报文");
    }

    // 需要提醒客户
    @Override
    public boolean remind() {
        return true;
    }
}
  1. 在基类中,加入一个判断条件,默认false,再加上一个提醒客户的方法
  2. 子类根据自己的需要,需要提醒客户的,则重写判断条件方法即可
与策略模式的区别
  1. 关注点不一样:模板方法关注的是算法大纲,而策略模式关注的是算法族
  2. 实现原理不一样:模板方法通过继承实现算法的微调,而策略模式通过组合实现不同算法的切换
  3. 算法控制权不一样:模板方法通过final禁止了子类修改算法大纲,而策略模式不同算法的实现没有要求
  4. 依赖程度不一样:模板方法使用的继承,策略模式使用组合,所以模板方法的的依赖程度更高,也就是不够解耦
优点
  1. 代码复用
  2. 控制大纲(final)
缺点
  1. 依赖程度较高,继承
  2. 类个数增多,一个不同的实现需要多一个类
应用

Arrays.sort()

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // getter setter and toString
}

public class Client {
    public static void main(String[] args) {
        Student[] arr = new Student[]{
                new Student("Tom", 18),
                new Student("Jerry", 17),
                new Student("Amy", 19)
        };
        Arrays.sort(arr);
        for (Student s : arr) {
            System.out.println(s);
        }
    }
}

运行报错

Exception in thread "main" java.lang.ClassCastException: com.balsam.pattern.templateMethod.Student cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:316)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:184)
	at java.util.Arrays.sort(Arrays.java:1246)
	at com.balsam.pattern.templateMethod.Client.main(Client.java:17)

得实现Comparable接口,重写compareTo()方法,也就是你得提供比较的依据

public class Student implements Comparable {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public int compareTo(Object o) {
        Student s = (Student) o;
        if (this.getAge() > s.getAge()) {
            return 1;
        } else if (this.getAge() < s.getAge()) {
            return -1;
        }
        return 0;
    }
}

客户端代码不变,运行结果

Student{name='Jerry', age=17}
Student{name='Tom', age=18}
Student{name='Amy', age=19}

看上去,这并不是一个合格的模板方法模式的应用,模板方法是通过继承去实现某些步骤的具体化,而这里显然不像,但是,它把排序的算法都实现好了,你只需要根据自己的需要提供比较的依据,就可以实现排序的功能,这正是模板方法模式的精神所在。

学习设计模式,不应该完全生搬硬套,得灵活运用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值