【设计模式】结构型设计模式

news2024/12/26 19:14:43

结构型设计模式

文章目录

  • 结构型设计模式
    • 一、概述
    • 二、适配器模式(Adapter Pattern)
      • 2.1 类适配器模式
      • 2.2 对象适配器模式
      • 2.3 接口适配器模式
      • 2.4 小结
    • 三、桥接模式(Bridge Pattern)
    • 四、装饰器模式(Decorator Pattern)
    • 五、组合模式(Composite Pattern)
    • 六、外观模式(Facade Pattern)
    • 七、享元模式(Flyweight Pattern)
    • 八、代理模式(Proxy Pattern)
    • 九、依赖注入模式 *(Dependency Injection)
    • 十、流接口模式 *(Fluent Interface)

一、概述

这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge Pattern)
  • 装饰器模式(Decorator Pattern)
  • 组合模式(Composite Pattern)
  • 外观模式(Facade Pattern)
  • 享元模式(Flyweight Pattern)
  • 代理模式(Proxy Pattern)
  • 依赖注入模式 *(Dependency Injection)
  • 流接口模式 *(Fluent Interface)

二、适配器模式(Adapter Pattern)

  1. 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
  2. 主要分为三类:类适配器模式对象适配器模式接口适配器模式

2.1 类适配器模式

Adapter类,通过继承 src类,实现 dst 类接口,完成src->dst的适配。

以手机充电器为例子:

在这里插入图片描述

充电器本身相当于Adapter,220V交流电相当于src (即被适配者),dst(即目标)是5V直流电

2.2 对象适配器模式

  1. 基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。 即:持有 src类,实现 dst 类接口,完成src->dst的适配
  2. 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系
  3. 对象适配器模式是适配器模式常用的一种
public class VoltageAdapter2 implements Voltage5 {
    private Voltage220 voltage220; //持有Voltage220对象,不是继承了
}

2.3 接口适配器模式

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。在使用Java做页面监听时经常会碰到对应适配类。

2.4 小结

  1. 三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。
    1. 类适配器:以类给到,在Adapter里,就是将src当做类,继承
    2. 对象适配器:以对象给到,在Adapter里,将src作为一个对象,持有
    3. 接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现
  2. Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
  3. 实际开发中,实现起来不拘泥于这三种经典形式

三、桥接模式(Bridge Pattern)

  1. 桥接模式(Bridge 模式)是指:是将抽象部分与它的具体实现部分分离,使它们都可以独立地变化。
  2. Bridge 模式基于类的最小设计原则,通过组合/聚合的方式建立两个类之间的联系,而不是继承。但又类似于多重继承方案,但是多重继承方案往往违背了类的单一职责原则,其复用性较差,桥接模式是比多重继承更好的替代方案。桥接模式的核心在于解耦抽象和实现。

在这里插入图片描述

  1. Client 类:桥接模式的调用者
  2. 抽象类(Abstraction) :维护了 Implementor / 即它的实现类 ConcreteImplementorA…, 二者是聚合关系, Abstraction充当桥接类
  3. RefinedAbstraction : 是 Abstraction 抽象类的子类
  4. Implementor : 行为实现类的接口
  5. ConcreteImplementorA /B :行为的具体实现类
  6. 从 UML 图:这里的抽象类和接口是聚合的关系,其实是调用和被调用关系

上面概念比较抽象,这里以手机为例:

  1. 首先有需求,手机可以按品牌分:HuaWei、Vivo、XiaoMi,不同品牌对应着不同的应用商城
  2. 业务需求变更,对于手机型号有了区分,分为A、B、C型,不按桥接模式设计,则需要多重继承实现功能全排序,不同品牌对应不同型号共9个实现
  3. 按桥接模式设计,新建一个桥接抽象类为Phone,其实现类分别为A、B、C型,有不同的功能,通过聚合关系,将品牌实现接口聚合到Phone抽象类中,这样只需新增3个实现类;而手机的型号以及品牌又可以区分开来,分别运用其不同特别功能

