模版方法模式

模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。这样,子类可以重新定义算法的某些步骤而不改变算法的结构。

结构

模板方法模式的结构包括以下关键组成部分:

  1. 抽象类(Abstract Class):抽象类是模板方法模式的核心组件之一。它定义了模板方法,该方法是算法的骨架,包含了算法的基本步骤。抽象类可能包含一个或多个抽象方法,这些方法由子类来实现,用于定制算法的特定步骤。抽象类可以包含一些具体方法,这些方法可以有默认实现,子类可以选择性地覆盖。

  2. 具体子类(Concrete Subclasses):具体子类是抽象类的派生类,它们实现了抽象类中的抽象方法,以提供算法的具体实现。每个具体子类可以根据需要覆盖抽象方法,以自定义算法的特定步骤。

  3. 模板方法(Template Method):模板方法是抽象类中定义的方法,它包含了算法的主要逻辑和步骤顺序。模板方法调用抽象方法和/或具体方法,以完成算法的各个步骤。模板方法通常被声明为 final,以确保子类无法改变算法的骨架。

  4. 基本方法(Basic Method):是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

    1. 抽象方法(Abstract Method):一个抽象方法由抽象类声明、由其具体子类实现。
    2. 具体方法(Concrete Method):一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖,也可以直接继承。
    3. 钩子方法(Hook Method):在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为Boolean类型。

下面是模板方法模式的结构示意图:

               +-----------------------------------------+
               |               AbstractClass             |
               +-----------------------------------------+
               | +TemplateMethod() : void               |
               | -AbstractMethod1() : void              |
               | -AbstractMethod2() : void              |
               | -ConcreteMethod() : void (optional)    |
               +-----------------------------------------+
                                    |
                                    |
               +-----------------------------------------+
               |         ConcreteSubclass1              |
               +-----------------------------------------+
               | -AbstractMethod1() : void (override)   |
               | -AbstractMethod2() : void (override)   |
               +-----------------------------------------+
                                    |
                                    |
               +-----------------------------------------+
               |         ConcreteSubclass2              |
               +-----------------------------------------+
               | -AbstractMethod1() : void (override)   |
               | -AbstractMethod2() : void (override)   |
               +-----------------------------------------+

在这个结构中,抽象类中的 TemplateMethod 包含了算法的基本步骤,它调用了抽象方法和/或具体方法,而具体子类则根据需要提供这些方法的具体实现。这种结构允许在不改变算法整体结构的情况下,让不同的子类实现自定义的算法步骤。

示例

下面将用示例来演示模板方法模式的实现。

将创建一个简单的咖啡制作模板,其中包含了制作咖啡的算法骨架,但具体的步骤由子类来实现。

首先创建一个抽象类 CoffeeTemplate,它包含了制作咖啡的模板方法 makeCoffee() 和两个抽象方法 brewCoffeeGrinds()addCondiments(),这两个方法由具体的子类实现。

abstract class CoffeeTemplate {
    // 模板方法,定义了咖啡制作的基本流程
    final void makeCoffee() { // 加上final修饰,避免被子类继承而修改其结构
        boilWater();
        brewCoffeeGrinds();
        pourInCup();
        addCondiments();
    }

    // 具体方法,烧开水
    void boilWater() {
        System.out.println("Boiling water");
    }

    // 抽象方法,冲泡咖啡
    abstract void brewCoffeeGrinds();

    // 抽象方法,将咖啡倒入杯子
    abstract void pourInCup();

    // 抽象方法,添加调味品
    abstract void addCondiments();
}

然后,创建两个具体子类,分别是 BlackCoffeeCappuccino,它们分别实现了 brewCoffeeGrinds()addCondiments() 方法。

class BlackCoffee extends CoffeeTemplate { // 黑咖啡
    @Override
    void brewCoffeeGrinds() {
        System.out.println("Dripping coffee through filter");
    }

