设计模式06-结构型模式1(适配器/桥接/组合模式/Java)
#1024程序员节|征文#
4.1 适配器模式
- 结构型模式(Structural Pattern)的主要目的就是将不同的类和对象组合在一起,形成更大或者更复杂的结构体。
- 结构性模式的分类:
类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关。
对象结构型模式关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象,然后通过该对象调用相应的方法。根据合成复用原则,在系统中尽量使用关联关系替代继承关系,因此大部分结构型模式都是对象结构型模式。
4.1.1 适配器模式的定义
1.动机:适配器可以使由于接口不兼容而不能交互的类可以一起工作,这就是适配器模式的模式动机。
2.定义:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器
4.1.2 适配器模式的分析与实现
在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adaptor),它所包装的对象就是适配者(Adaptee),即被适配的类。适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说,当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。
类适配器:
public interface Target {public void request();
}
public class Adaptee {public void specialRequest() {System.out.println("特殊的请求方式");}
}
public class Adapter extends Adaptee implements Target{@Overridepublic void request() {super.specialRequest();}
}
对象适配器:
public interface Target {public void request();
}
public class Adaptee {public void specialRequest() {System.out.println("特殊的请求方式");}
}
public class Adapter implements Target {private Adaptee adaptee;public void setAdaptee(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specialRequest();}
}
-
类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用。
-
适配者类无法继承时,只能用对象适配器。
4.1.3 适配器模式的案例
现需要设计一个可以模拟各种动物行为的机器人,在机器人中定义了一系列方法,如机器人叫喊方法cry()、机器人移动方法move()等。如果希望在不修改已有代码的基础上使得机器人能够像狗一样叫,像狗一样跑,使用适配器模式进行系统设计。
public interface Robot {public void cry();public void move();
}
public class Dog {public void cry() {System.out.println("汪汪叫");}public void move() {System.out.println("快快跑");}
}
public class DogAdapter extends Dog implements Robot{public void cry(){super.cry();}public void move(){super.move();}
}
4.1.4 适配器模式的优缺点
优点 | 缺点 |
---|---|
1.将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构 | |
2.增加了类的透明性和复用性,提高了适配者的复用性 | |
3.灵活性和扩展性非常好,更换(增加)适配器,符合开闭原则 | |
4.类适配器模式:由于继承关系,置换一些适配者的方法很方便 | 不能适配多个适配类,每个目标类必须为接口,对于适配类为finial修饰的不可使用 |
5.对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类 | 在适配器中置换适配者类的某些方法比较麻烦 |
4.1.5 适配器模式的适用场景
-
系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码
-
创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作
4.1.6 双向适配器类
public class Adapter implements Target,Adaptee {private Target target;private Adaptee adaptee;public Adapter(Target target) {this.target = target; } public Adapter(Adaptee adaptee) {this.adaptee = adaptee; } public void request() {adaptee.specificRequest(); } public void specificRequest() {target.request(); }
}
4.2 桥接模式
4.2.1 桥接模式的定义
1.动机:如果系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使得两者可以独立扩展。桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多重继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效地控制了系统中类的个数。
2.定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
4.2.2 桥接模式的分析与实现
-
桥接模式中体现开闭原则、合成复用原则、里氏代换原则、依赖倒转原则等。
-
脱耦:桥接模式将抽象化和实现化之间的耦合解开,或者说是将强关联(继承)改换成弱关联,将两个角色之间的继承关系改为关联关系,使得两者可以独立变化。
public interface Implementor {public void implOperation(); } public class ConcreteImplementorA implements Implementor{@Overridepublic void implOperation() {System.out.println("一个实体的维度1的特征A");} } public class ConcreteImplementorB implements Implementor{@Overridepublic void implOperation() {System.out.println("一个实体的维度1的特征B");} }public abstract class Abstracter {protected Implementor implementor;public void setImplementor(Implementor implementor) {this.implementor = implementor;}public abstract void operation(); } public class RefinedAbstracter extends Abstracter{@Overridepublic void operation() {System.out.println("一个实体维度2的特征");implementor.implOperation();} }
4.2.3 桥接模式的案例
public interface VideoFile {public void decode(String osType, String fileName);
}
public class MPEGFile implements VideoFile{@Overridepublic void decode(String osType, String fileName) {System.out.println("位于" + osType + "操作系统的" + fileName + ".MPEG文件进行播放");}
}
public class WMVFile implements VideoFile{@Overridepublic void decode(String osType, String fileName) {System.out.println("位于" + osType + "操作系统的" + fileName + ".WMV文件进行播放");}
}
public abstract class OSVersion {protected VideoFile videoFile;public void setVideoFile(VideoFile videoFile) {this.videoFile = videoFile;}public abstract void play(String fileName);
}
public class LinuxVersion extends OSVersion{@Overridepublic void play(String fileName) {videoFile.decode("Linux", fileName);}
}
public class WindowsVersion extends OSVersion{@Overridepublic void play(String fileName) {videoFile.decode("Windows", fileName);}
}
public class Main {public static void main(String[] args) {WMVFile wmvFile = new WMVFile();WindowsVersion windowsVersion = new WindowsVersion();windowsVersion.setVideoFile(wmvFile);windowsVersion.play("刺客五六七");MPEGFile mpegFile = new MPEGFile();LinuxVersion linuxVersion = new LinuxVersion();linuxVersion.setVideoFile(mpegFile);linuxVersion.play("喜羊羊与灰太狼");}
}
4.2.4 桥接模式的优缺点
优点 | 缺点 |
---|---|
1.分离抽象接口和实现部分 | 1.不容易正确识别俩个变化的维度 |
2.取代多继承,减少子类个数 | 2.关联关系在抽象层,设计难度大 |
3.扩展性,俩个维度均可扩展 |
4.2.5 桥接模式的适用场景
-
需要在抽象化和具体化之间增加更多的灵活性,扩展性,避免在两个层次之间建立静态的继承关系
-
一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立地进行扩展
-
不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统
4.3 组合模式
4.3.1 组合模式的定义
1.动机:描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象。
2.定义:组合多个对象形成树形结构以表示“部分-整体”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。
4.3.2 组合模式的分析与实现

