设计模式② :交给子类

文章目录

  • 一、前言
  • 二、Template Method 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 三、Factory Method 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 参考内容

一、前言

有时候不想动脑子,就懒得看源码又不像浪费时间所以会看看书,但是又记不住,所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》(【日】结城浩 著),内容仅用于个人学习记录,可随意转载。

二、Template Method 模式

Template Method 模式 :将具体处理交给子类

1. 介绍

在父类中定义处理流程框架,在子类中实现具体处理的模式就称为 Template Method 模式。

在 Template Method 模式 中登场的角色:

  • AbstractClass(抽象类):AbstractClass 角色不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类ConcreteClass角色负责实现。
  • ConcreteClass (具体类):该角色负责具体实现AbstractClass角色中定义的抽象方法。

类图如下, 抽象模板类会提供两个 methodA 、 methodB 两个抽象方法供子类实现,同时自身实现一个模板方法 templateMethod。在 templateMethod 方法中定义程序的行为,但行为的具体内容则是由子类实现:

在这里插入图片描述


Demo 如下:

	public abstract class AbstractClass {
		// 定义两个由子类实现的方法
	    protected abstract void methodA();
		// 定义两个由子类实现的方法
	    protected abstract void methodB();
	    
	    // 模板方法,在合适的场景可以将该方法定义为 final。
	    // 模板类通过该方法定义了整个程序的行为,如下行为为:先调用 methodA 再调用 methodB
	    // 对于子类则只需要关注 methodA 和 methodB 的具体实现,而不需要关注整个程序的行为。
	    public void templateMethod() {
	        methodA();
	        methodB();
	    }
	}
	// 子类只需要关注抽象方法的实现,而不需要关注整个调用过程
	public class StudentClass extends AbstractClass {
	
	    @Override
	    public void methodA() {
	        System.out.println("StudentClass.methodA");
	    }
	
	    @Override
	    public void methodB() {
	        System.out.println("StudentClass.methodB");
	    }
	}
	
	public class TeacherClass extends AbstractClass {
	
	    @Override
	    public void methodA() {
	        System.out.println("TeacherClass.methodA");
	    }
	
	    @Override
	    public void methodB() {
	        System.out.println("TeacherClass.methodB");
	    }
	}

2. 应用

Spring模板方法模式实质是模板方法模式和回调模式的结合,是Template Method不需要继承的另一种实现方式,如 JdbcTemplate、RedisTeplate、MongoTemplate 等。

这里我们以 JdbcTemplate为例,当我们调用 JdbcTemplate#execute 执行 Sql 时,JdbcTemplate#execute 流程是 DB 连接、Sql 执行、DB 释放,而我们实际只编写了Sql 部分,如下(下面代码仅作演示,真实代码并非如下)。

	private <T> T execute(String sql){
		// 1. 获取DB 连接以及其他预处理
		doDbConnect();
		// 2. Sql 执行
		executeSql(sql);
		// 3. 释放资源
		releaseDbConnection();
	}

可以看出 JdbcTemplate#execute 作为一个 Template Method 通过完成了 DB 连接与释放的功能。但实际上 Spring几乎所有的外接扩展都采用回调模式模式来执行。如下, 通过 callback 回调来执行具体的业务逻辑:

    public final Object execute(StatementCallback callback){    
        Connection con=null;    
        Statement stmt=null;    
        try {    
            con=getConnection();    
            stmt=con.createStatement();    
            Object retValue=callback.doWithStatement(stmt);    
            return retValue;    
        } catchSQLException e){    
            ...    
        } finally{    
            closeStatement(stmt);    
            releaseConnection(con);    
        }    
    }  

JDBC的抽象和对Hibernate的集成,都采用了一种理念或者处理方式,那就是模板方法模式与相应的Callback接口相结合。