    @Override
    void pourInCup() {
        System.out.println("Pouring coffee into cup");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

class Cappuccino extends CoffeeTemplate { // 卡布奇诺
    @Override
    void brewCoffeeGrinds() {
        System.out.println("Espresso shot");
    }

    @Override
    void pourInCup() {
        System.out.println("Pouring coffee into cup");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding foamed milk");
    }
}

最后可以创建一个客户端程序来制作不同类型的咖啡。

public class CoffeeMaker {
    public static void main(String[] args) {
        CoffeeTemplate blackCoffee = new BlackCoffee();
        CoffeeTemplate cappuccino = new Cappuccino();

        System.out.println("Making Black Coffee:");
        blackCoffee.makeCoffee();
		System.out.println("----------------------");
        System.out.println("Making Cappuccino:");
        cappuccino.makeCoffee();
    }
}

运行结果:

image-20230920210633337

优点

模版方法模式的优点有以下几点:

  1. 代码复用: 模板方法模式将算法的通用骨架封装在一个抽象类中,这使得多个子类可以共享相同的代码,从而提高了代码的复用性。
  2. 扩展性: 由于模板方法模式允许子类实现算法的具体步骤,因此可以轻松地添加新的具体子类来扩展算法,而不会影响到现有的代码。
  3. 提高代码的可维护性: 模板方法将算法的结构清晰地分离出来,使得代码更易于理解和维护。每个步骤都位于单独的方法中,可以单独修改或扩展。
  4. 控制算法流程: 抽象类中的模板方法定义了算法的流程和顺序,这可以确保算法的执行顺序不会被修改,从而减少了错误的可能性。

缺点

  1. 限制子类的灵活性: 尽管模板方法提供了一个通用的算法骨架,但在某些情况下,这可能会限制子类的灵活性。子类必须按照固定的流程来实现算法,无法完全自由地定义自己的逻辑。
  2. 增加类的数量: 模板方法模式引入了抽象类和具体子类之间的层次结构,这可能会增加类的数量,使得代码更加复杂。
  3. 复杂性: 在大型项目中,如果不合理地使用模板方法模式,可能会导致类层次结构变得复杂,难以理解和维护。
  4. 不适用于所有情况: 模板方法模式并不适用于所有情况。如果算法的不同部分变化较少或根本不变,或者如果不需要控制算法的流程,那么使用模板方法模式可能会显得过于繁琐。

适用场景

模板方法模式适用于以下情况:

  1. 算法的骨架相同,但具体步骤不同: 当有一组算法,它们共享相同的基本结构或骨架,但具体步骤各不相同,这是模板方法模式的典型应用场景。该模式允许将算法的通用部分封装在一个抽象类中,而具体的步骤由子类实现。

  2. 避免代码重复: 当发现多个类中存在相似的代码,但每个类需要稍作修改以适应不同的情况时,可以使用模板方法模式来避免代码的重复。将共同的代码提取到模板方法中,减少了代码冗余。

  3. 定义算法的骨架而让子类提供具体实现: 当想要定义一个算法的基本框架,但希望留下一些特定部分供子类来实现时,模板方法模式非常有用。这允许将算法的通用逻辑与特定实现分离开来。

  4. 控制算法的流程: 如果需要确保算法的执行顺序不会被改变,或者需要在算法的不同步骤中执行特定的操作,模板方法模式可以帮助控制算法的流程。

  5. 基于继承的设计: 模板方法模式通常基于继承,这意味着可以通过创建具体子类来实现不同的算法版本。这对于需要多个变种或扩展的算法非常有用。

  6. 框架设计: 在设计框架或库时,模板方法模式可以用来定义框架的核心部分,同时允许用户通过子类化来自定义框架的行为。这种方式提供了一种灵活的扩展方式,使用户可以根据自己的需求来使用框架。

源码解析

AbstractList 为例,简要解析其源码,以理解其中的模板方法模式的应用:

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    // ...
    
    public E get(int index) {
        // 抽象方法,由具体子类实现
        return listIterator(index).next();
    }
    
    // ...
    
    public ListIterator<E> listIterator(int index) {
        // 默认实现
        rangeCheckForAdd(index);
        return new ListItr(index);
    }
    
    private class ListItr extends Itr implements ListIterator<E> {
        // ...
    }
    
    // ...
    
    public void add(int index, E element) {
        // 抽象方法,由具体子类实现
        listIterator(index).add(element);
    }
    
    // ...
}

在上述代码中,AbstractList 是抽象列表的基类。它提供了列表操作的通用骨架,包括 getlistIteratoradd 等方法。这些方法中的一些是抽象的,需要由具体的子类(如 ArrayList)来实现,而其他方法则提供了默认实现。

其中,getadd 方法是模板方法。get 方法通过调用 listIterator 方法获取列表迭代器,然后从迭代器中获取元素。add 方法也通过调用 listIterator 方法获取列表迭代器,然后添加元素。这两个方法的通用骨架在抽象类中定义,但具体的操作则由子类实现,以适应不同类型的列表。

这是 Java 集合框架中模板方法模式的一个示例,通过这种方式,Java 集合库提供了一种一致的方式来定义和操作不同类型的集合,同时确保了集合操作的通用性和可维护性。不同的集合类(如 ArrayListLinkedList)都继承自相应的抽象类,然后根据自身的特点来实现抽象方法,从而完成了集合的不同实现。这种设计模式提供了高度的代码重用和扩展性。

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值