行为型模式——模板方法(一)

该项目源码地址:https://github.com/ggb2312/JavaNotes/tree/master/design-pattern (设计模式相关代码与笔记)

1. 定义

定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤

2. 适用场景

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
  • 各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复

3. 相关设计模式

模板方法模式和工厂方法模式

  • 工厂方法模式是模板方法模式的一种特殊实现

模板方法模式和策略模式

  • 策略模式是使不同的算法可以相互替换,并且不影响客户端的使用。模板方法模式定义算法的执行流程,不同的算法交给子类去实现

4. 模式实例

4.1 常规-模板方法

背景:学炒菜,手撕包菜 & 蒜蓉炒菜心**

(1)创建抽象模板结构(Abstract Class)

炒菜的步骤

public abstract class Abstract {
    //模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序 
    final void cookProcess() {
        //第一步:倒油
        this.pourOil();
         //第二步:热油
        this.HeatOil();
        //第三步:倒蔬菜
        this.pourVegetable();
        //第四步:倒调味料
        this.pourSauce();
        //第五步:翻炒
        this.fry();
    }

//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的

    //第一步:倒油是一样的,所以直接实现
    void pourOil() {
        System.out.println("倒油");
    }

    //第二步:热油是一样的,所以直接实现
    void HeatOil() {
        System.out.println("热油");
    }

    //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现 
    abstract void pourVegetable();

    //第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现 
    abstract void pourSauce();


    //第五步:翻炒是一样的,所以直接实现
    void fry() {
        System.out.println("炒啊炒啊炒到熟啊");
    }
}

(2) 创建具体模板(Concrete Class)

炒”手撕包菜“和”蒜蓉炒菜心“的具体步骤

//炒手撕包菜的类
public class ConcreteClass_BaoCai extends Abstract {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是包菜");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是辣椒");
    }
}
//炒蒜蓉菜心的类
public class ConcreteClass_CaiXin extends Abstract {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是菜心");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是蒜蓉");
    }
}

(3)测试

public class Test {
    public static void main(String[] args) {
        //炒 - 手撕包菜
        ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai();
        BaoCai.cookProcess();
        //炒 - 蒜蓉菜心
        ConcreteClass_CaiXin CaiXin = new ConcreteClass_CaiXin();
        CaiXin.cookProcess();
    }
}

测试结果

4.2 扩展-钩子方法

如果想炒两盘青菜,一盘是不放调味料,一盘放调味料怎么办呢。
按照模板方法的正常步骤就是,写一个炒青菜子类ConcreteClass_Qingcai_NotPourSauce空实现倒调味料方法,写一个炒青菜子类ConcreteClass_Qingcai实现倒调味料方法。然后炒不放调味料的青菜使用ConcreteClass_Qingcai_NotPourSauce,炒放调味料的青菜
使用ConcreteClass_Qingcai。

如果想要炒更多青菜怎么办呢?

可以在抽象的模板里面添加钩子方法,在抽象的执行流程中,根据钩子函数决定是否调用某个步骤。

public abstract class Abstract {
    //模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序 
    final void cookProcess() {
        //第一步:倒油
        this.pourOil();
        //d第二步:热油
        this.HeatOil();
        //第三步:倒蔬菜
        this.pourVegetable();
        //第四步:倒调味料
        
        // 这里是否需要倒调味料交由钩子方法来决定
        if (needPourSauce()) {
            this.pourSauce();
        }
        //第五步:翻炒
        this.fry();
    }


//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的

    //第一步:倒油是一样的,所以直接实现
    void pourOil() {
        System.out.println("倒油");
    }

    //第二步:热油是一样的,所以直接实现
    void HeatOil() {
        System.out.println("热油");
    }

    //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现 
    abstract void pourVegetable();

    //第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现 
    abstract void pourSauce();


    //第五步:翻炒是一样的,所以直接实现
    void fry() {
        System.out.println("炒啊炒啊炒到熟啊");
    }

    /**
     * 我们就要给这个倒调味料声明一个钩子方法
     */
    protected boolean needPourSauce() {
        return true;
    }
}

这样我们只需要一个炒青菜子类。子类可以选择性的重写钩子方法,决定是否倒入调料。

5. 优缺点

优点:

  • 提高复用性
  • 提高扩展性
  • 符合开闭原则

缺点:

  • 类数目增加
  • 增加了系统实现的复杂度
  • 如果父类添加新的抽象方法,所有子类都要改一遍

6. 扩展-JDK1.7源码以及框架中的模板方法

6.1 jdk

java.util.AbstractList定义一套算法模板,如get(),set()等等,由子类实现。

6.2 servlet

javax.servlet.http.HttpServlet定义一套算法模板,如doGet()、doPost()等等,由子类实现。。

6.3 mybatis

org.apache.ibatis.executor.BaseExecutor

猜你喜欢

转载自www.cnblogs.com/gj-blog/p/10929517.html