个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 项目A中,需要根据通道的不同构建不同的数据集,便使用了如下的模板模式,不同的通道实现各自的模板类,其中 DataHead 和 DataDetail 可以通过继承的方式进行各个通道的数据扩展。通过 DataTemplate#getFinalData 获取最终的数据集。

    // 抽象模板方法,供不同通道的子类实现
    public abstract class DataTemplate {
    
        /**
         * 获取头数据
         * @return
         */
        protected abstract DataHead getDataHead();
    
        /**
         * 获取详细数据
         * @return
         */
        protected abstract List<DataDetail> getDataDetail();
    
        /**
         * 获取最终的数据集
         * @return
         */
        public FinalData getFinalData() {
            final FinalData finalData = new FinalData();
            finalData.setDataHead(getDataHead());
            finalData.setDataDetailList(getDataDetail());
            return finalData;
        }
    }
    
  1. 项目B中,需要对客户资料进行解析,资料固定是一个 PDF、一个 Excel 文件,需要对两个文件中的数据解析并汇总处理,而可能存在的情况是PDF 和 Excel 存在多套格式。即可以定义出来一个 FileTemplate ,不同格式的 PDF 和 Excel 实现不同的 FileTemplate ,最终完成多种模板格式的解析(可以通过策略模式对每一种不同的格式的文件实现单独的解析策略,进一步解耦)

    public abstract class FileTemplate {
    
        /**
         * 获取头数据
         * @return
         */
        protected abstract PdfData getPdfData();
    
        /**
         * 获取详细数据
         * @return
         */
        protected abstract ExcelData getExcelData();
    
        /**
         * 获取最终的数据集
         * @return
         */
        public FileData getFinalData() {
            final FileData finalData = new FileData();
            finalData.setPdfData(getPdfData());
            finalData.setExcelData(getExcelData());
            return finalData;
        }
    }
    

3. 总结

在 Template Method 模式中,可以使用继承(实现)改变程序的行为。这是因为 Template Method 模式在父类中定义程序行为的框架,在子类中决定具体的处理。在该模式中,处理的流程被定义在父类中,而具体的处理则交给了子类。

在 Strategy 模式中,可以使用委托改变程序的行为。与 Template Method 模式中改变部分程序行为不同的是, Strategy 模式用于替换整个算法。


相关设计模式:

  • Factory Method 模式:Factory Method 模式是将 Template Method 模式用于生成实例的一个典型例子。
  • Strategy 模式:在 Template Method 模式中,可以使用继承(实现)改变程序的行为。这是因为 Template Method 模式在父类中定义程序行为的框架,在子类中决定具体的处理。在该模式中,处理的流程被定义在父类中,而具体的处理则交给了子类。而在 Strategy 模式中,他可以使用委托改变程序的行为。与 Template Method 中改变部分程序行为不同的是,Strategy 模式用于替换整个算法

三、Factory Method 模式

Factory Method 模式 :将实例的生成交给子类。

1. 介绍

Template Method 模式在父类中定义程序行为的框架,在子类中决定具体的处理。如果将该模式用于生成实例,那么他将演变成 Factory Method 模式。在该模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责。这样就可以将生成实例的框架和实际负责生成实例的类解耦。


Factory Method 模式中登场的角色

  • Product (产品):Product 属于框架这一方,是一个抽象类。它定义了在 Factory Method 模式中生成的那些实例所持有的接口 (API),但具体的处理则由子类角色决定。
  • Creator (创建者):Creator 属于框架这一方,它是负责生成 Product 角色的抽象类,但具体的处理规则由子类角色决定。Creator 角色对于实际负责生成实例的ConcreteCreator角色一无所知,它唯一知道的就是只要调用 Product 角色和生成实例的方法就可以生成 Product 的实例。
  • ConcreteProduct (具体的产品):ConcreteProduct 属于具体的加工者一方,它决定了具体的产品。
  • ConcreteCreator(具体的创建者):ConcreteCreator 属于具体的加工者一方,它负责生产具体的产品。

类图如下:我们可以得知父类(框架)这一方面的角色的关系与子类(具体加工)这一方面的角色关系是平行的。这里Creator 定义了 create 方法用于创建(生产) Product 实例,同时 factoryMethod 则是该工厂类的其他工厂方法,具体需要根据业务去定义。

在这里插入图片描述