- 组合模式用面向对象的方式来处理树形结构,它为叶子构件和容器构件提供了一个公共的抽象构件类,客户端可以针对该抽象类进行处理,而无需关心所操作的是哪种类型的对象。
- 使用聚合关系使得容器既可以添加叶子节点也可以添加容器
public abstract class Component {public abstract void add(Component component);public abstract void remove(Component component);public abstract Component getChild(int i);public abstract void method();
}public class Composite extends Component{private ArrayList<Component> list = new ArrayList<>();@Overridepublic void add(Component component) {list.add(component);}@Overridepublic void remove(Component component) {list.remove(component);}@Overridepublic Component getChild(int i) {return list.get(i);}@Overridepublic void method() {for (Component component : list) {component.method();}}
}public class Leaf extends Component{@Overridepublic void add(Component component) {throw new RuntimeException("叶子节点无法添加元素");}@Overridepublic void remove(Component component) {throw new RuntimeException("叶子节点无法删除元素");}@Overridepublic Component getChild(int i) {throw new RuntimeException("叶子节点无法获取元素");}@Overridepublic void method() {}
}
4.3.3 组合模式的案例
在操作系统中,一个文件夹中可能存放着图像文件,视频文件,文本文件,也可能存放其他的文件夹,而对不同类型的文件进行的浏览操作也不一样。