常见应用场景

  1. JDBC 驱动程序
  2. 银行转账系统
    • 转账分类: 网上转账,柜台转账,AMT 转账
    • 转账用户类型:普通用户,银卡用户,金卡用户
  3. 消息管理
    • 消息类型:即时消息,延时消息
    • 消息分类:手机短信,邮件消息,QQ 消息…

四、装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。

典型的应用就是jdk中IO流的应用,FilterInputStream 就是一个装饰者,BufferInputStream是具体的实现类,通过组合的关系,使得输入流有了缓冲的功能,而又无需修改原有inputStream代码。

在这里插入图片描述

五、组合模式(Composite Pattern)

组合模式就运用了树形结构,该模式的核心思想是:将多个对象组合成树形结构,以此结构来表示“整体-部分”之间的层次关系。

在这里插入图片描述

  1. 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性
  2. 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一 个树形结构
  3. 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位

应用场景:

  1. 组合-部分整体场景:将多个对象组合成一个整体,并且整体与部分是一致对待的。比如树形菜单、文件夹和文件等
  2. 递归结构场景:处理递归的数据结构。比如文件和目录的关系就是递归的
  3. 规则场景:当需要处理的对象具有明显的层次结构时,可以考虑使用组合模式。比如,企业中不同职位的员工,每个职位的员工都有一些共同的属性(比如姓名、公司邮件地址),但也有一些不同的属性(比如职位、薪水等)
  4. 任务分解场景:将一个大任务分解为多个小任务,然后再将小任务组合起来,形成一个任务树。这种情况下,可使用组合模式对任务进行建模
  5. GUI控件场景:GUI控件通常可以嵌套在其他控件中,并且用户可以在控件中插入其他控件,组合模式适用于GUI控件的场景

组合模式适用于处理树形结构的场景,将一个对象的部分和整体看作一样,形成一个树形结构。在需要统一处理整个树形结构时,可以考虑使用组合模式。

六、外观模式(Facade Pattern)

外观模式(Facade),也叫过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口。

外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。这个模式我们平时就会使用,比如调用交易接口,交易接口中会再去调用其他各个接口。或者MVC模式下我们写的Controller,即对外暴露接口,前端开发无需关心后端干了什么。

在这里插入图片描述

  1. 通过合理的使用外观模式,可以帮我们更好的划分访问的层次
  2. 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个 Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
  3. 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的

七、享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象。

享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

在这里插入图片描述

  1. FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
  2. ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
  3. UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂
  4. FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从池中获取对象方法,这个一般被设计为单例模式

内部状态是不会变化的,可以被多个对象共享,而外部状态会随着对象的使用而改变。比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。

八、代理模式(Proxy Pattern)

代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

在这里插入图片描述

代理模式有不同的形式, 主要有三种 静态代理动态代理 (JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴)。最经典的运用即Spring的AOP。

九、依赖注入模式 *(Dependency Injection)

依赖注入(Dependency Injection)是控制反转(Inversion of Control)的一种实现方式。

我们先来看看什么是控制反转。当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者的工作不再由调用者来完成,而是将被调用者的创建移到调用者的外部,从而反转被调用者的创建,消除了调用者对被调用者创建的控制,因此称为控制反转。

要实现控制反转,通常的解决方案是将创建被调用者实例的工作交由 IoC 容器来完成,然后在调用者中注入被调用者(通过构造器/方法注入实现),这样就实现了调用者与被调用者的解耦,该过程被称为依赖注入。依赖注入不是目的,它是一系列工具和手段,最终的目的是帮助开发出松散耦合(loose coupled)、可维护、可测试的代码和程序。这条原则的做法是大家熟知的面向接口,或者说是面向抽象编程。典型应用案例即Spring中使用@Autowize 注解实现属性注入。

个人感觉这种模式和注册模式类似,都是通过统一容器收纳注册对象,调用方直接通过注册容器调用接口,具体实现类则由容器返回。故不再对注册模式分析。

十、流接口模式 *(Fluent Interface)

