设计模式之结构型模式(适配器、桥接、组合、享元、装饰者、外观、代理)

文章目录

    • 一、结构型设计模式
    • 二、适配器模式
    • 三、桥接模式
    • 四、组合模式
    • 五、享元模式
    • 六、装饰者模式
    • 七、外观模式
    • 八、代理设计模式

一、结构型设计模式

这篇文章我们来讲解下结构型设计模式,结构型设计模式,主要处理类或对象的组合关系,为如何设计类以形成更大的结构提供指南。

结构型设计模式包括:适配器模式(Adapter Pattern)、桥接模式(Bridge Pattern)、组合模式(Composite Pattern)、装饰器模式(Decorator Pattern)、外观模式(Facade Pattern)、享元模式(Flyweight Pattern)、代理模式(Proxy Pattern)

二、适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。它结合了两个独立接口的功能。
优点:

  1. 可以让任何两个没有关联的类一起运行。
  2. 提高了类的复用。
  3. 增加了类的透明度。
  4. 灵活性好。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,在视频播放器中,假设视频播放器只能播放MP4格式的视频,那现在又有个VLC格式的视频,就不能播放了,那要如何解决这个问题?如果我们做个转换器,将VLC格式的视频转换为MP4格式的视频不就可以播放了吗,那这个转换器我们就可以采用适配器设计模式来设计。

下面使用程序演示下上面的例子:

  1. 定义视频接口
public interface VideoInterFace {String getVideoPath();
}
  1. 定时Mp4格式视频实例
public class Mp4Video implements VideoInterFace {@Overridepublic String getVideoPath() {return "Mp4视频的路径";}
}
  1. 定义VLC格式视频实例
public class VlcVideo implements VideoInterFace{@Overridepublic String getVideoPath() {return "Vlc视频的路径";}
}
  1. 定义播放器,只接口Mp4格式的视频
public class Player {private Mp4Video video;public Player(Mp4Video video) {this.video = video;}public void play() {System.out.println(StringFormatter.concat("播放视频视频地址:", video.getVideoPath()).getValue());}
}
  1. 需要播放VLC格式的视频,定义Mp4的适配器,并接收VLC格式视频,进行转码。
public class Mp4Adapter extends Mp4Video {private VlcVideo vlcVideo;public Mp4Adapter(VlcVideo vlcVideo) {this.vlcVideo = vlcVideo;}@Overridepublic String getVideoPath() {System.out.println(StringFormatter.concat("开始格式转换,vlc地址:", vlcVideo.getVideoPath()).getValue());return "转换后的Mp4路径!";}
}
  1. 测试
public class demo {public static void main(String[] args) {Player player = new Player(new Mp4Video());player.play();VlcVideo vlcVideo = new VlcVideo();Player player1 = new Player(new Mp4Adapter(vlcVideo));player1.play();}
}

在这里插入图片描述

从上面的例子可以看出,需要播放VLC格式,就需要写一个目标适配器,这里是Mp4适配器,并继承Mp4,使之有Mp4的特性,并在内部做相应的转换即可,提高了系统的可扩展性。

三、桥接模式

