【设计模式】模板方法与策略模式的结合使用

news2024/11/20 14:36:30

文章目录

  • 1. 概述
    • 1.1.简述模板方法
  • 2.模板方法实现
    • 2.1.简单实现
    • 2.2.在SpringBoot中的实现
  • 3.模板方法与策略模式的结合使用
    • 3.1.代码实现
  • 4.总结

1. 概述

模板方法是一种非常简单的设计模式,只要能够理解面向对象中的继承与多态就能够理解这种设计模式,我们可以在很多的框架源码中找到它的身影。
同时在我们的日常开发中,它一般是用在同类型且不同实现方式的业务逻辑中,抽取公共逻辑,简单的说就是,模板方法经常和策略模式结合使用

本篇后续的代码中会涉及到策略模式,如果还不太熟悉策略模式使用实例的同学,可以先看一下上一篇文章:《SpringBoot优雅使用策略模式》

1.1.简述模板方法

模板方法通过继承来实现,顶层是一个抽象类,用于封装通用函数,并提供一个或多个抽象方法,下层是多个实现类,用于实现不同的业务逻辑分支,类图如下:
在这里插入图片描述

2.模板方法实现

实际使用的时候,一般会通过子类的实例调用父类中的模板方法templateMethod,在模板方法中调用抽象方法,最终还是会调用到子类中覆写的实例方法,这是一种常见的钩子函数使用方式。

2.1.简单实现

  • 抽象父类:
    /**
     * 抽象父类
     */
    public abstract class BaseClass {
        final public void templateMethod() {
            System.out.println("执行模板方法");
            method1();
            method2();
        }
    
        abstract protected void method1();
    
        abstract protected void method2();
    }
    
  • 子类实现
    /**
     * 子类1
     */
    public class ChildClass1 extends BaseClass {
        @Override
        protected void method1() {
            System.out.println("执行子类1的方法1");
        }
        @Override
        protected void method2() {
            System.out.println("执行子类1的方法2");
        }
    }
    
    /**
     * 子类2
     */
    public class ChildClass2 extends BaseClass {
        @Override
        protected void method1() {
            System.out.println("执行子类2的方法1");
        }
        @Override
        protected void method2() {
            System.out.println("执行子类2的方法2");
        }
    }
    

简单的测试一下:

public static void main(String[] args) {
    BaseClass baseClass = new ChildClass1();
    baseClass.templateMethod();

    baseClass = new ChildClass2();
    baseClass.templateMethod();
}

打印出测试结果:
在这里插入图片描述

2.2.在SpringBoot中的实现

在使用SpringBoot进行开发的时候,我们通常不会手动去创建对象,而是将不同的已经创建好的bean结合起来使用,我们可以在子类的对象中,通过@Autowired将其他的bean注入进来,并在运行时执行方法调用。

如果这个方法调用是通用的,我们可以将它抽取到的父类中去,但由于父类是抽象类无法实例化,自然也无法直接通过@Autowired注入bean,此时可以对子类做一点小改造,通过构造函数对父类进行赋值。

实现起来也非常的简单,下面是代码示例:

  • 首先提供一个QueryService供子类注入
    import org.springframework.stereotype.Service;
    
    @Service
    public class QueryService {
    
        public void query() {
            System.out.println("执行查询方法");
        }
    
    }
    
  • 其次需要再抽象父类中定义QueryService
    public abstract class BaseClass {
    
        protected QueryService queryService;
    
        final public void templateMethod() {
            System.out.println("执行模板方法");
            queryService.query();
            method1();
        }
    
        abstract protected void method1();
    
    }
    
  • 最后,在子类中注入并对父类的QueryService赋值
import org.springframework.stereotype.Component;

/**
 * 子类1
 */
@Component
public class ChildClass1 extends BaseClass {

    public ChildClass1(QueryService queryService) {
        super.queryService = queryService;
    }

    @Override
    protected void method1() {
        System.out.println("执行子类1的方法1");
    }

}
/**
 * 子类2
 */
@Component
public class ChildClass2 extends BaseClass {

    public ChildClass2(QueryService queryService) {
        super.queryService = queryService;
    }

    @Override
    protected void method1() {
        System.out.println("执行子类2的方法1");
    }
    
}

使用ApplicationContext做一个简单的测试:

@Component
public class Test implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContext.getBean(ChildClass1.class).templateMethod();
        applicationContext.getBean(ChildClass2.class).templateMethod();
    }
}

在这里插入图片描述

3.模板方法与策略模式的结合使用

模板方法与策略模式天然的可以结合使用,为了大家能够有个更直观的感受,我把两个类图放到一起,大家可以做一下对比。
在这里插入图片描述
在这里插入图片描述
相信大家也很容易可以看出来,如果我们现在业务中通过策略模式,让程序能够自行选择需要使用的子类实例,只需要再加上一个选择器就好了。