在软件工程中,流接口(Fluent Interface)是指实现一种面向对象的、能提高代码可读性的 API 的方法,其目的就是可以编写具有自然语言一样可读性的代码,我们对这种代码编写方式还有一个通俗的称呼 —— 方法链。

最常见的就是Lombak注解中,@Builder 或者 @Accessors(chain=true) 开启链式编程,即可通过方法链调用的方式设置实体类的属性。

// 用简洁的方式实例化实体类并完成赋值操作,而无需多行不断调用set方法
XUser xUser = XUser.builder().userId(1).userName("AAAAA").build();

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

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

相关文章

毕业设计ASP.NET 2368酒店信息管理系统【程序源码+文档+调试运行】

一、摘要 本文旨在设计并实现一个功能全面、易于使用的酒店信息管理系统。系统将管理员、客户和前台客服三种用户的需求纳入考虑,并针对每种用户设计了相应的功能模块。系统功能包括用户管理、客户管理、客房管理、商品管理、客房预订管理、入住管理和系统管理。此…

HDD与QLC SSD深度对比:功耗与存储密度的终极较量

在当今数据世界中,存储设备的选择对于整体系统性能和能耗有着至关重要的影响。硬盘HDD和大容量QLC SSD是两种主流的存储设备,而它们在功耗方面的表现是许多用户关注的焦点。 扩展阅读: 1.面对SSD的步步紧逼,HDD依然奋斗不息 2.…

动态页面调研及设计方案

文章目录 vue2 动态表单、动态页面调研一、form-generator二、ng-form-element三、Variant Form四、form-create vue2 动态表单、动态页面调研 一、form-generator 预览:https://mrhj.gitee.io/form-generator/#/ Vue2 Element UI支持拖拽生成表单不支持其他组件…

【iOS】——知乎日报第五周总结

文章目录 一、评论区展开与收缩二、FMDB库实现本地持久化FMDB常用类:FMDB的简单使用: 三、点赞和收藏的持久化 一、评论区展开与收缩 有的评论没有被回复评论或者被回复评论过短,这时就不需要展开全文的按钮,所以首先计算被回复评…

单图像3D重建AI算法综述【2023】

计算机视觉是人工智能的一个快速发展的领域,特别是在 3D 领域。 本概述将考虑一个应用任务:2D 和 3D 环境之间的转换。 在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编…

Flink 运行架构和核心概念

Flink 运行架构和核心概念 几个角色的作用: 客户端:提交作业JobManager进程 任务管理调度 JobMaster线程 一个job对应一个JobMaster 负责处理单个作业ResourceManager 资源的分配和管理,资源就是任务槽分发器 提交应用,为每一个…

【C++上层应用】1. 异常处理

文章目录 【 1. C的标准异常 】【 2. 异常转移处理 】2.1 throw 抛出异常2.2 try 捕获异常2.3 catch 捕获异常2.4 实例 【 3. 定义新的异常 】 异常是程序在执行期间产生的问题,比如编译报错、链接错误等。 【 1. C的标准异常 】 C 提供了一系列标准的异常&#xf…

聊一聊go的单元测试(goconvey、gomonkey、gomock)

文章目录 概要一、测试框架1.1、testing1.2、stretchr/testify1.3、smartystreets/goconvey1.4、cweill/gotests 二、打桩和mock2.1、打桩2.2、mock2.2.1、mockgen2.2.1、示例 三、基准测试和模糊测试3.1、基准测试3.2、模糊测试 四、总结4.1、小结4.2、其他4.3、参考资料 概要…

数电实验-----实现74LS153芯片扩展为8选1数据选择器以及应用(Quartus II )

目录 一、74LS153芯片介绍 管脚图 功能表 二、4选1选择器扩展为8选1选择器 1.扩展原理 2.电路图连接(Quartus II ) 3.仿真结果 三、8选1选择器的应用 1.三变量表决器 2.奇偶校验电路 一、74LS153芯片介绍 74ls153芯片是属于四选一选择器的芯片。…

Python---函数的嵌套(一个函数里面又调用了另外一个函数)