桥接模式(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
优点:

  1. 抽象和实现的分离。
  2. 优秀的扩展能力。
  3. 实现细节对客户透明。

举个例子:绘画不同颜色的各种图像,画不同的形状和涂颜色,便是两个不同的功能,但两者又相互联系,在画完形状后需要涂颜色,但颜色和形状有使多种多样的,此时就可以采用桥接设计模式,将两者的抽象化与实现化解耦,形状和颜色可以独立变化

下面使用程序演示下上面的例子:

  1. 定义颜色的接口
public interface ColorApi {public void drawCircle();
}
  1. 定义不同颜色的实现,这里采用红色和绿色
public class ReqColor implements ColorApi {@Overridepublic void drawCircle() {System.out.println("开始涂红色!");}
}
public class GreenColor implements ColorApi {@Overridepublic void drawCircle() {System.out.println("开始涂绿色!");}
}
  1. 定义形状的接口
public interface ShapeApi {//画形状void draw();//画形状并涂颜色void drawShapeAndsColor();
}
  1. 定义形状的抽象模板,将共性的操作定义到抽象中
public abstract class ShapeAbstract implements ShapeApi {public ColorApi colorApi;public ShapeAbstract(ColorApi colorApi) {this.colorApi = colorApi;}@Overridepublic void drawShapeAndsColor() {draw();colorApi.drawCircle();}
}
  1. 定义圆形的实例
public class Circle extends ShapeAbstract {public Circle(ColorApi colorApi) {super(colorApi);}@Overridepublic void draw() {System.out.println("开始画圆形!");}
}
  1. 定义矩形的实例
public class Rectangle extends ShapeAbstract {public Rectangle(ColorApi colorApi) {super(colorApi);}@Overridepublic void draw() {System.out.println("开始画矩形");}
}
  1. 演示
public class demo {public static void main(String[] args) {ShapeApi shapeReq = new Circle(new ReqColor());shapeReq.drawShapeAndsColor();ShapeApi shapeGreen = new Circle(new GreenColor());shapeGreen.drawShapeAndsColor();ShapeApi rectangle = new Rectangle(new GreenColor());rectangle.drawShapeAndsColor();}
}

在这里插入图片描述

上面可以看出,可以灵活的定义形状和颜色的组合,并且他们两个都可以独立变化,添加新的形状只需,建立新的类并实现形状接口,添加颜色也是如此,极大的提高的系统的可扩展性和可维护型。

四、组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。它创建了对象组的树形结构。
优点:

  1. 高层模块调用简单。
  2. 节点自由增加。

缺点:
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

举个例子:一个公司,从上到下分为,公司、部门、小组等,他们整个在一起才能称为一个完整的公司,要表示做个公司的结构,就可以采用组合设计模式。

下面使用程序演示下上面的例子:

  1. 定义属性类,用来表示不同层级的对象
@Data
public class Property {private String name;//下一层的子集private List<Property> next;public Property(String name) {this.name = name;next = new ArrayList<Property>();}public void add(Property e) {next.add(e);}public void remove(Property e) {next.remove(e);}public List<Property> getSubordinates(){return next;}
}
  1. 使用演示
public class demo {public static void main(String[] args) {Property company = new Property("公司");Property department = new Property("部门");Property group = new Property("小组");company.add(department);department.add(group);System.out.println(company);}
}

在这里插入图片描述

上面就演示了一个公司的组合,通过company对象就可以获得整个公司的各个部分的对象。组合设计模式主要适合于整体部分的场景。

五、享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。他的优点是大大减少对象的创建,降低系统的内存,使效率提高,但也有可能造成内存的浪费。比如Spring的采用容器的方式存储bean,使用时从容器中获取。

还是拿画不同颜色形状的例子演示下享元设计模式的使用:

  1. 定义形状的接口
public interface Shape {void draw();
}
  1. 定义圆形的实现,并接收一个颜色值:
public class Circle implements Shape {private String color;public Circle(String color) {this.color = color;}@Overridepublic void draw() {System.out.println(StringFormatter.concat("开始画 ", color, " 色的圆 ").getValue());}
}
  1. 定义形状对象获取工厂,根据颜色值将对象存储到HashMap中,后再根据颜色值取对象,达到复用的效果。
public class ShapeFactory {private static final HashMap<String, Shape> circleMap = new HashMap<>();public static Shape getCircle(String color) {if (!circleMap.containsKey(color)) {System.out.println(StringFormatter.concat(">> 创建", color, "颜色的圆 ").getValue());circleMap.put(color, new Circle(color));}return circleMap.get(color);}
}
  1. 使用
public class demo {private static final String colors[] ={"Red", "Green", "Blue", "White", "Black"};public static void main(String[] args) {for (int i = 0; i < 20; ++i) {Shape circle = ShapeFactory.getCircle(getRandomColor());circle.draw();}}private static String getRandomColor() {return colors[(int) (Math.random() * colors.length)];}
}

在这里插入图片描述

上面可以看出,享元设计模式可以大大减少对象的创建,但也有可以造成内存的浪费,比如某个对象的使用频率非常低,如果一直存在内存中就有点浪费空间了。

六、装饰者模式

装饰者模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。它是作为现有的类的一个包装
装饰类和被装饰类可以独立发展,不会相互耦合,装饰者模式是继承的一个替代模式,装饰者模式可以动态扩展一个实现类的功能。

举个例子:还是绘画不同的形状的例子,加入系统中有画各种形状的功能,但随着功能后期的演化,需要画出带有边框的各种形状,那么此时就可以采用装饰者设计模式来做增强。

下面使用程序演示下上面的例子:

  1. 定义形状接口
public interface Shape {void draw();
}
  1. 定义圆形的实现
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("开始画圆形!");}
}
  1. 定义矩形的实现
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("开始画矩形!");}
}
  1. 定义装饰器的抽象模板
