设计模式 - D8 - 模板方法模式

抽象算法

假设现在我们有一个Coffe类和一个Tea类,他们分别实现了各自的饮料冲调方法

public class Coffee {

    public void prepareRecipe() {
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addSugar();
    }

    public void boilWater() {
        System.out.println("Boiling Water");
    }

    public void brewCoffeeGrinds() {
        System.out.println("Dripping Coffee through filter");
    }

    public void pourInCup() {
        System.out.println("Pouring into cup");
    }

    public void addSugar() {
        System.out.println("Adding Sugar and Milks");
    }
}
public class Tea {

    void prepareRecipe() {
        boilWater();
        steepTeaBag();
        addLemon();
        pourInCup();
    }

    public void boilWater() {
        System.out.println("Boiling water");
    }

    public void steepTeaBag() {
        System.out.println("Stepping the tea");
    }

    public void addLemon() {
        System.out.println("Adding Lemon");
    }

    public void pourInCup() {
        System.out.println("Pouring into Cup");
    }

}

观察类结构,可以发现两种饮料都有共同的冲调流程(即preapareRecipe()方法)基本一致方法,其中boilWater()和pourInCup()是相同的,而剩余的方法虽然具体实现不同,但其实是类似的;所以我们可以将preapareRecipe()这方法稍作改变并抽取出来放到一个抽象超类中,超类提供boilWater()、pourInCup()的实现,同时将剩余两个方法设为抽象,要求其子类实现,从而得出下面的代码

public abstract class CaffeineBeverage {

    // 抽取出相同的流程,并设为final,防止子类改变
    public final void prepareRecipe() {
        boilWater();
        brew();
        addCondiments();
        pourInCup();
    }

    public void boilWater() {
        System.out.println("Boiling water");
    }

    // 将原来的steepTeaBag和brewCoffeeGrinds抽取为brew方法,并要求子类实现
    public abstract void brew();

    // 将原来的addSugar和addLemon方法抽取为addCondiments,并要求子类实现
    public abstract void addCondiments();

    public void pourInCup() {
        System.out.println("Pouring into Cup");
    }

}
public class Tea extends CaffeineBeverage{

    public void brew() {
        System.out.println("Stepping the tea");
    }

    public void addCondiments() {
        System.out.println("Adding Lemon");
    }

}
public class Coffee extends CaffeineBeverage{

    public void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    public void addCondiments() {
        System.out.println("Adding Sugar and Milks");
    }
}

测试

public class TemplateTest {
    public static void main(String[] args) {
        Coffee coffee = new Coffee();
        coffee.prepareRecipe();
        System.out.println();
        Tea tea = new Tea();
        tea.prepareRecipe();
    }
}

输出
Boiling waterDripping Coffee through filter
Adding Sugar and Milks
Pouring into Cup

Boiling water
Stepping the tea
Adding Lemon
Pouring into Cup

模板方法模式

其实在上一个例子中,抽取出来的CaffeineBeverage抽象超类中的preapaerRecipe()就是一个模板方法,它作为一个算法的模板,在其中将每一个步骤用一个方法代表。这些方法有的由超类提供并处理,有的被设为抽象方法,必须由子类实现并处理。也就是模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
这种模式的的好处是

  • 模板方法类主导一切,它拥有算法并保护这个算法
  • 对于子类来说,模板方法类可将代码复用最大化
  • 算法只存在一个地方,容易修改
  • 提供了一个框架,可以让其他相关对象作为子类插入进来,其只需要实现自己的方法即可
  • 专注在算法本身,而由子类提供完整的实现

定义

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

在这里插入图片描述
可见,模板方法在实现算法的过程中,用到了两个原语操作(primitiveOperation),模板方法本身与这两个操作的具体实现之间解耦

一般形式

// 声明为抽象类,要求子类实现其操作
public abstract class AbstractClass {
	// 模板方法声明为final,避免子类更改
	final void templateMethod()	{
    	primitiveOperation1();
      	primitiveOperation2();
      	concreteOperation();
      	hook();
    }
  	
  	// 原语操作是抽象方法,子类必须实现
   abstract void primitiveOperation1();
   abstract void primitiveOperation1();
	
   // 声明为final的具体方法,可被子类使用,但不能修改
   final void concreteOperation() {
  		// 实现
  	}
  
    // 一个什么都不做的具体方法,这种方法被称为“钩子”
  	void hook() {}
}

钩子 - hook

钩子是一种被生命在抽象类中的方法,但只有空或默认的实现,其存在是让子类有能力对算法的不同点进行挂钩,而要不要挂钩,由子类自行决定。如下例子,customerWantsCondiments()就是一个钩子,它控制了模板方法中是否执行某部分的算法

public abstract class CaffeineBeverageWithHook {
    
    public final void prepareRecipe() {
        boilWater();
        brew();
        // 条件语句使用钩子判断
        if(customerWantsCondiments()) {
            addCondiments();
        }
        addCondiments();
        pourInCup();
    }

    public void boilWater() {
        System.out.println("Boiling water");
    }
    
    public abstract void brew();
    
    public abstract void addCondiments();

    public void pourInCup() {
        System.out.println("Pouring into Cup");
    }
    
    // 一个钩子,子类可以覆盖这个方法,但不是必须的
    public boolean customerWantsCondiments() {
        return true;
    }
  
}

所以,当子类必须提供算法中某个方法或步骤的实现时,该方法应该声明为抽象的。相反,当算法某个方法是可选的,就使用钩子,子类可以选择实现或不实现这个钩子

好莱坞原则

好莱坞原则:别调用我们,我们会调用你

好莱坞原则可以防止“依赖腐败”(高层组件依赖低层组件,而低层组件又依赖高层组件,而高层组件又依赖边侧组件,而边侧组件又依赖低层组件)
好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件,即对于高层组件对待低层组件的方式是:别调用我,我会调用你们
在之前的CaffeineBeverage例子中,CaffeineBeverage就是一个高层组件,它控制了算法的执行,只有在需要子类实现某个方法时,才会调用子类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值