一个菜鸡JAVA后端的博客~

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

定义

  • Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤)

优点

  • 封装不变部分,扩展可变部分
  • 提取公共部分代码,便于维护
  • 行为由父类控制,子类实现

缺点

  • 按照一般设计习惯,抽象类负责声明最抽象、最一般的事务属性和方法,实现类完成具体的事务属性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,在复杂的项目中,会带来代码阅读的难度

使用场景

  • 场景:
    • 多个子类有公有的方法,并且逻辑基本相同时。
    • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
    • 重构时使用,把相同的代码抽取到父类中,然后通过钩子函数约束其行为(参考模板方法模式的扩展)。
  • 模板方法模式在开源框架中运用广泛,提供一个抽象类,然后提供了一堆子类。如果需要扩展功能,可以继承这个抽象类,然后复写protected方法,然后在调用一个类是execute方法,就能完成扩展开发。

代码

模板方法模式可能是最常用的设计模式之一了,因为它只需要依靠Java或者其他语言的扩展机制就能实现。并且由于它太基本了,可能使你并没有意识到自己使用的是模板方法模式

通用类图

模板方法模式通用类图

基本的模板方法模式

类图

模板方法模式基本类图

代码

模板父类(抽象类

package com.pattern.templatemethod.template;
public abstract class AbstractClass {
    // 具体的方法,由子类去实现,尽量使用protected修饰
    protected abstract void step1();
    protected abstract void step2();
    protected abstract void step3();
    // 模板方法,抽取子类的共有代码。同时使用为了防止子类复写模板方法使用final修饰
    public final void start(){
        // 具体的模板方法,子类共有的代码
        this.step1();
        this.step2();
        this.step3();
    }
}

具体子类1(继承自模板父类

package com.pattern.templatemethod.template;

public class ConcreteClass1 extends AbstractClass{

    @Override
    protected void step1() {
        System.out.println("ConcreteClass1步骤1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClass1步骤2");
    }

    @Override
    protected void step3() {
        System.out.println("ConcreteClass1步骤3");
    }
    
}

具体子类2(继承自模板父类

package com.pattern.templatemethod.template;

public class ConcreteClass2 extends AbstractClass{

    @Override
    protected void step1() {
        System.out.println("ConcreteClass2步骤1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClass2步骤2");
    }

    @Override
    protected void step3() {
        System.out.println("ConcreteClass2步骤3");
    }
    
}

测试代码

package com.pattern;

import com.pattern.templatemethod.template.AbstractClass;
import com.pattern.templatemethod.template.ConcreteClass1;
import com.pattern.templatemethod.template.ConcreteClass2;

import org.junit.Test;

public class TemplateMethodTest {
    @Test
    /**
     * 基本的模板方法模式测试
     */
    public void TemplateTest(){
        AbstractClass client1 = new ConcreteClass1();
        AbstractClass client2 = new ConcreteClass2();
        client1.start();
        client2.start();
    }
}

结果

模板方法模式执行结果

模板方法模式的扩展

类图

模板方法模式扩展类图

代码

模板父类(抽象类

package com.pattern.templatemethod.extend;
public abstract class AbstractExtendClass {
    // 具体的方法,由子类去实现,尽量使用protected修饰
    protected abstract void step1();
    protected abstract void step2();
    protected abstract void step3();
    // 步骤4,通过钩子控制是否执行
    protected abstract void step4();
    // 定义了一个钩子,用于控制是否执行step4。可以由子类继承,然后控制此钩子
    protected boolean isExecute(){
        return true;
    }
    // 模板方法,抽取子类的共有代码。同时使用为了防止子类复写模板方法使用final修饰
    public final void start(){
        // 具体的模板方法,子类共有的代码
        this.step1();
        this.step2();
        this.step3();
        // 如果为true执行step4,如果为false不执行step4
        if(isExecute()){
            this.step4();
        }

    }
}

具体子类1(继承自模板父类

package com.pattern.templatemethod.extend;
public class ConcreteExtendClass1 extends AbstractExtendClass{

    @Override
    protected void step1() {
        System.out.println("ConcreteClass1步骤1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClass1步骤2");
    }

    @Override
    protected void step3() {
        System.out.println("ConcreteClass1步骤3");
    }

    @Override
    protected void step4() {
        System.out.println("钩子被激活,执行了步骤4");
    }
    // 由于子类1不需要执行步骤4,使用重写了isExecute控制钩子,使得它永远不执行步骤4
    @Override
    protected boolean isExecute() {
        return false;
    }
    
}

具体子类2(继承自模板父类

package com.pattern.templatemethod.extend;

public class ConcreteExtendClass2 extends AbstractExtendClass {
    private boolean execute;
    @Override
    protected void step1() {
        System.out.println("ConcreteClass2步骤1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClass2步骤2");
    }

    @Override
    protected void step3() {
        System.out.println("ConcreteClass2步骤3");
    }

    @Override
    protected void step4() {
        System.out.println("钩子被激活,执行了步骤4");
    }

    // 子类2并不知道是否要执行步骤4,所以我们在外部控制。
    @Override
    protected boolean isExecute() {
        return this.execute;
    }
    // 用于外界控制构造
    public void setExecute(boolean execute){
        this.execute = execute;
    }
}

测试代码

package com.pattern;

import com.pattern.templatemethod.extend.AbstractExtendClass;
import com.pattern.templatemethod.extend.ConcreteExtendClass1;
import com.pattern.templatemethod.extend.ConcreteExtendClass2;
import com.pattern.templatemethod.template.AbstractClass;
import com.pattern.templatemethod.template.ConcreteClass1;
import com.pattern.templatemethod.template.ConcreteClass2;

import org.junit.Test;

public class TemplateMethodTest {
    @Test
    /**
     * 扩展的模板方法模式测试
     */
    public void ExtendTemplateTest(){
        ConcreteExtendClass1 client1 = new ConcreteExtendClass1();
        ConcreteExtendClass2 client2 = new ConcreteExtendClass2();
        System.out.println("----------子类1并不需要执行步骤4-----------");
        // 子类1并不需要执行步骤4
        client1.start();
        // 子类2执行步骤4需要外界控制
        // 首先子类2想执行步骤4
        System.out.println("----------首先子类2想执行步骤4-----------");
        client2.setExecute(true);
        client2.start();
        // 然后子类2不想执行步骤4了
        System.out.println("----------然后子类2不想执行步骤4了-----------");
        client2.setExecute(false);
        client2.start();
    }
}

运行结果

模板方法模式扩展执行结果

注意

  • 为了防止恶意的操作,一般模板方法都加上final关键字,不允许被覆写
  • 抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限。

NOTE: 文章若无特别说明均为原创文章,如果要转载请保留出处!