public abstract class ShapeDecorator implements Shape {protected Shape decoratedShape;public ShapeDecorator(Shape decoratedShape){this.decoratedShape = decoratedShape;}@Overridepublic void draw(){decoratedShape.draw();}  
}
  1. 定义具体的边框装饰器
public class BorderShapeDecorator extends ShapeDecorator {public BorderShapeDecorator(Shape decoratedShape) {super(decoratedShape);     }@Overridepublic void draw() {decoratedShape.draw();         setRedBorder(decoratedShape);}private void setRedBorder(Shape decoratedShape){System.out.println("画边框!");}
}
  1. 演示
public class demo {public static void main(String[] args) {Shape circle = new Circle();circle.draw();Shape shape = new BorderShapeDecorator(new Circle());shape.draw();Shape shape1 = new BorderShapeDecorator(new Rectangle());shape1.draw();}
}

在这里插入图片描述

上面可以看出再不改变原先类的基础上,做了画边框的效果,对原有做增强,使用装饰者设计模式,可以大大提高系统的可扩展性。

七、外观模式

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
它的优点是可以减少系统相互依赖、提高灵活性、提高了安全性。但是它不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

举个例子:画各种图形的例子,比如要画圆形、矩形、三角形,每画一种图像都要拿到对应的抽象,并调用绘制方法,如果要画的形状过多,这么多的抽象就不好管理了,而如果使用外观设计模式,提供一个统一的抽象,在这个抽象中就可以完成上面不同的绘制,这样就方便了我们的管理。

下面使用程序演示下上面的例子:

  1. 定义形状的接口
public interface Shape {void draw();
}
  1. 定义圆形的实例
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("开始画圆!");}
}
  1. 定义矩形的实例
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("开始画矩形!");}
}
  1. 定义三角形的实例
public class Triangle implements Shape {@Overridepublic void draw() {System.out.println("开始画三角形!");}
}
  1. 定义一个外观类,并调用上面的功能
public class ShapeFacade {private Shape circle;private Shape rectangle;private Shape square;public ShapeFacade() {circle = new Circle();rectangle = new Rectangle();square = new Triangle();}public void drawCircle(){circle.draw();}public void drawRectangle(){rectangle.draw();}public void drawSquare(){square.draw();}
}
  1. 演示
public class demo {public static void main(String[] args) {ShapeFacade shapeFacade = new ShapeFacade();shapeFacade.drawCircle();shapeFacade.drawRectangle();shapeFacade.drawSquare();}
}

在这里插入图片描述
外观设计模式还是比较容易理解的,就是把多个功能统一整个到一个对象中,由这个对象再去调用具体的类和方法。

八、代理设计模式

代理设计模式通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。既(AOP微实现) 。

代理有分静态代理动态代理

  • 静态代理:在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
  • 动态代理:是在使用时,动态的生成代理对象,他是在内存中构建代理对象的。

举个例子,在做数据库操作时,一般我们都会在事物中做SQL的操作,那就需要在操作前开启事物,操作后如果成功就需要提交事物,如果代用代理设计模式,就可以将事物开启提交逻辑放在代理类中,被代理的类,只需要关注业务逻辑即可。

下面以支付和事物为例演示下代理模式

采用静态代理,实现上面例子:

  1. 定义支付接口
public interface PayInterFace {void pay();
}
  1. 定义微信支付实现
public class WxPay implements PayInterFace {@Overridepublic void pay() {System.out.println("支付中...");}
}
  1. 定义支付的代理类
public class PayProxy implements PayInterFace {private WxPay pay;public PayProxy(WxPay pay) {this.pay = pay;}@Overridepublic void pay() {System.out.println("事物开始!");pay.pay();System.out.println("提交事物!");}
}
  1. 演示
public class demo {public static void main(String[] args) {PayInterFace pay = new PayProxy(new WxPay());pay.pay();}
}

