设计模式——模版方法

一、首先记住什么模版方法模式(看后面的代码理解):

模版方法模式指在一个方法,里面包含了一个多步骤的算法骨架,其中一个或者多个步骤允许子类延后实现。允许子类在不修改算法的结构的
前提下,对一个或多个步骤进行自己的实现。

 

二、下面用代码来创建的咖啡和茶来说明:

第一种情况:

分别创建Cofee.java和Tea,里面分别都有四个步骤:

1.煮水

2.用沸水泡茶 或者 用沸水泡咖啡

3.把茶或者咖啡装进杯子

4.往杯子加入柠檬或者牛奶

package design.template;

public class Cofee {
    
    void prepare(){
        boilWater();
        chongkaifei();
        pourInCup();
        addSugarAndMilk();
    }
    //煮沸水
    private void boilWater() {
        System.out.println("煮沸水");
        
    }

    //用沸水冲咖啡
    private void chongkaifei(){
        System.out.println("用沸水冲咖啡");
    }
    
    //倒入杯子
    private void pourInCup() {
        System.out.println("倒入杯子");
        
    }

    
    //加糖和牛奶
    private void addSugarAndMilk(){
        System.out.println("加糖和牛奶");
    }

}

package design.template;

public class Tea {
    
    void prepare(){
        boilWater();
        chongcha();
        pourInCup();
        addLemon();
    }
    
    //煮水
    private void boilWater(){
        System.out.println("煮水");
    }
    
    //用沸水冲茶
    private void chongcha(){
        System.out.println("用沸水冲茶");
    }
    
    //把茶装进被子
    private void pourInCup(){
        System.out.println("装进杯子");
    }
    //加柠檬
    private void addLemon(){
        System.out.println("加柠檬");
    }
}

package design.template;

import design.template3.Cofee;

//测试类
public class TestTemplate {
    public static void main(String[] args) {
        new Cofee().prepare();
    }
}
View Code

仔细看,发现泡咖啡和泡茶两个行为其实有很多相似之处。例如第一和第三步其实都是要把水煮沸,然后把液体倒入被子。

 

第二种情况:

定义一个超类CaffeineBeverage,因为第一步和第三步方法被Tea和Cofee类共享,所以直接在父类实现。

而prepare()方法在每个类中都不一样,所以定义成抽象方法。

package design.template2;
/**
 * prepare()方法在每个类中都不一样,所以定义成抽象方法。
 * boilWater(),pourInCup()被两个子类所共享,所以被定义在这个超类中
 * @author lindq3
 *
 * 2017-2-15
 */
public abstract class CaffeineBeverage {
    //因为prepare在Tea和Cofee类里面都不同,所以定义为抽象方法。在子类里面重写
    abstract void prepare();
    
     void boilWater(){
         System.out.println("正在煮水");
     }
     void pourInCup(){
         System.out.println("倒入杯子");
     }
}


package design.template2;

public class Cofee extends CaffeineBeverage {
    
    void prepare(){
        boilWater();
        chongkaifei();
        pourInCup();
        addSugarAndMilk();
    }
    
    //用沸水冲咖啡
    private void chongkaifei(){
        System.out.println("用沸水冲咖啡");
    }
    
    //加糖和牛奶
    private void addSugarAndMilk(){
        System.out.println("加糖和牛奶");
    }

}
View Code

 

第三种情况(其中这个就是模版方法模式的实现):

进一步观察,Tea类和Cofee类除了共享第一和第三个方法外,还有一个相似的地方:就是整个泡茶,泡咖啡的“步骤相似”。于是可以考虑把prepare()方法也在父类实现。

prepare()就是一个包含多个步骤的方法,允许里面的步骤部分由父类实现,部分由子类实现。
如果需要控制
boilWater()或者pourInCup()不被子类重写,也可以定义为final
public abstract class CaffeineBeverage {
       //因为2个子类也具备相似的步骤,把prepare法官也在父类定义。
      //定义为final防止被子类重写。
    public final  void prepare(){
        boilWater();
        mixSth();//冲东西
        pourInCup();
        addSth();//加东西
    }
        //具体冲什么东西由子类决定
    abstract void mixSth();
        //具体加东西由子类决定
    abstract void addSth();
    
        //以下2个方法为子类共有
    private void boilWater() {
        System.out.println("煮沸水");
        
    }
    
    private void pourInCup() {
        System.out.println("倒入杯子");
    }
    
}            

 

package design.template3;

public class Cofee extends CaffeineBeverage{

    @Override
    void mixSth() {
        System.out.println("用沸水冲咖啡");
        
    }

    @Override
    void addSth() {
        System.out.println("加糖和牛奶");
        
    }


}

 

第四种情况:对模板方法进行挂钩

钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。例如咖啡加不加牛奶应该由客户决定。

具体例子如下:

package design.template4;
/**
 * 咖啡因饮料类
 * 同时进一步对prepare()方法进行抽象。
 * 因为不希望prepare被子类覆盖,所以定义为final类型。
 * prepare()方法被成为模版方法。为什么?
 * 这个例子里面:
 * 1. prepare()是一个方法
 * 2.它用作一个算法的模板,在这个例子中,算法是用来制作咖啡因饮料的。里面的每一个方法步骤都在prepare里面定义了。
 * 而且有的方法是由父类提供,有的是由子类提供。
 * 
 * 简单地说,模版方法就是提供了一个算法(包含多个方法),允许子类对其中的某几个方法提供实现。
 * 模版方法模式指在一个方法,里面包含了一个多步骤的算法骨架,其中一个或者多个步骤允许子类延后实现。允许子类在不修改算法的结构的
 * 前提下,对一个或多个步骤进行自己的实现。
 * @author lindq3
 *
 * 2017-2-15
 */
public abstract class CaffeineBeverageWithHook {
    public final  void prepare(){
        boilWater();
        mixSth();//冲东西
        pourInCup();
        // 如果顾客“想要”调料,只有这时我们才调用addCondiments()。
        if(customerWantsCondiments()){
            addSth();//加东西
        }
    }

    abstract void mixSth();
    abstract void addSth();
    
    private void boilWater() {
        System.out.println("煮沸水");
        
    }
    
    private void pourInCup() {
        System.out.println("倒入杯子");
    }
    //定义一个方法作为hook
    boolean customerWantsCondiments(){
        return true;
    }
}


package design.template4;

public class Cofee extends CaffeineBeverageWithHook{

    @Override
    void mixSth() {
        System.out.println("用沸水冲咖啡");
        
    }

    @Override
    void addSth() {
        System.out.println("加糖和牛奶");
        
    }

    //定义一个方法作为hook
        //客户不想要加东西
    boolean customerWantsCondiments(){
        return false;
    }

}
    
View Code

 

 

三、总结使用模版方法模式的优势:

 

四、参考:

《 Head first设计模式 》学习笔记 – 模板方法模式  2017-02-14 ImportNew

posted on 2017-02-15 01:44  lukelin1989  阅读(173)  评论(0编辑  收藏  举报

导航