public abstract class AbstractFile {protected String fileName;public abstract void display();public abstract void add(AbstractFile file);public abstract void remove(AbstractFile file);
}
public class ImageFile extends AbstractFile{public ImageFile(String fileName) {this.fileName = fileName;}@Overridepublic void display() {System.out.println("正在打开" + this.fileName + ".png");}@Overridepublic void add(AbstractFile file) {throw new RuntimeException("无法添加文件");}@Overridepublic void remove(AbstractFile file) {throw new RuntimeException("无法删除文件");}
}
public class TextFile extends AbstractFile{public TextFile(String fileName) {this.fileName = fileName;}@Overridepublic void display() {System.out.println("正在打开" + this.fileName + ".txt");}@Overridepublic void add(AbstractFile file) {throw new RuntimeException("无法添加文件");}@Overridepublic void remove(AbstractFile file) {throw new RuntimeException("无法删除文件");}
}
public class Folder extends AbstractFile{private ArrayList<AbstractFile> files;public Folder(String fileName) {files = new ArrayList<>();this.fileName = fileName;}@Overridepublic void display() {System.out.println("打开" + this.fileName + "文件夹");for (AbstractFile file : files) {file.display();}}@Overridepublic void add(AbstractFile file) {this.files.add(file);}@Overridepublic void remove(AbstractFile file) {this.files.remove(file);}
}
4.3.4 组合模式的优缺点
优点 | 缺点 |
---|---|
1.可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次 | 1.在增加新构件时很难对容器中的构件类型进行限制 |
2.客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构 | 2.不是所有方法在叶子节点中都可以使用 |
3.增加新的容器构件和叶子构件都很方便,符合开闭原则 |
4.3.5 组合模式的适用场景
-
在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们
-
在一个使用面向对象语言开发的系统中需要处理一个树形结构
-
在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型
4.3.6 组合模式的类型
- 透明组合模式
定义:抽象构件中声明了所有用于管理成员对象的方法,包括add()、remove(),以及getChild()等方法。
优点:确保所有的构件类都有相同的接口,客户端一致对待所有对象。
缺点:不够安全,因为叶子对象和容器对象在本质上是有区别的。
- 安全组合模式

定义:抽象构件Component中没有声明任何用于管理成员对象的方法,在Composite类中声明与实现这些方法
优点:优点是安全,对于叶子对象,客户端不可能调用到这些方法
缺点:不够透明,客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件
相关文章:

设计模式06-结构型模式1(适配器/桥接/组合模式/Java)
#1024程序员节|征文# 4.1 适配器模式 结构型模式(Structural Pattern)的主要目的就是将不同的类和对象组合在一起,形成更大或者更复杂的结构体。结构性模式的分类: 类结构型模式关心类的组合,由多个类…...

【损害和风险评估&坑洼】路面坑洼检测系统源码&数据集全套:改进yolo11-DCNV3
改进yolo11-DLKA等200全套创新点大全:路面坑洼检测系统源码&数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.24 注意:由于项目一直在更新迭代,上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视频可…...

GenAI 生态系统现状:不止大语言模型和向量数据库
自 20 个月前 ChatGPT 革命性的推出以来,生成式人工智能(GenAI)领域经历了显著的发展和创新。最初,大语言模型(LLMs)和向量数据库吸引了最多的关注。然而,GenAI 生态系统远不止这两个部分&#…...
gitlab 配置ssh keys
settings -- 终端配置: git config --global user.email "yxthotmail.cm" 配置gitlab 账号邮箱 git config --global user.name "xt.yao" 配置gitlab账号用户名 生成SSH key,输入命令ssh-keygen -t rsa,一直按回车…...

小程序开发实战:PDF转换为图片工具开发
目录 一、开发思路 1.1 申请微信小程序 1.2 编写后端接口 1.3 后端接口部署 1.4 微信小程序前端页面开发 1.5 运行效果 1.6 小程序部署上线 今天给大家分享小程序开发系列,PDF转换为图片工具的开发实战,感兴趣的朋友可以一起来学习一下!…...

我有两台120kw充电桩一天能赚多少钱
(当前是理想状态下,当然还要看场地费用,还有物业,变压器,等等) ———————————————————— ———————————————————— 要计算两台120kW充电桩能赚多少钱,我们…...

深入了解 Android 中的命名空间:`xmlns:tools` 和其他常见命名空间
在 Android 开发中,xmlns (.xml的namespace)命名空间是一个非常重要的概念。通过引入不同的命名空间,可以使用不同的属性来设计布局、设置工具属性或者支持自定义视图等。除了 xmlns:tools 以外,还有很多常见的命名空间…...

stable-zero123模型构建指南
一、介绍 stabilityai出品,能够对有简单背景的物体进行三维视角图片的生成,简单来说也就是通过调整变换观察的视角生成对应视角的图片。 本项目通过comfyui实现。 二、容器构建说明 1. 部署ComfyUI (1)使用命令克隆ComfyUI g…...
算法题解记录32+++最长连续序列(百题筑基)
你们好,我是蚊子码农,好久不见。由于秋招求职的繁琐事情,我有很长一段时间没更新博客,希望我的粉丝们能够谅解。 秋招我拿到了一些offer,最终决定去一个主要做“网络安全”业务的公司工作,也许明天会更好&a…...