在这里插入图片描述

上面的静态代理,可以看出,我们需要对每个被代理对象设计一个代理类,如果代理的功能非常多,那就需要开发人员写特别多的代理类,下面可以看下动态代理的使用。

采用动态代理,实现上面例子:
这里使用JDK自带的动态代理来实现

  1. 再定义一个支付宝的支付实现
public class ZfbPay implements PayInterFace {@Overridepublic void pay() {System.out.println("支付宝支付中...");}
}
  1. 定义代理对象,采用jdk的 InvocationHandler 接口
public class PayProxy implements InvocationHandler {private Object object;public PayProxy(Object object) {this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("事物开始!");Object result = method.invoke(object, args);System.out.println("提交事物!");return result;}
}
  1. 演示
public class demo {public static void main(String[] args) {PayInterFace pay = (PayInterFace) Proxy.newProxyInstance(PayInterFace.class.getClassLoader(),new Class[]{PayInterFace.class},new PayProxy(new WxPay()));pay.pay();PayInterFace pay1 = (PayInterFace) Proxy.newProxyInstance(PayInterFace.class.getClassLoader(),new Class[]{PayInterFace.class},new PayProxy(new ZfbPay()));pay1.pay();}
}

在这里插入图片描述

上面使用一个代理类,代理了多个对象,相对于静态代理,是代码更简介,灵活性也更高。

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

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

相关文章

【已解决】ModuleNotFoundError: No module named ‘IPython‘

问题描述 Traceback (most recent call last): File "<stdin>", line 1, in <module> ModuleNotFoundError: No module named IPython 解决方法 pip install ipython 完结撒花 我并不想穿过荆棘去到黎明&#xff0c;我猜也没有人会想

proftpd安全加固:禁用匿名登录

其实&#xff0c;proftpd默认是禁止匿名登录的。今天我们反其道&#xff0c;研究一下如何开启匿名登录。清楚了怎么破防&#xff0c;才能更好得防。 查看/etc/proftpd.conf 配置文件&#xff1a; # A basic anonymous configuration, with an upload directory # Enable this…

计算机设备管理器如何看内存,怎么查看电脑配置信息?3种方法,让你掌握电脑全部信息!...

转载&#xff1a;https://blog.csdn.net/weixin_35849957/article/details/118512756?spm1001.2014.3001.5502 原标题&#xff1a;怎么查看电脑配置信息&#xff1f;3种方法&#xff0c;让你掌握电脑全部信息&#xff01; 电脑的配置决定了电脑性能高低以及运行速度。而电脑…

