[Modo de diseño] Modo de plantilla, ¡apréndelo y escribe código elegante y robusto!

Sustituir a la fuerza

Definición del método de la plantilla:

Defina el esqueleto de un algoritmo en una operación, aplazando algunos pasos a las subclases. El método de plantilla permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar la estructura del algoritmo.

Define el esqueleto de un algoritmo en una operación, pero difiere algunos pasos del algoritmo a las subclases, lo que permite que las subclases redefinan ciertos pasos del algoritmo sin cambiar la estructura del algoritmo.

Es un patrón de comportamiento.

¿Sobre qué está escrito esto? No se preocupe, supongamos primero un escenario y veamos qué significa esto a través del código.

Suponer:

AnimalLa clase abstracta de animal tiene metabolismel método de metabolismo, que se ejecuta después de tres acciones de comer(), beber() y dormir().

Poni Pony, Calfherencia Animal.

Diagrama de clases para este escenario hipotético:

Código

/**
 * 模板方法设计模式
 * 范式重写的方法,系统帮我们自动调用的
 * 都可以称之为模板方法
 */
public class Main {
    public static void main(String[] args) {
        Animal pony = new Pony();
        pony.metabolism();
    }
}

abstract class Animal {
    /**
     * 新陈代谢
     * 假设都需要经过吃、喝、睡
     */
    public void metabolism() {
        eat();
        drink();
        sleep();
    }

    abstract void eat();

    abstract void drink();

    abstract void sleep();
}

class Pony extends Animal {
    @Override
    void eat() {
        System.out.println("Pony eating");
    }

    @Override
    void drink() {
        System.out.println("Pony drinking");
    }

    @Override
    void sleep() {
        System.out.println("Pony sleeping");
    }
}

Si hay otro ternero Calf, también es un animal pequeño y puede heredar naturalmente de Animalél. ¿Qué pasa si el método de metabolismo del ternero se llama en el método principal? Simplemente llamarlo directamente está hecho, no necesita llamar a comer, beber y dormir uno por uno (se llama automáticamente en el método de metabolismo).

class Calf extends Animal {
    @Override
    void eat() {
        System.out.println("Calf eating");
    }

    @Override
    void drink() {
        System.out.println("Calf drinking");
    }

    @Override
    void sleep() {
        System.out.println("Calf sleeping");
    }
}

transferir:

public static void main(String[] args) {
    Animal pony = new Pony();
    pony.metabolism();
    System.out.println("------------------------");
    Animal calf = new Calf();
    calf.metabolism();
}

resultado de la operación:

Pony eating
Pony drinking
Pony sleeping
------------------------
Calf eating
Calf drinking
Calf sleeping

Process finished with exit code 0

OK, el programa está terminado, ¡y usé el patrón de diseño del método de plantilla !

Mirando hacia atrás ahora, la definición del método de plantilla:

Define el esqueleto de un algoritmo en una operación, pero difiere algunos pasos del algoritmo a las subclases, lo que permite que las subclases redefinan ciertos pasos del algoritmo sin cambiar la estructura del algoritmo.

小动物类Animal的新陈代谢方法metabolism就相当于一个算法骨架,我们的子类并没有改变算法(没有重写),只是对算法内部调用的方法继承下来,每个子类对这些特定步骤有自己的实现而已。

客户端调用的时候只需要调用骨架即可,具体的内部方法(eat、drink、sleep),系统会帮我们自动调用。

这种模式就是模板方法设计模式

是的,我们一直在用它!

模板方法类图

这个时候,我们应该已经很熟悉模板方法了,类图很简单:

模板方法在Spring中的应用

1. JDBCTemplate

JDBCTemplate是Spring对JDBC的封装,开发人员自己写SQL,需要注入dataSource。

2. NamedParameterJdbcTemplate

也是基于JDBC的封装,不过在参数的书写上不使用 ? 占位符,而是使用 :参数名 的形式。

2. RestTemplate

RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

3. AmqpTemplate

AmqpTemplate接口定义了发送和接收消息的所有基本操作。

4. AbstractBeanFactory

下面提供一个简化版的 AbstractBeanFactory 抽象类,该类实现了获取实例的 getBean 方法,在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作。

getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。

/**
 * 抽象类定义模板方法
 * 继承了 DefaultSingletonRegistry ,也就具备了使用单例注册类方法
 *
 * @author 行百里者
 * @date 2022-07-17 22:33
 */
public abstract class AbstractBeanFactory extends DefaultSingletonRegistry implements BeanFactory {

    /**
     * 获取实例
     * 主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作
     * @param name 实例名称
     * @return 实体类
     * @throws BeansException 抛出 Bean 异常信息
     */
    @Override
    public Object getBean(String name) throws BeansException {
        //获取单例类
        Object bean = getSingleton(name);
        if (bean != null) {
            return bean;
        }
        //如果实体类为空,则创建实例(具备了创建实例的能力)
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name, beanDefinition);
    }

    /**
     * 获取 Bean 定义
     * 由实现此抽象类的其他类做相应的实现
     * @param beanName Bean名称
     * @return Bean 的定义信息
     * @throws BeansException 抛出Bean异常
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    /**
     * 抽象方法:创建 Bean 实例
     * 由实现此抽象类的其他类做相应的实现
     * @param beanName Bean 名称
     * @param beanDefinition Bean 的定义信息
     * @return Bean 实体类
     * @throws BeansException 抛出Bean异常
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
}

总结模板方法的特点

通过上面的例子,我们可以看到,这种模式的优点:

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

当然我们也能看到,对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。

以上。

点个赞再走吧~

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

Supongo que te gusta

Origin juejin.im/post/7121543775133892644
Recomendado
Clasificación