Demo 如下:

	public abstract class Creator {
	    // create 方法用于创建 具体 Product,并可以执行其他逻辑,如 调用registerProduct方法实现注册功能等(具体看业务需求)
	    public final Product create(){
	        final Product product = createProduct();
	        registerProduct(product);
	        return product;
	    }
	    
	    // 下面是 factoryMethod 方法
	    
	    // 使用 createProduct 创建 Product,目的是父类与子类解耦。
	    protected abstract Product createProduct();
	    // 注册产品
	    protected abstract void registerProduct(Product product);
	
	}
	
	public abstract class Product {
	    // 随意定义的两个 方法
	    public abstract void methodA();
	
	    public abstract void methodB();
	}
	
	public class ConcreteCreator extends Creator{
	    // 实现 factoryMethod, 根据业务需要可以有不同的实现
	
	    @Override
	    protected Product createProduct() {
	        return new ConcreteProduct();
	    }
	    // 根据业务需要进行实现,这里随意调用了 Product 的两个方法
	    @Override
	    protected void registerProduct(Product product) {
	        product.methodA();
	        product.methodB();
	    }
	}
	
	public class ConcreteProduct extends Product {
	
	    @Override
	    public void methodA() {
	        System.out.println("ConcreteProduct.methodA");
	    }
	
	    @Override
	    public void methodB() {
	        System.out.println("ConcreteProduct.methodB");
	    }
	}
	
	// 用于测试的 Main 方法
	public class FactoryMethodMain {
	    public static void main(String[] args) {
	        // 实际场景下  Creator 应该有多个实现类,而在这种情况下可以使用工厂模式或简单工厂模式来获取 Creator 实例。
	        Creator creator = new ConcreteCreator();
	        // 调用模板方法创建具体实例
	        final Product product = creator.create();
	        // TODO : do something
	    }
	}

2. 应用

  • Spring 框架支持通过 factory-bean 和 factory-method 属性的方式来指定工厂方法来创建Bean。如下指定 DesignConfig#designDemo 的方法来创建 DesignDemo 并注册到容器中,在指定的 factory-method 方法中可以实现自定义的逻辑:

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="designConfig" class="com.kingfish.pojo.config.DesignConfig"/>
        <bean id="designDemo" class="com.kingfish.pojo.DesignDemo" factory-bean="designConfig" factory-method="designDemo"/>
    
    </beans>
    


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 项目A中,不同的 Region 都会获取相同的数据,但数据来源以及拼接不同,因此计划在服务启动或在其他触发条件下,生成一个 RegionContent 类,可以作为上下文或者其他的数据,不同 Region 进行具体实现,根据 Region 的不同加载不同的 RegionContent 实例来获取对应的全局数据。如下:

    /**
     * 顶层定义一个 Creator 接口
     */
    public interface ContextCreator {
        RegionContent createContext();
    }
    
    // 定义一个 Region Creator 的抽象类,用于创建 RegionContent 
    public abstract class RegionContentCreator implements ContextCreator {
    	
        @Override
        public final RegionContent createContext() {
            final RegionContent regionContent = doCreateContent();
            postProcess(regionContent);
            return regionContent;
        }
    	// 创建方法
        protected abstract RegionContent doCreateContent();
    	// 后置处理,在 RegionContent 创建的后置处理 - 需要的话可以实现
        protected abstract void postProcess(RegionContent regionContent);
    }
    
    
    // SH  Region的实现
    @Slf4j
    public class ShRegionContentCreator extends RegionContentCreator {
        @Override
        protected RegionContent doCreateContent() {
            return new ShRegionContext();
        }
    
        @Override
        protected void postProcess(RegionContent regionContent) {
            log.info("[region 后期处理][regionContext = {}]", regionContent);
        }
    }
    
    // RegionContent 接口定义
    public interface RegionContent {
        /**
         * 返回 region 标识
         * @return
         */
        String getRegion();
    
        /**
         * 获取关区内容
         * @return 懒得定义实现类,所以返回 Object
         */
        Object getContent();
    }
    
    // SH RegionContent 的实现
    public class ShRegionContext implements RegionContent {
    
    
        @Override
        public String getRegion() {
            // 应该用全局变量或者枚举
            return "sh";
        }
    
        @Override
        public Object getContent() {
            // 随便返回
            return "这里是 SH Region 的 Content";
        }
    }
    
    @Slf4j
    public class DemoMain {
        public static void main(String[] args) {
        	// 创建 RegionContent 。实际业务会有多个 RegionContentCreator, 可以根据环境或者参数加载
            RegionContentCreator regionContentCreator = new ShRegionContentCreator();
            final RegionContent content = regionContentCreator.createContext();
            // 随便打印
            log.info("region = {}, regionContent = {}", content.getRegion(), content.getContent());
        }
    }
    

