设计模式の享元模板代理模式

文章目录

  • 前言
  • 一、享元模式
  • 二、模板方法模式
  • 三、代理模式
    • 3.1、静态代理
    • 3.2、JDK动态代理
    • 3.3、Cglib动态代理
    • 3.4、小结


前言

  本篇是关于设计模式中享元模式、模板模式、以及代理模式的学习笔记。


一、享元模式

  享元模式是一种结构型设计模式,目的是为了相似对象的复用,减少内存的消耗,在享元模式中,主要包含以下的角色:

  • Flyweight(抽象享元):定义享元对象的接口,规定了可以共享的内容。
  • ConcreteFlyweight(不可变对象):实现抽象享元接口,封装内部状态,确保可以被共享。
  • UnsharedConcreteFlyweight(可变对象):不需要共享的对象。
  • FlyweightFactory(享元工厂):管理和创建享元对象,确保可以复用已经存在的享元。
  • Client(客户端):使用享元对象,负责将外部状态传递给享元对象。

  下面通过一个案例说明一下,例如围棋,有黑白两种颜色的棋子,如果为每个棋子都创建一次对象,会占用很大的内存。如果利用享元模式进行改造,可以将下棋的行为看做是抽象享元,而棋子可以视为实现了抽象享元接口的不可变对象下棋的选手可以看作是可变对象

public interface PlayChess {/*** 下棋* @param player*/void playChess(Player player);}
/*** 棋子类*/
public class GochessPieces implements PlayChess{private String color;public GochessPieces() {}public GochessPieces(String color) {this.color = color;}@Overridepublic void playChess(Player player) {System.out.println(player + "落" + color);}
}
/*** 玩家*/
public class Player {private String name;public Player() {}public Player(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Player{" +"name='" + name + '\'' +'}';}
}
/*** 享元工厂*/
public class ChessFactory {private HashMap<String,PlayChess> map = new HashMap<>();public PlayChess getChess(String color){if (!map.containsKey(color)){map.put(color,new GochessPieces(color));}return (PlayChess)map.get(color);}public int poolSize(){return map.size();}
}

  模拟下棋的操作:

/*** 模拟下棋*/
public class Client {public static void main(String[] args) {ChessFactory chessFactory = new ChessFactory();PlayChess chess1 = chessFactory.getChess("白子");chess1.playChess(new Player("张三"));PlayChess chess2 = chessFactory.getChess("黑子");chess2.playChess(new Player("张三"));PlayChess chess3 = chessFactory.getChess("白子");chess3.playChess(new Player("李四"));PlayChess chess4 = chessFactory.getChess("黑子");chess4.playChess(new Player("李四"));PlayChess chess5 = chessFactory.getChess("白子");chess5.playChess(new Player("张三"));System.out.println("chessFactory.poolSize() = " + chessFactory.poolSize());}
}

Player{name=‘张三’}落白子
Player{name=‘张三’}落黑子
Player{name=‘李四’}落白子
Player{name=‘李四’}落黑子
Player{name=‘张三’}落白子
chessFactory.poolSize() = 2

  可以看出,虽然棋子被多次利用,但是在享元工厂中,最多只有两个棋子对象,即黑子白子,其关键点在于:

  • 共享对象:黑棋和白棋各只有一个实例,无需为每个棋子创建新对象。
  • 内外部状态分离:颜色作为内部状态是共享的;位置作为外部状态在调用时传入。
  • 减少内存占用:通过共享棋子对象,大幅减少了内存开销。

二、模板方法模式

  模板方法模式是一种行为设计模式,通过定义一个算法框架,允许子类在不改变算法结构的情况下重新定义算法的某些步骤。模板方法模式的主要目的是将算法的不变部分抽取到父类中,而将可变部分留给子类去实现,提高代码的复用性和灵活性,其中还涉及到钩子方法的使用。
  举一个生活中的案例,比如制作豆浆的过程,有相同的操作,也有针对不同类型的豆浆采取的特殊处理,用一个抽象的模板方法类,说明制作豆浆的步骤,以及对外提供制作的方法:

public abstract class CreateSoyaMilk {final void make(){prepare();if (isSoakBeans()){soakBeans();}grinding();boil();}/*** 钩子方法* @return*/boolean isSoakBeans(){return true;}/*** 准备材料* 不同的豆浆准备不同材料*/public abstract void prepare();/*** 浸泡豆子* 不同的豆浆浸泡不同的豆子*/public abstract void soakBeans();public void grinding(){System.out.println("打磨豆浆");}public void boil(){System.out.println("煮沸豆浆");}
}