全球知名度最高的华人起名大师颜廷利:世界顶级思想哲学教育家
全国给孩子起名最好的大师颜廷利教授在其最新的哲学探索中,提出了《升命学说》这一前沿理论观点,该理论不仅深刻地回应了古今中外众多哲学流派和思想体系的精髓,还巧妙地融合了实用主义、理想主义以及经验主义的核心理念。通过这一独特的视角…...
Flink Rest API
REST API | Apache Flink Flink官网API 通过curl 或者Rest API工具测试web UI对应的接口返回信息 Flink 提交yarn任务 ./bin/flink run -t yarn-per-job historyServer ../bin/historyserver.sh start...

Zig 语言通用代码生成器:逻辑,冒烟测试版发布二
Zig 语言通用代码生成器:逻辑,冒烟测试版发布二 Zig 语言是一种新的系统编程语言,其生态位类同与 C,是前一段时间大热的 rust 语言的竞品。它某种意义上的确非常像 rust,尤其是在开发过程中无穷无尽抛错的过程&#x…...

mysql 通过GROUP BY 聚合并且拼接去重另个字段
我的需求: 我想知道同一个手机号出现几次,并且手机号出现在哪些地方。下面是要的效果。 源数据: CREATE TABLE bank (id bigint(20) unsigned NOT NULL AUTO_INCREMENT,user_id int(11) NOT NULL DEFAULT 0,tel varchar(255) COLLATE utf8mb4_unicode_…...

Java应用程序的测试覆盖率之设计与实现(一)-- 总体设计
一、背景 作为测试,如何保证开发人员提交上来的代码都被测试覆盖到,是衡量测试质量的一个重要指标。 本系列文章将要说一说,如何搭建一套测试覆盖率的系统。 包括以下内容: jacoco agent采集执行覆盖率数据jacoco climaven集成jacoco:jacoco-maven-pluginant集成jacoco:…...

Unity C#脚本的热更新
以下内容是根据Unity 2020.1.0f1版本进行编写的 目前游戏开发厂商主流还是使用lua框架来进行热更,如xlua,tolua等,也有的小游戏是直接整包更新,这种小游戏的包体很小,代码是用C#写的;还有的游戏就是通过…...

监督学习之逻辑回归
逻辑回归(Logistic Regression) 逻辑回归是一种用于二分类(binary classification)问题的统计模型。尽管其名称中有“回归”二字,但逻辑回归实际上用于分类任务。它的核心思想是通过将线性回归的输出映射到一个概率值…...
深度优先算法(DFS)洛谷P1683-入门
虽然洛谷是有题解的,但是你如果直接看得懂题解,你也不会来看这篇文章.. 这些代码均是我记录自身成长的记录,有写的不好的地方请谅解! 先上代码: #include <iostream> #include <vector> #include<iomanip> #include<cstdio&…...

Python数据分析基础
本文介绍了Python在数据分析中的应用,包括数据读取、清洗、处理和分析的基本操作。通过使用Pandas和Numpy库,我们可以高效地处理大量数据,并利用Matplotlib和Seaborn库进行数据可视化。 1. 引言 Python因其简洁的语法和强大的库支持&#x…...

《企业自设2-软件测试》线下课day3: 006扩展虚拟机
1.win11 修改hosts无权限 分别再cmd终端输入以下两行代码: C:\Windows\System32\drivers\etcnotepad hosts 2.先保存快照!!! 3.关闭虚拟机,将内存,CPU进行修改 就是再这个位置修改: 4.运…...

配置和排查 Lombok 在 IDEA 中使用的详细步骤
在日常开发中,Java 代码常常需要大量的样板代码,比如 getter、setter、toString 等方法。Lombok 是一个 Java 库,可以通过注解的方式,自动生成这些常见的代码,从而让代码更加简洁、清晰。比如,我们可以通过…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
字符串哈希+KMP
P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...