模板类

  • 举一个冲泡饮料的例子。泡茶喝泡咖啡除了某些步骤不一样外,大体是一样的,我们可以把冲泡饮料抽象成一个模板,进行共用。而到具体内容的时候,它们再有具体的实现。
  • 模板方法:1.烧开水;2.把饮料放到水杯里;3.倒入开水;4.加调味剂。第1、3步都是一样的,只有2、4需要根据需要调整,因此写为抽象方法由子类具体实现。
public abstract class Beverage {

    // 具体的模板方法, 要用final关键字进行修饰,避免子类进行修改
    public final void prepareBeverageTemplate() {
        // 1, 烧开水
        boilWater();

        // 2, 放到杯中
        putIntoCup();

        // 加入开水
        addHotWater();

        // 加入调味应有剂
        addCondiments();
    }

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

    protected abstract void putIntoCup();

    private void addHotWater() {
        System.out.println("加水");
    }
    protected abstract void addCondiments();

}
  • 声咖啡子类和茶子类实现这两个方法

    public class Coffee extends Beverage {
    
        @Override
        protected void putIntoCup() {
            System.out.println("把咖啡放到水杯中");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("加入糖和牛奶");
        }
    }
    
    public class Tea extends Beverage {
    
        @Override
        protected void putIntoCup() {
            System.out.println("把咖啡放到水杯中");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("加入糖和牛奶");
        }
    }
    
  • 建立一个test类来测试一下模板模式

    public class Test {
    
        public static void main(String[] args) {
    
            // 咖啡制作
            Beverage coffee = new Coffee();
            coffee.prepareBeverageTemplate();  // 调用模版
            System.out.println("\n***********************\n");
            // 茶制作
            Beverage tea = new Tea();
            tea.prepareBeverageTemplate(); // 调用模版
        } 
    }
    

    钩子函数

  • 其实第4个步骤不是必须的,因为有些人喝茶或咖啡不喜欢加调料,那么如何让程序更加通用化或者更加体现个性化需求呢?这就需要提供一个钩子函数,具体到每个场景,使用不同的钩子函数。
  • 我们把原来的额addCondiments方法放入一个if函数中调用,而if条件就是我们的钩子函数的调用。
  • prepareBeverageTemplate方法中的代码修改如下:

    if(isCustomered){
        //加入调味剂
        addCondiments();
    }
    
    //新增isCustomered方法
    protected abstract boolean isCustomered(){
        return true;
    }
    
  • 这时只要在子类中重写这个钩子函数,就实现个性化需求,比如在茶中重写这个方法

    public class Tea extends Beverage(){
        @Override
        protected void putIntoCup(){
            System.out.println("把茶放入水杯中");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("加入糖和牛奶");
        }
    
        @Override
        protected boolean isCustomered(){
            return false;
        }
    }
    
  • 运行Test,就可以去掉加调料这个步骤
  • 再举一个例子,下面也是重写了钩子函数的情况

    //模板设计模式
    subFiles =src.listFiles(new FilenameFilter(){
    
        @Override
        /**
        * 重写了文件过滤器,返回为真时才将文件挑选出来,否则将剔除出listFiles
        * dir 代表src
        */
        public boolean accept(File dir, String name) { 
            //System.out.println(dir.getAbsolutePath());
            return  new File(dir,name).isFile()&&name.endsWith(".java"); //符合条件
        }
    });