  对于制作不同类型的豆浆,只需要继承模板方法类,然后根据自己的特点进行特殊处理即可:

public class RedBeanSoybeanMilk extends CreateSoyaMilk{@Overridepublic void prepare() {System.out.println("准备红豆");}@Overridepublic void soakBeans() {System.out.println("浸泡红豆");}
}

  如果黄豆豆浆不需要浸泡,则重写模板中的钩子方法

public class YellowBeanSoybeanMilk extends CreateSoyaMilk{@Overridepublic void prepare() {System.out.println("准备黄豆");}/*** 假设黄豆豆浆不需要浸泡*/@Overridepublic void soakBeans() {}@Overrideboolean isSoakBeans() {return false;}
}

  制作豆浆:

public class Client {public static void main(String[] args) {new RedBeanSoybeanMilk().make();System.out.println("**************************");new YellowBeanSoybeanMilk().make();}
}

准备红豆
浸泡红豆
打磨豆浆
煮沸豆浆


准备黄豆
打磨豆浆
煮沸豆浆

  模板方法模式也存在一定的局限性,因为是使用继承的体系结构,如果父类的修改影响较大,会导致所有子类需要同步更新。通常适用于有固定的算法结构,但子类的实现细节不同的场景。

三、代理模式

  代理模式是一种结构型设计模式,其核心目的是为了在使用目标对象的前后,对其进行拦截,增加一些统一的操作或是校验,是一种非常重要的模式,Spring AOP就是基于代理实现。
  代理模式可以分为静态代理动态代理,动态代理又可以细分为JDK动态代理Cglib动态代理。前者需要目标类实现接口,后者不需要,会在运行时动态生成目标类的子类。

3.1、静态代理

  首先演示一下什么是静态代理,静态代理要求目标类和代理类都要同时去实现接口:

public interface ITeacher {void teach();
}
public class TeacherDao implements ITeacher{@Overridepublic void teach() {System.out.println("开始上课。。。。");}
}
/*** 静态代理案例* 缺点,如果接口中要增加方法,被代理类和代理类都需要同步增加*/
public class TeacherDaoProxy implements ITeacher{private ITeacher teacher;public TeacherDaoProxy(ITeacher teacher){this.teacher = teacher;}@Overridepublic void teach() {System.out.println("课前准备。。。。");teacher.teach();System.out.println("下课。。。。。");}
}

课前准备。。。。
开始上课。。。。
下课。。。。。

3.2、JDK动态代理

  JDK动态代理的实现,主要依靠java.lang.reflect.Proxy包下的newProxyInstance方法,该方法会在运行时动态的生成代理类,详见:从源码角度分析JDK动态代理:

public interface ITeacher {void teach();
}
public class TeacherDao implements ITeacher {@Overridepublic void teach() {System.out.println("开始上课。。。。");}
}
public class JdkProxyFactory {/*** 被代理的目标类*/private Object target;public JdkProxyFactory(Object target) {this.target = target;}public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),(proxy, method, args) -> {System.out.println("课前准备");//通过反射调用目标方法Object o = method.invoke(target, args);System.out.println("下课");return o;});}
}
public class Client {public static void main(String[] args) {JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(new TeacherDao());ITeacher proxyInstance = (ITeacher) jdkProxyFactory.getProxyInstance();proxyInstance.teach();}
}

3.3、Cglib动态代理

  JDK动态代理的实现,主要依靠org.springframework.cglib.proxy.MethodInterceptor包下的intercept方法,和JDK动态代理不同的是,不要求目标类实现接口,而是直接通过字节码技术生成目标类的子类

public class TeacherDao{public void teach() {System.out.println("开始上课。。。。");}
}
/*** cglib动态代理,不需要目标类实现接口* 运行时使用字节码技术动态增强*/
public class CglibProxyFactory implements MethodInterceptor {Object target;public CglibProxyFactory(Object target) {this.target = target;}public Object getProxyInstance(){Enhancer enhancer = new Enhancer();enhancer.setCallback(this);enhancer.setSuperclass(target.getClass());return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("课前准备");Object invoke = method.invoke(target, objects);System.out.println("下课");return invoke;}
}
public class Client {public static void main(String[] args) {CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(new TeacherDao());TeacherDao proxyInstance = (TeacherDao) cglibProxyFactory.getProxyInstance();proxyInstance.teach();}
}