3. 总结

相关的设计模式:

  • Template Method 模式:Factory Method 模式是 Template Method 的典型应用
  • Singleton 模式:多数情况下 Singleton 模式用于扮演 Creator 角色 或者 ConcreteCreator 橘色的类,这是因为在程序中没有必要存在多个Creator 或 ConcreteCreator 角色的实例。
  • Composite 模式:有时候可以将 Composite 模式用于 Product 或 ConcreteProduct 角色。
  • Iterator 模式:有时在 Iterator 模式中使用 iterator 方法生成 Iterator 的实例时会使用 Factory Method。

参考内容

https://mp.weixin.qq.com/s/JUV4cnE_HqRMFriKMHk0Ug

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#之反编译之路(一)

本文将介绍微软反编译神器dnSpy的使用方法 c#反编译之路(一) dnSpy.exe区分64位和32位,所以32位的程序,就用32位的反编译工具打开,64位的程序,就用64位的反编译工具打开(个人觉得32位的程序偏多,如果不知道是32位还是64位,就先用32位的打开试试) 目前只接触到wpf和winform的桌…

算法每日一题:在链表中插入最大公约数 | 链表 | 最大公约数

hello&#xff0c;大家好&#xff0c;我是星恒 今天的题目是有关链表和最大公约数的题目&#xff0c;比较简单&#xff0c;核心在于求解最大公约数&#xff0c;我们题解中使用辗转相除法来求解&#xff0c;然后我们会在最后给大家拓展一下求解最大公约数的四个方法&#xff0c;…

码云Gitee复制 GitHub 项目

码云提供了直接复制 GitHub 项目的功能&#xff0c;方便我们做项目的迁移和下载。 1.新建仓库 2.导入仓库 3.强制同步 如果 GitHub 项目更新了以后&#xff0c;在码云项目端可以手动重新同步&#xff0c;进行更新&#xff01;

odoo16 连接postgresql错误

odoo16 连接postgresql错误 odoo16 用odoo15的环境出错&#xff0c;看到是psycopg2.OperationalError分析是postgresql版本问题&#xff0c;安装了13版本&#xff0c;还是出错&#xff0c;多版本共存问题如下&#xff1a; Traceback (most recent call last):File "D:\o…

数一下 1到 100 的所有整数中出现多少个数字9并输出这些数字

分析&#xff1a; 我们知道 1-100的整数 i 中&#xff0c;9会出现在十位和个位上&#xff0c;数9出现的次数可以通过以下来实现&#xff1a; 个位是9 // i % 10得到整数 i 个位上的数十位是9 // i / 10得到整数 i 除了个位数的数字 这也是做这道题之后&#xff0c;我们需要…

MySQL基础笔记(5)DCL数据控制语句

数据控制语句&#xff0c;用来管理数据库用户、控制数据库的访问权限~ 目录 一.用户管理 1.查询用户 2.创建用户 3.修改用户密码 4.删除用户 二.权限管理 1.查询权限 2.授予权限 3.撤销权限 一.用户管理 1.查询用户 use MySQL; select * from user; 2.创建用户 crea…

MySQL——视图

目录 一.视图介绍 二.基本使用 三.视图规则和限制 一.视图介绍 视图是一个虚拟表&#xff0c;其内容由查询定义。同真实的表一样&#xff0c;视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表&#xff0c;基表的数据变化也会影响到视图。 二.基本使用 创…

深入理解MySQL索引底层数据结构

听课问题(听完课自己查资料) 什么是二叉树 二叉树是怎么存储数据的一个链表是一个集合的数据结构 List是怎么便利找到指定下标元素为什么会快&#xff1f;什么是红黑树 红黑树是怎么存储数据的什么是B TREE 是怎么存储数据的什么是BTREE 是怎么存储数据的 疑惑答案 a. 二叉树…

用通俗易懂的方式讲解:结合检索和重排序模型,改善大模型 RAG 效果明显

最近出现了在构建聊天机器人方面的应用浪潮&#xff0c;这主要得益于LlamaIndex 和 LangChain 这样的框架。许多这类应用都采用了用于检索增强生成&#xff08;RAG&#xff09;的标准技术栈&#xff0c;其中包括以下关键步骤&#xff1a; 向量存储库&#xff1a; 使用向量存储库…

