23种设计模式之模板方法(Template)模式

1,675 阅读3分钟

1、定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义改算法的某些特定步骤。模板方法模式是一种基于继承的代码复用技术。

2、模式结构

模板方法模式由两部分组成:

  • AbstractClass(抽象类):在抽象类中定义了一系列基本操作,这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤,同时,在抽象类实现了模板方法,用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象的子类中实现的基本方法,还可以调用其他对象的方法。
  • 模板方法(Template Method):定义了算法的骨架,按某种顺序调用其包含的基本方法。一般以final修饰,不允许子类重写。
  • 基本方法是实现算法各个步骤的方法,是模板方法的组成部分。基本方法分为三种。
  • 抽象方法(Abstract Method):父类中只声明但不加以实现,而是定义好规范,然后由它的子类去实现。
  • 具体方法(Concrete Method):由抽象类声明并加以实现,其子类可以进行重写或者直接继承。
  • 钩子方法(Hook Method):由抽象类声明并加以实现。但是子类可以去扩展,子类可以通过扩展钩子方法来影响模板方法的逻辑。
  • ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体操作。

3、实例

3.1 豆浆类(AbstractClass)

public abstract class SoyMilk {

    public final void make() {
        select();
        if (customerWantCondiments()) {
            addCondiments();
        }
        soak();
        beat();
    }

    public void select() {
        System.out.println("选择上好的新鲜黄豆。");
    }

    public abstract void addCondiments();

    public void soak() {
        System.out.println("浸泡三小时原料。");
    }

    public void beat() {
        System.out.println("放入到豆浆机打碎。");
    }

    // 钩子方法,决定是否需要添加配料
    protected boolean customerWantCondiments() {
        return true;
    }

}

3.2 具体豆浆类(ConcreteClass)

public class AlmondSoyMilk extends SoyMilk {
    
    @Override
    public void addCondiments() {
        System.out.println("加入上好的杏仁。");
    }
}
public class WalnutSoyMilk extends SoyMilk {
    
    @Override
    public void addCondiments() {
        System.out.println("加入上好的核桃。");
    }
}
public class OriginalSoyMilk extends SoyMilk {
    
    @Override
    public void addCondiments() {}
    
    @Override
    protected boolean customerWantCondiments() {
        return false;
    }
}

3.3 客户端调用

public class Client {

    public static void main(String[] args) {
        
        System.out.println("------制作杏仁豆浆------");
        SoyMilk almondSoyMilk = new AlmondSoyMilk();
        almondSoyMilk.make();
        
        System.out.println("------制作核桃豆浆------");
        SoyMilk walnutSoyMilk = new WalnutSoyMilk();
        walnutSoyMilk.make();
        
        System.out.println("------制作原味豆浆------");
        SoyMilk originalSoyMilk = new OriginalSoyMilk();
        originalSoyMilk.make();
    }
}

4、适用场景

  • 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
  • 控制子类扩展。模板方法只在特定点调用“hook”操作,这样就只允许在这些点扩展。

5、在Spring中的应用

6、优缺点

6.1 优点
  • 可以将公共代码行为进行提取,以达到复用的目的。
  • 子类实现算法的某些细节,有助于算法的扩展。
  • 可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一个特定步骤是否需要执行。
6.2 缺点
  • 通过继承实现代码复用来改变算法,使得灵活度会降低。
  • 子类的执行影响父类的结果,增加代码阅读难度。

源代码:github.com/freedom9/de…