函数嵌套调用------就是一个函数里面又调用了另外一个函数。 基本语法: # 定义 函数B def funcB():print(这是funcB函数的函数体部分...)# 定义 函数A def funcA():print(- * 80) # 这一行为了更好区分print(这是funcA函数的函数体部分...)# 假设我们在调用funcA…

FPGA系列:1、FPGA/verilog源代码保护:基于Quartus13.1平台保护verilog源码发给第三方但不泄露源码

catlog 需求具体步骤工程描述去掉相关调试文件切换顶层模块并导出相应模块为网表文件切换回原顶层模块并添加相应保护模块的qxp文件再次编译工程 参考: 需求 有时需要将源码交付给第三方,但是源码中部分模块涉及到的核心代码无法暴漏给第三方。因此&…

视频转码方法:多种格式视频批量转FLV视频的技巧

随着互联网的发展,视频已成为日常生活中不可或缺的一部分。然而,不同的视频格式可能适用于不同的设备和平台,因此需要进行转码。在转码之前,要了解各种视频格式的特点和适用场景。常见的视频格式包括MP4、AVI、MKV、FLV等。其中&a…

【数据结构与算法】Kadane‘s算法(动态规划、最大子数组和)

文章目录 一、算法原理二、例题2.1 最大子数组和2.2 环形子数组的最大和 一、算法原理 Kadanes算法是一种用于解决最大子数组和问题的动态规划算法。这类问题的目标是在给定整数数组中找到一个连续的子数组,使其元素之和最大(数组含有负数)。…

采集1688整店商品(店铺所有商品、店铺列表api)

返回数据: 请求链接 {"user": [],"items": {"item": [{"num_iid": "738354436678","title": "国产正品i13 promax全网通5G安卓智能手机源头厂家批发手机","pic_url": "http…

elementui表格自定义指令控制显示哪些列可以拖动

Vue.directive(tableBorder, function (el, {value}) {// value允许传字符串数字和数组el.classList.add(z_table_hasBorder)let hasStyle el.querySelector(style)if(hasStyle){hasStyle.remove()}let style document.createElement(style)let str .z_table_hasBorder .el…

基于ResNet框架的CNN

数据准备 DATA_URL http://download.tensorflow.org/example_images/flower_photos.tgz 一、训练集和验证集的划分 #spile_data.pyimport os from shutil import copy import randomdef mkfile(file):if not os.path.exists(file):os.makedirs(file)file flower_data/flower…

代码随想录算法训练营第四十九天| 123.买卖股票的最佳时机III 188.买卖股票的最佳时机IV

文档讲解:代码随想录 视频讲解:代码随想录B站账号 状态:看了视频题解和文章解析后做出来了 123.买卖股票的最佳时机III class Solution:def maxProfit(self, prices: List[int]) -> int:if len(prices) 0:return 0dp [[0] * 5 for _ in…

【监控系统】日志可视化监控体系ELK搭建

1.ELK架构是什么 ELK是ElasticsearchLogstashKibana的简称。 Elasticsearch是一个开源的分布式搜索和分析引擎,可以用于全文检索、结构化检索和分析,它构建在Lucene搜索引擎库之上,是当前使用较为广泛的开源搜索引擎之一。 Logstash是一个…

Linux从 全栈开发 centOS 7 到 运维

Linux从 全栈开发centOS 7 到 运维 一 Linux 入门概述1.1 操作系统1.2 Linux 简介1.3 Linux 系统组成1.4 Linux 发行版1.4 Linux 应用领域1.5 Linux vs Windows 二 环境搭建【狂神说Java】服务器购买及宝塔部署环境说明为什么程序员都需要一个自己的服务器服务器如何购买买完服…

gitlab利用CI多工程持续构建

搭建CI的过程中有多个工程的时候,一个完美的构建过程往往是子工程上的更新(push 或者是merge)触发父工程的构建,这就需要如下建立一个downstream pipeline 子仓库1 .gitlab-ci.yml stages:- buildbuild_job:stage: buildtrigger:project: test_user/tes…