状态机(有限状态机(Finite State Machine, FSM)、推进自动机(Pushdown Automata)、并发状态机、分层状态机)

文章目录 状态机&#xff08;State Machine&#xff09;定义与组成定义组成状态&#xff08;States&#xff09;事件&#xff08;Events&#xff09;转换&#xff08;Transitions&#xff09;初始状态&#xff08;Initial State&#xff09; 状态机的类型有限状态机&#xff08…

网络调试 UDP1,开发板用静态地址-入门6

https://www.bilibili.com/video/BV1zx411d7eC?p11&vd_source109fb20ee1f39e5212cd7a443a0286c5 1, 开发板连接路由器 1.1&#xff0c;烧录无OS UDP例程 1.2&#xff0c;Mini USB连接电脑 1.3&#xff0c;开发板LAN接口连接路由器 2. Ping开发板与电脑之间通信* 2.1 根据…

某金属加工公司的核心人才激励体系搭建项目纪实

【客户行业】金属加工行业 【问题类型】薪酬体系/激励体系 【客户背景】 某大型金属加工企业位于河北地区&#xff0c;成立于2000年&#xff0c;隶属于某大型有色金属集团&#xff0c;是一家集科研、开发、生产、销售于一体的国有企业&#xff0c;人员达到1000人。经过多年…

【Redis端口】通过修改端口一个计算机上可以运行两个redis

一个计算机上可以运行多个Redis实例。每个Redis实例都会监听一个特定的端口&#xff0c;所以只要确保每个实例使用的端口不冲突&#xff0c;就可以在同一台计算机上运行多个Redis实例。例如&#xff0c;你可以配置一个Redis实例监听6379端口&#xff0c;另一个Redis实例监听638…

阿里云服务器配置jupyter(新手入门,详细全面)

设置安全组 1.租好服务器后在阿里云服务器平台上打开控制台&#xff08;右上角&#xff09; 2.点开自己的云服务器控制台&#xff0c;在左栏“安全组”部分添加安全规则&#xff0c;点击“管理规则” 单击“手动添加”&#xff0c;将安全组设为如下格式&#xff0c;端口范围…

Java经典框架之Dubbo

Dubbo Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. Dubbo概述 2. Dubbo基本应用 3…

React基础应用及常用代码

目录 基础知识 babel.config.js js,html,css,Vue,react,angular,nodejs,webpack,less,ES6,commonjs等的关系 ECMAScript 6&#xff08;ES6&#xff09; let、const、var 的区别 Webpack、npm、node关系 props和state区别 通用框架类 ES 6 export React React.Fragm…

【LLM】大型语言模型:2023年完整指南

Figure 1: Search volumes for “large language models” 近几个月来&#xff0c;大型语言模型&#xff08;LLM&#xff09;引起了很大的轰动&#xff08;见图1&#xff09;。这种需求导致了利用语言模型的网站和解决方案的不断开发。ChatGPT在2023年1月创下了用户群增长最快…

thinkphp学习04-控制器定义

控制器&#xff0c;即 controller&#xff0c;控制器文件存放在 controller 目录下&#xff1b; 如果想改变系统默认的控制器文件目录&#xff0c;可以在 config 下 route.php 配置&#xff1a; 将controller修改为controller123&#xff0c;就会报错&#xff0c;说明这个配置…

【大数据进阶第三阶段之Hive学习笔记】Hive安装

【大数据进阶第三阶段之Hive学习笔记】Hive安装-CSDN博客 【大数据进阶第三阶段之Hive学习笔记】Hive常用命令和属性配置-CSDN博客 【大数据进阶第三阶段之Hive学习笔记】Hive基础入门-CSDN博客 【大数据进阶第三阶段之Hive学习笔记】Hive查询、函数、性能优化-CSDN博客 …

Linux与安全

本心、输入输出、结果 文章目录 系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第八部分&#xff1a;Linux、安全 前言 Linux 文件系统解释应该知道的 18 个最常用的 Linux 命令HTTPS如何工作&#xff1f; 数据是如何加密和解密的&#xff1f;为什么HTTPS在数据传输过程…
最新文章