Emacs之Plantuml用于复杂UML类图(Markdown用于简单类图)(一百三十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

python主流开发工具排名,python开发工具有哪些

本篇文章给大家谈谈python的开发工具软件有哪些&#xff0c;以及python主流开发工具排名&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 python中用到哪些软件 一、Python代码编辑器1、sublime Textsublime Text是一款非常流行的代码编辑器&#xff0c;支持P…

STM32L051使用HAL库操作实例(13)- 读取IAQ-CORE-C传感器实例

目录 一、前言 二、传感器参数 三、STM32CubeMX配置&#xff08;本文使用的STM32CubeMX版本为6.1.2&#xff09;例程使用模拟I2C进行数据读取 1.MCU选型 2.使能时钟 3.时钟配置 4.GPIO口配置 四、配置STM32CubeMX生成工程文件 五、点击GENERATE CODE生成工程文件 六、…

Kubersphere应用【二】Docker安装

一、Docker安装 1.下载Docker安装包 【地址】Index of linux/static/stable/x86_64/ 2.上传至服务器 # 解压文件 tar -xvf docker-20.10.10.tgz# 将docker 目录中的所有文件复制至/usr/bin/目录下 cp docker/* /usr/bin 3.配置docker.service文件 vim /usr/lib/systemd/sy…

arm平台编译so文件回顾

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、几个点二、回顾过程 1.上来就执行Makefile2.编译第三方开源库.a文件 2.1 build.sh脚本2.2 Makefile3.最终编译三、其它知识点总结 前言 提示&#xff1a;这…

MSSQL存储过程的功能和用法(简明扼要)

官方文档 存储过程&#xff08;数据库引擎&#xff09; - SQL Server | Microsoft Learn Transact-SQL 参考&#xff08;数据库引擎&#xff09; - SQL Server | Microsoft Learn 定义 存储过程可以用编程语言的方法来类比&#xff0c;有输入输出。区别是其使用SQL表达业务…

spring 单元测试 Junit

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

解决打印机超出字节之使用PHP拆分字符串并保留分隔符

在开发过程中&#xff0c;我们经常需要将一个长字符串拆分成多个片段&#xff0c;并且保留原始字符串中的分隔符。在本篇教程中&#xff0c;我们将使用PHP编写一个函数来实现这个功能。 首先&#xff0c;让我们来看一下我们要解决的问题。假设我们有一个长字符串&#xff0c;我…

pywinrm 解析输出为 dict 格式 字典格式

s winrm.Session(host, auth(username, password))task_folder \RPA\\ # 运行 schtasks 命令来列出任务计划程序 command schtasks /query /fo csv # command fschtasks /query /fo csv /tn {task_folder} # command fschtasks /query /fo csv /tn "{task_folder}\\…

mapbox使用v3版本,v2的样式切换不同时间段

创建DayAndNight.js /*** 使用方式* const dayNight new DayAndNight({ map: map // map 地图对象}) * 修改类型* dayNight.setConfigProperty(value)*/ class DayAndNight {constructor (sdMap) {this.map sdMap.mapthis.initStyle()}// 初始化时添加必要样式initStyle () {…

删除计算机用户时拒绝访问权限,c盘为什么拒绝访问 删除c盘文件需要管理员权限怎么办...

转载&#xff1a;​​​​​​删除计算机用户时拒绝访问权限,c盘为什么拒绝访问 删除c盘文件需要管理员权限怎么办...-CSDN博客 c盘是电脑中的关键位置&#xff0c;存储着很多系统重要文件&#xff0c;如果电脑出问题一般就是c盘中的文件异常&#xff0c;近日有小伙伴出现这样…

前端知识笔记(四十一)———nodejs发起http或https请求

http请求 const express require(express) const http require(http)const app express()const loginConfig (token) > {return {hostname: api.test.com,port: 80,path: /test?access_token${token},method: GET} }app.get(/login, (req, res) > {const options …

c++通过serial库进行上下位机通信

​编辑 风紊 现役大学牲&#xff0c;半退休robomaster视觉队员 写在前面 本文章主要介绍的是如何通过开源的serial库和虚拟串口实现上位机和下位机通信。 需求 假设下位机有这样一个数据报发送给上位机 struct DataRecv {char start s;TeamColor color TeamColor::Blu…

Premiere Pro 2024 新功能有哪些?视频剪辑软件PR2024更新内容及问题修复

PR软件“基于文本的编辑”中的填充词检测与批量删除功能 “基于文本的编辑”可让您检测“呃”和“嗯”填充词并批量删除它们&#xff0c;从而使您的转录文本更加准确。就像处理停顿一样&#xff0c;您可以单击填充词并将其从序列转录文本中删除。填充词与语言无关&#xff0c;…

STM32CubeIDE(CUBE-MX hal库)----RTC时钟,时钟实时显示

系列文章目录 STM32CubeIDE(CUBE-MX hal库)----初尝点亮小灯 STM32CubeIDE(CUBE-MX hal库)----按键控制 STM32CubeIDE(CUBE-MX hal库)----串口通信 STM32CubeIDE(CUBE-MX hal库)----定时器 STM32CubeIDE(CUBE-MX hal库)----蓝牙模块HC-05&#xff08;详细配置&#xff09; 前言…

【无标题】安装环境

这里写目录标题 清华镜像加速 安装cuda11.3 PyTorch 1.10.1https://pytorch.org/get-started/previous-versions/[如果没有可以点Previous pyTorch Versions&#xff0c;这里面有更多的更早的版本](https://pytorch.org/get-started/locally/) 复制非空文件夹cp: -r not specif…

传染病传播速度

题干 R0值是基本传染数的简称&#xff0c;指的是在没有采取任何干预措施的情况下&#xff0c;平均每位感染者在传染期内使易感者个体致病的数量。数字越大说明传播能力越强&#xff0c;控制难度越大。一个人传染的人的数量可以用幂运算来计算。假设奥密克戎的R0为10&#xff0…