3.1.代码实现

在 上篇文章 中已经介绍了如何构建选择器,有需要的同学可以去看一下,在这里就不过多赘述,直接放实现代码。

只需要两个步骤就可以完成改造:

  • 第一步,编写策略选择器选择器枚举

    import org.jetbrains.annotations.NotNull;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    import java.util.stream.Collectors;
    
    /**
     * 策略选择器
     */
    @Component
    public class Selector implements ApplicationContextAware {
    
        private Map<String, BaseClass> selectorMap;
    
        public BaseClass select(String type) {
            return selectorMap.get(type);
        }
    
        @Override
        public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
            this.selectorMap = applicationContext.getBeansOfType(BaseClass.class).values().stream()
                    .filter(strategy -> strategy.getClass().isAnnotationPresent(SelectorAnno.class))
                    .collect(Collectors.toMap(strategy -> strategy.getClass().getAnnotation(SelectorAnno.class).value(), strategy -> strategy));
        }
    }
    
    import java.lang.annotation.*;
    
    /**
     * 选择器注解
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Inherited
    @Documented
    public @interface SelectorAnno {
    
        String value();
    
    }
    
  • 第二步:在两个子类中分别加入枚举

    @Component
    @SelectorAnno("child1")
    public class ChildClass1 extends BaseClass{
    }
    
    @Component
    @SelectorAnno("child2")
    public class ChildClass2 extends BaseClass {
    }
    

改造完成之后,模拟一下调用端发起请求,做一个简单的测试:

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class Test {

    private final Selector selector;

    public Test(Selector selector) {
        this.selector = selector;
    }

    @PostConstruct
    public void test() {
        // 模拟调用端传入策略标识
        this.doInvoke("child1");
    }

    public void doInvoke(String type) {
        BaseClass baseClass = selector.select(type);
        baseClass.templateMethod();
    }

}

这里模拟的是调用端传入child1,选择子类1进行测试,打印的结果为:

执行模板方法
执行查询方法
执行子类1的方法1

4.总结

本篇文章介绍了什么是模板方法、模板方法的简单实现与在SpringBoot中的实现的。然后对比了模板方法与策略模式的类图,发现两者天然就可以结合在一起使用。最后,通过代码实现验证了两者结合使用的可行性。

当然,本篇文章中的都是简单的示例代码,突出的只是实现的思想,在日常的开发中,可以结合实际的业务流程对上述的代码进行改造。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/693993.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安卓布局详解:探索各种布局方式

文章目录 前言一、线性布局&#xff08;LinearLayout&#xff09;二、相对布局&#xff08;RelativeLayout&#xff09;三、帧布局&#xff08;FrameLayout&#xff09;四、表格布局&#xff08;TableLayout&#xff09;五、约束布局&#xff08;ConstraintLayout&#xff09;六…

自学黑客(网络安全),一般人我劝你还是算了吧(自学网络安全学习路线--第十二章 无线网络安全上)【建议收藏】

文章目录 一、自学网络安全学习的误区和陷阱二、学习网络安全的一些前期准备三、自学网络安全学习路线一、无线网络技术1、GSM、CDMA与3G网络2、无线局域网 二、移动通信网安全性分析1、GSM网络安全2、3G网络安全 一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员…

springboot项目构建docker镜像部署到云服务器

云服务器系统环境: 华为云 Ubuntu 9.3.0-17ubuntu1~20.04 1.ECS准备docker相关环境 1.1ECS安装docker 一行一行执行 都是从官网找的命令 sudo -i apt update apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common curl -fsSL htt…

【Leetcode -872.叶子相似的树 -993.二叉树的堂兄弟节点】

Leetcode Leetcode -872.叶子相似的树Leetcode -993.二叉树的堂兄弟节点 Leetcode -872.叶子相似的树 题目&#xff1a;请考虑一棵二叉树上所有的叶子&#xff0c;这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。 举个例子&#xff0c;如上图所示&#xff0c;给定一棵…

C++ 文件和流

我们已经使用了 iostream 标准库&#xff0c;它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流。 本教程介绍如何从文件读取流和向文件写入流。这就需要用到 C 中另一个标准库 fstream&#xff0c;它定义了三个新的数据类型&#xff1a; 数据类型描述of…

React createContext优化方案contextType

我们先来看个这样的组件 import React,{createContext} from "react"const ThemeContext createContext();export default class Demo14 extends React.Component {constructor(props){super(props);this.state {theme:"red"}}render(){const { theme }…

划片机QFN封装工艺流程揭秘及芯片切割分离技术及工艺应用

QFN封装工艺流程包括以下步骤 磨片&#xff1a;对晶圆厂出来的圆片进行减薄处理&#xff0c;方便在有限的空间中进行封装。 划片&#xff1a;将圆片上成千上万个独立功能的芯片进行切割分离。 装片&#xff1a;将芯片装入QFN封装壳中。 焊线&#xff1a;将芯片与壳体上的引…

Java——《面试题——Redis篇》

前文 java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 Java——《面试题——MySQL篇》​​​​​​ Java——《面试题——SpringCloud》 Java…

c++11 标准模板(STL)(std::basic_streambuf)(八)

定义于头文件 <streambuf> template< class CharT, class Traits std::char_traits<CharT> > class basic_streambuf; 类 basic_streambuf 控制字符序列的输入与输出。它包含下列内容并提供到它们的访问&#xff1a; 1) 受控制字符序列&#xff0…

hadoop-hdfs分布式文件系统理论(一)

为什么要开发HDFS分布式文件系统 可以更好的支持分布式计算。 hadoop distribute file system是一个分布式 文件系统&#xff0c;操作的是文件&#xff0c;增、删都是以文件为单位。 存储模型 文件线性按字节切割成块&#xff08;block&#xff09;&#xff0c;具有offset&a…

驱动开发:取进程模块的函数地址

在笔者上一篇文章《驱动开发&#xff1a;内核取应用层模块基地址》中简单为大家介绍了如何通过遍历PLIST_ENTRY32链表的方式获取到32位应用程序中特定模块的基地址&#xff0c;由于是入门系列所以并没有封装实现太过于通用的获取函数&#xff0c;本章将继续延申这个话题&#x…

【UE5 Cesium】03-Cesium for Unreal 添加本地数据集

上一篇&#xff1a;【UE5 Cesium】02-Cesium for Unreal 添加在线数据集 步骤 1. 在官网&#xff08;Adding Datasets – Cesium&#xff09;上下载一个示例 下载的是一个名为“Tileset.zip”的压缩文件 解压后文件内容如下 2. 打开虚幻编辑器&#xff0c;点击“Blank 3D Tiles…

macOS Ventura 13.5beta4(22G5059d)发布

系统介绍 黑果魏叔 6 月 28 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 13.5 开发者预览版 Beta 4 更新&#xff08;内部版本号&#xff1a;22G5059d&#xff09;&#xff0c;本次更新距离上次发布隔了 12 天。 macOS Ventura 带来了台前调度、连续互通相机、F…

Spring Boot + Vue3前后端分离实战wiki知识库系统<九>--文档管理功能开发一

接着Spring Boot Vue3前后端分离实战wiki知识库系统&#xff1c;八&#xff1e;--分类管理功能开发二继续往下&#xff0c;这次咱们则来到文档管理功能的开发学习了&#xff0c;对于知识库的核心功能当然得是能发表文章及查看对吧&#xff0c;所以接下来一步一个脚印来完成它&…

Linux CentOS_7.9卸载Oracle适用于多版本(亲测验证)

前言&#xff1a;近期安装测评的比较多&#xff0c;在完成使命后还是可以按部就班形成个B环&#xff0c;这里主要是记录Linux卸载Oracle&#xff0c;下述方法适用于Oracle 11g&#xff0c;Oracle 12c&#xff0c;Oracle 19g版本的数据库&#xff0c;我们这边以CentOS_7.9、Orac…

C语言进阶教程(include只能包含.h文件吗?)

文章目录 前言一、include工作原理二、include只能包含.h文件吗三、include包含文件时<>和""的区别总结 前言 include在多文件编程中是非常重要的&#xff0c;我们经常使用他来包含一些头文件&#xff0c;方便我们管理代码和项目&#xff0c;那么include是只能…

《计算机系统与网络安全》 第八章 操作系统安全基础

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

Linux文件的权限

1.Linux文件的权限 linux文件的权限可以分为四类&#xff1a;可读、可写、可执行、没有权限。分别用字符r、w、x、- 表示。 2. 用户与用户组 Linux是一个多用户多任务的操作系统&#xff0c;可以通过用户和用户组来更好的控制文件的权限。 每个文件都有一个拥有者&#xff08…

能在图片上涂鸦的软件有哪些?分享几种好用涂鸦工具

涂鸦可以增加趣味性。在照片上涂鸦可以增加趣味性&#xff0c;让照片更加有趣。无论是在旅行照片中添加一些有趣的涂鸦&#xff0c;还是在生活照片中添加一些幽默的涂鸦&#xff0c;都可以让照片更具有趣味性。很多小伙伴不清楚怎么在照片上进行涂鸦&#xff0c;下面给大家分享…

【JSP】使用 JSTL 增强 JSP 功能

本文仅供学习参考&#xff01; JSTL 代表 JavaServer Pages Standard Tag Library;它提供了一组核心网页功能&#xff0c;可以执行许多基本任务&#xff0c;例如结构元素的条件和迭代、操作 XML 文档以及支持国际化标记以更复杂的 SQL 元素处理。JSP以scriples而闻名&#xff0…