  但是目标类,或方法如果被privatefinal关键字修饰,以及目标方法被static修饰,则无法进行代理,因为上述修饰符的方法无法被继承。

3.4、小结

  动态代理和静态代理的区别,主要体现在:

  • 静态代理是在编译期间由开发者显式编写代理类或通过工具生成,代理类在运行时已经存在:
    在这里插入图片描述
  • 动态代理在运行时动态生成代理类,代理类的字节码不会提前编写,而是通过反射或字节码操作框架生成。(使用JDK或第三方字节码库提供的API)
    运行时,JDK 动态代理会生成类似下面的代理类:com.light.proxy.jdkproxy.$Proxy0,其不会存在于target目录下。
      在Spring AOP中,也会根据是否实现了接口,去选择不同的动态代理。

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

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

相关文章

flink实现复杂kafka数据读取

接上文&#xff1a;一文说清flink从编码到部署上线 环境说明&#xff1a;MySQL&#xff1a;5.7&#xff1b;flink&#xff1a;1.14.0&#xff1b;hadoop&#xff1a;3.0.0&#xff1b;操作系统&#xff1a;CentOS 7.6&#xff1b;JDK&#xff1a;1.8.0_401。 常见的文章中&…

越疆科技营收增速放缓:毛利率未恢复,持续亏损下销售费用偏高

《港湾商业观察》施子夫 12月13日&#xff0c;深圳市越疆科技股份有限公司&#xff08;以下简称&#xff0c;越疆科技&#xff0c;02432.HK&#xff09;发布全球发售公告&#xff0c;公司计划全球发售4000万股股份&#xff0c;其中3800万股国际发售&#xff0c;200万股香港公开…

datasets 笔记:加载数据集(基本操作)

参考了huggingface的教程 1 了解数据集基本信息&#xff08; load_dataset_builder&#xff09; 在下载数据集之前&#xff0c;通常先快速了解数据集的基本信息会很有帮助。数据集的信息存储在 DatasetInfo 中&#xff0c;可能包括数据集描述、特征和数据集大小等信息。&…

uniapp video组件无法播放视频解决方案

前言 一般正常的视频使用video组件就能播放。但视频源存在问题&#xff0c;在浏览器能正常播放 在Hbuilderx内置浏览器 在真机无法播放 使用v-html的方式 <template> <uni-popup class"videoPop" type"center" ref"videoPop">&…

Springboot 学习 之 logback-spring.xml 日志压缩 .tmp 临时文件问题

文章目录 前言功能简述1. 自定义日志文件名2. 归档规则 && 压缩2.1. 归档配置2.2. 归档压缩2.3. 日志格式 && 编码 现象原因解决办法 前言 在 Springboot 应用中&#xff0c;默认使用 logback-spring.xml 配置日志相关 功能简述 1. 自定义日志文件名 <fi…

Java程序设计2(六)

第五章&#xff1a;IO流 &#xff08;java.io包中&#xff09; 一、理解 1. 简单而言&#xff1a;流就是内存与存储设备之间传输数据的通道、管道。 2. 分类&#xff1a; (1) 按方向(以JVM虚拟机为参照物)【重点】 输入流&#xff1a;将中的内容读入到中。 输出流&#xff1a…

Java图片拼接

最近遇到一个挺离谱的功能&#xff0c;某个表单只让上传一张图&#xff0c;多图上传会使导出失败。跟开发沟通后表示&#xff0c;这个问题处理不了。我... 遂自己思考&#xff0c;能否以曲线救国的方式拯救一下&#xff0c;即不伤及代码之根本&#xff0c;又能解决燃眉之急。灵…

工程经济学(尊享版)

工程经济学是一门应用性的经济学科 也是一门介于自然科学与社会科学的之间的边缘学科。它是根据现代科学技术和社会经济发展的需要&#xff0c;在自然科学和社会科学的发展过程中相互渗透、相互促进&#xff0c;逐渐形成和发展起来的&#xff0c;是工程技术学科和经济学科交叉的…

爬虫基础学习

爬虫概念与工作原理 爬虫是什么&#xff1a;爬虫&#xff08;Web Scraping&#xff09;是自动化地访问网站并提取数据的技术。它模拟用户浏览器的行为&#xff0c;通过HTTP请求访问网页&#xff0c;解析HTML文档并提取有用信息。 爬虫的基本工作流程&#xff1a; 发送HTTP请求…

.NET重点

B/S C/S什么语言 B/S&#xff1a; 浏览器端&#xff1a;JavaScript&#xff0c;HTML&#xff0c;CSS 服务器端&#xff1a;ASP&#xff08;.NET&#xff09;PHP/JSP 优势&#xff1a;维护方便&#xff0c;易于升级和扩展 劣势&#xff1a;服务器负担沉重 C/S java/.NET/…

STM32HAL I2C函数

8.5 使用IIC协议读写EEPROM 硬件方式实现 &#xff08;HAL库&#xff09; **HAL_I2C_Mem_Write() :这种方法可以写1个或者多个字节 ** /*** brief 以阻塞模式向指定的内存地址写入数据* param hi2c 指向 I2C_HandleTypeDef 结构体的指针&#xff0c;包含指定 I2C 的配置信息…

智能工厂的设计软件 三种处理单元(NPU/GPU/CPU)及其在深度学习框架中的作用 之5(腾讯云AI代码助手 之3)

前情提要 前面讨论了智能工厂的设计软件 中三种处理单元&#xff08;NPU/GPU/CPU&#xff09;及其在深度学习框架中的作用是协作完成一个深度学习任务。 最后通过明确深度学习本身的目的是建构一个公理化系统--作为 自然语言形式化 建模约束&#xff08;为人类编辑 &#xff0…

Linux——卷

Linux——卷 介绍 最近做的项目&#xff0c;涉及到对系统的一些维护&#xff0c;有些盘没有使用&#xff0c;需要创建逻辑盘并挂载到指定目录下。有些软件需要依赖空的逻辑盘&#xff08;LVM&#xff09;。 先简单介绍一下卷的一些概念&#xff0c;有分区、物理存储介质、物…

M3D: 基于多模态大模型的新型3D医学影像分析框架,将3D医学图像分析从“看图片“提升到“理解空间“的层次,支持检索、报告生成、问答、定位和分割等8类任务

M3D: 基于多模态大模型的新型3D医学影像分析框架&#xff0c;将3D医学图像分析从“看图片“提升到“理解空间“的层次&#xff0c;支持检索、报告生成、问答、定位和分割等8类任务 论文大纲理解1. 确认目标2. 分析过程&#xff08;目标-手段分析&#xff09;核心问题拆解 3. 实…

clickhouse-副本和分片

1、副本 1.1、概述 集群是副本和分片的基础&#xff0c;它将ClickHouse的服务拓扑由单节点延伸到多个节点&#xff0c;但它并不像Hadoop生态的某些系统那样&#xff0c;要求所有节点组成一个单一的大集群。ClickHouse的集群配置非常灵活&#xff0c;用户既可以将所有节点组成…

Redis 集群实操:强大的数据“分身术”

目录 Redis Cluster集群模式 1、介绍 2、架构设计 3、集群模式实操 4、故障转移 5、常用命令 Redis Cluster集群模式 1、介绍 redis3.0版本推出的Redis Cluster 集群模式&#xff0c;每个节点都可以保存数据和整个集群状态&#xff0c;每个节点都和其他所有节点连接。Cl…

C# 从控制台应用程序入门

总目录 前言 从创建并运行第一个控制台应用程序&#xff0c;快速入门C#。 一、新建一个控制台应用程序 控制台应用程序是C# 入门时&#xff0c;学习基础语法的最佳应用程序。 打开VS2022&#xff0c;选择【创建新项目】 搜索【控制台】&#xff0c;选择控制台应用(.NET Framew…

WPF制作图片闪烁的自定义控件

1.定义自定义控件 BlinkingImage.cs: using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Animation; using System.Windows.Media.Imaging;namespace YourNamespace {public class BlinkingImage : Control{public static rea…

猫咪睡眠:萌态背后的奥秘与启示

猫咪的睡眠&#xff0c;犹如一本充满趣味与奥秘的小书&#xff0c;每一页都写满了它们独特的习性与本能。 猫咪堪称 “睡眠大师”&#xff0c;睡眠时间之长令人咋舌&#xff0c;一天中大约有 12 - 16 个小时在梦乡中度过&#xff0c;幼猫和老年猫甚至能睡更久。它们似乎深谙放…

QML Text组件

文章目录 前言主体基本文本显示字体和样式富文本支持长文本的处理文本样式与效果超链接Label 元素总结 前言 在 QML 中&#xff0c;Text 和 Label 是常用的文本显示元素&#xff0c;它们在显示文本方面具有相似性&#xff0c;但在功能和定制性上也存在一些差异。Text 元素用于…