万字解析设计模式之桥接模式、外观模式

一、桥接模式

1.1概述

桥接模式是一种结构型设计模式,它的作用是将抽象部分和实现部分分离开来,使它们能够独立地变化。这样,抽象部分和实现部分可以分别进行扩展,而不会相互影响。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

在桥接模式中,抽象部分和实现部分之间通过一个称为“桥”的接口进行连接。这个桥接口定义抽象部分所需要的所有方法,而实现部分则实现这些方法。这种设计方式可以使得实现部分的变化不会对抽象部分造成影响,因为抽象部分只依赖于桥接口,而不依赖于具体的实现部分。

桥接模式通常使用在需要跨越多个平台或多个产品版本的场景中。它可以提高代码的可复用性和可维护性,同时也可以使得系统更加灵活和可扩展。

现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系

我们可以发现有很多的类,假如我们再增加一个形状或再增加一种颜色,就需要创建更多的类。

试想,在一个有多种可能会变化的维度的系统中,用继承方式会造成类爆炸,扩展起来不灵活。每次在一个维度上新增一个具体实现都要增加多个子类。为了更加灵活的设计系统,我们此时可以考虑使用桥接模式。

 1.2结构

桥接(Bridge)模式包含以下主要角色:

  • 抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。

 1.3实现

【例】视频播放器

需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。

类图如下:

实现化(Implementor)角色

package com.yanyu.Bridge;//视频文件
public interface VideoFile {void decode(String fileName);
}

具体实现化(Concrete Implementor)角色

package com.yanyu.Bridge;//avi文件
public class AVIFile implements VideoFile {public void decode(String fileName) {System.out.println("avi视频文件:"+ fileName);}
}
package com.yanyu.Bridge;//rmvb文件
public class REVBBFile implements VideoFile {public void decode(String fileName) {System.out.println("rmvb文件:" + fileName);}
}

 抽象化(Abstraction)角色

package com.yanyu.Bridge;//操作系统版本
public abstract class OperatingSystemVersion {protected VideoFile videoFile;public OperatingSystemVersion(VideoFile videoFile) {this.videoFile = videoFile;}public abstract void play(String fileName);
}

 扩展抽象化(Refined Abstraction)角色

package com.yanyu.Bridge;//Windows版本
public class Windows extends OperatingSystemVersion {public Windows(VideoFile videoFile) {super(videoFile);}public void play(String fileName) {videoFile.decode(fileName);}
}
package com.yanyu.Bridge;//mac版本
public class Mac extends OperatingSystemVersion {public Mac(VideoFile videoFile) {super(videoFile);}public void play(String fileName) {videoFile.decode(fileName);}
}

客户端类

package com.yanyu.Bridge;//测试类
public class Client {public static void main(String[] args) {OperatingSystemVersion os = new Windows(new AVIFile());os.play("战狼3");}
}

好处:

  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。

    如:如果现在还有一种视频文件类型wmv,我们只需要再定义一个类实现VideoFile接口即可,其他类不需要发生变化。

  • 实现细节对客户透明

1.4应用场景

适合应用场景:

  • 如果你想要拆分或重组一个具有多重功能的庞杂类 (例如能与多个数据库服务器进行交互的类), 可以使用桥接模式。类的代码行数越多, 弄清其运作方式就越困难, 对其进行修改所花费的时间就越长。 一个功能上的变化可能需要在整个类范围内进行修改, 而且常常会产生错误, 甚至还会有一些严重的副作用。
  • 桥接模式可以将庞杂类拆分为几个类层次结构。 此后, 你可以修改任意一个类层次结构而不会影响到其他类层次结构。 这种方法可以简化代码的维护工作, 并将修改已有代码的风险降到最低。

1.5JDK源码解析

桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象与实现分离,使它们可以独立变化。在桥接模式中,抽象和实现可以分别在两个不同的类层次结构中定义,而通过组合的方式将它们联系起来。这样做的好处是可以减少继承带来的耦合问题,提高系统的灵活性和可扩展性。

在 JDK 中,桥接模式的应用非常广泛,下面我们分别来看几个典型的例子:

1. JDBC

在 JDBC 中,数据访问 API 和数据库驱动实现是分离的。数据访问 API 是一组接口和类,定义了数据库操作的抽象方法和常量。而具体的数据库驱动实现则是一组不同的 jar 包,每个包对应一种数据库的不同驱动实现。这样设计的好处是可以实现代码的复用,将代码的改动范围限定在实现层次结构中,不影响客户端的使用。

2. AWT

在 AWT 中,抽象窗口工具包(Abstract Window Toolkit,简称 AWT)提供了一系列抽象类和接口,用于实现和显示图形界面。而具体的 GUI 组件实现则是由不同的操作系统提供的。比如,Windows 提供的 AWT 组件实现和 Linux 提供的 AWT 组件实现是不同的,但它们都可以通过抽象类和接口来实现统一的调用方式,从而保证了跨平台的兼容性。

3. java.util.logging

在 java.util.logging 中,抽象日志记录器(Logger)定义了一组抽象方法和常量,用于记录日志信息。而具体的日志记录实现则是通过不同的日志 Handler 实现的。比如,FileHandler、ConsoleHandler、StreamHandler 等,它们都继承自抽象类 AbstractHandler,实现了具体的日志记录方式。这样设计的好处是可以将日志记录器与具体记录方式分离,提高代码的可扩展性和灵活性。

总之,桥接模式是一种非常实用的设计模式,它可以将抽象和实现分离,使它们可以独立变化。在 JDK 中,有很多典型的桥接模式应用,这些应用不仅为我们提供了很好的学习案例,还可以帮助我们更好地理解桥接模式的思想和作用。

 二、外观模式

2.1概述

外观模式(Facade Pattern)是一种结构型设计模式,它为一组复杂的子系统接口提供了一个统一的接口,以方便客户端使用。外观模式通过将复杂的系统封装在一个简单的外观对象中,简化了客户端的调用过程,同时隐藏了系统的复杂性。

外观模式通常会定义一个简单的高层接口,这个接口封装了系统的所有复杂流程和方法调用,并将这些流程和方法调用转化为若干个简单的方法,供客户端直接调用。这样客户端就不需要了解系统的内部实现细节,也不需要知道哪些子系统需要协同工作才能完成一个请求。

总的来说,外观模式提供了一种简单的方式来使用复杂系统,并使得系统更加易于使用和维护。外观(Facade)模式是“迪米特法则”的典型应用

2.2  结构

外观(Facade)模式包含以下主要角色:

  • 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
  • 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。

2.3实现

【例】智能家电控制

小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭。类图如下:

 

外观(Facade)角色

package com.yanyu.Facade;//智能音箱
public class SmartAppliancesFacade {//定义三个私有属性,分别代表三种电器设备private Light light;private TV tv;private AirCondition airCondition;//构造方法,初始化三种电器设备的对象public SmartAppliancesFacade() {light = new Light();tv = new TV();airCondition = new AirCondition();}//公共方法,根据语音指令控制电器的开关public void say(String message) {//如果语音指令包含“打开”,则调用on()方法if(message.contains("打开")) {on();//如果语音指令包含“关闭”,则调用off()方法} else if(message.contains("关闭")) {off();//否则,打印提示信息} else {System.out.println("我还听不懂你说的!!!");}}//私有方法,起床后一键开电器private void on() {//打印提示信息System.out.println("起床了");//调用三种电器设备的on()方法,分别开启灯、电视和空调light.on();tv.on();airCondition.on();}//私有方法,睡觉一键关电器private void off() {//打印提示信息System.out.println("睡觉了");//调用三种电器设备的off()方法,分别关闭灯、电视和空调light.off();tv.off();airCondition.off();}
}

定义了一个智能音箱类,作为三种电器设备的门面,提供了一个统一的接口,使得客户端可以通过语音指令来控制电器的开关,而不需要了解电器的具体实现细节。外观模式可以简化客户端与子系统之间的交互,降低系统的复杂度和耦合度

 子系统(Sub System)角色

package com.yanyu.Facade;//灯类
public class Light {public void on() {System.out.println("打开了灯....");}public void off() {System.out.println("关闭了灯....");}
}
package com.yanyu.Facade;//电视类
public class TV {public void on() {System.out.println("打开了电视....");}public void off() {System.out.println("关闭了电视....");}
}
package com.yanyu.Facade;//空调类
public class AirCondition {public void on() {System.out.println("打开了空调....");}public void off() {System.out.println("关闭了空调....");}
}

客户端类

package com.yanyu.Facade;//测试类
public class Client {public static void main(String[] args) {//创建外观对象SmartAppliancesFacade facade = new SmartAppliancesFacade();//客户端直接与外观对象进行交互facade.say("打开家电");facade.say("关闭家电");}
}
  • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
  • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
  • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

2.4应用场景

  • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
  • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
  • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

三、 桥接模式实验

任务描述

某软件公司欲开发一个数据转换工具,可以将数据库中的数据转换成多种文件格式,例如 TXT、XML、PDF 等格式,同时该工具需要支持多种不同的数据库。

本关任务:用桥接模式对模拟程序进行框架搭建,如图。

,

实现方式

  1. 明确类中独立的维度。 独立的概念可能是: 抽象/平台, 域/基础设施, 前端/后端或接口/实现。

  2. 了解客户端的业务需求, 并在抽象基类中定义它们。

  3. 确定在所有平台上都可执行的业务。 并在通用实现接口中声明抽象部分所需的业务。

  4. 为你域内的所有平台创建实现类, 但需确保它们遵循实现部分的接口。

  5. 在抽象类中添加指向实现类型的引用成员变量。 抽象部分会将大部分工作委派给该成员变量所指向的实现对象。

  6. 如果你的高层逻辑有多个变体, 则可通过扩展抽象基类为每个变体创建一个精确抽象。

  7. 客户端代码必须将实现对象传递给抽象部分的构造函数才能使其能够相互关联。 此后, 客户端只需与抽象对象进行交互, 无需和实现对象打交道。

编程要求

根据提示,在右侧编辑器 Begin-End 内补充 "DataHandler.java"、"FileConvertor.java"、"PDFConvertor.java"、"XMLConvertor.java" 和 "TXTConvertor.java" 文件的代码。

测试说明

平台会自动从 xml 文件中读取内容,然后对你编写的代码进行测试:

预期输出: 从Oracle数据库中读取数据 转换成PDF格式的数据

预期输出: 从SQLServer数据库中读取数据 转换成TXT格式的数据

客户端类

package step1;import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;public class XMLUtils {//定义一个静态方法,根据传入的标签名,返回对应的类的实例对象public static Object getBean(String name) {try {//创建DOM文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;//解析XML文件,获取Document对象doc = builder.parse(new File("/data/workspace/myshixun/src/config.xml"));//获取包含类名的文本结点NodeList nl = doc.getElementsByTagName(name);Node classNode = nl.item(0).getFirstChild();//获取结点的值,即类名String cName = classNode.getNodeValue();//通过类名生成实例对象并将其返回Class c = Class.forName(cName);Object obj = c.getDeclaredConstructor().newInstance();return obj;}catch(Exception e) {//捕获并打印异常e.printStackTrace();return null;}}
}

这段代码是一个XML工具类,它使用了DOM方式来解析XML文件,获取其中的类名,并通过反射机制来创建类的实例对象。这样可以实现配置文件和代码的分离,提高代码的可维护性和可扩展性 

package step1;public class Client {public static void main(String[] args) {//创建一个抽象化角色的对象,通过XML配置文件获取其具体实现类的名称FileConvertor fileConvertor = (FileConvertor) XMLUtils.getBean("className1");//将一个实现化角色的对象注入到抽象化角色中,通过XML配置文件获取其具体实现类的名称fileConvertor.setDataHandler((DataHandler) XMLUtils.getBean("className2"));//调用抽象化角色的方法,实现对文件的转换fileConvertor.translate();}
}

 实现化角色


package step1;/********** Begin *********/
//定义一个抽象的数据处理类,作为实现化角色
public abstract class  DataHandler{//定义一个抽象的数据读取方法,由子类实现public abstract void readData();
}/********** End *********/

具体实现化角色

package step1;//定义一个从Oracle数据库中读取数据的类,继承自DataHandler类,作为具体实现化角色
public class OracleHandler extends  DataHandler{//重写父类的抽象方法,实现具体的数据读取操作@Overridepublic void readData() {System.out.println("从Oracle数据库中读取数据");}
}
package step1;public class SQLServerHandle extends DataHandler{@Overridepublic void readData() {System.out.println("从SQLServer数据库中读取数据");}
}

抽象化角色

package step1;/********** Begin *********/
//定义一个抽象的文件转换类,作为抽象化角色
public abstract class  FileConvertor{//定义一个受保护的数据处理类的引用,作为实现化角色的接口protected DataHandler handler;//定义一个公共的方法,用于设置数据处理类的对象,实现桥接的过程public void setDataHandler(DataHandler handler) {this.handler = handler;}//定义一个抽象的方法,用于转换文件,由子类实现,调用数据处理类的方法public abstract void translate();}/********** End *********/

扩展抽象类

package step1;//定义一个PDF文件转换类,继承自FileConvertor类,作为扩展抽象化角色
public class PDFConvertor extends FileConvertor{//重写父类的抽象方法,实现具体的文件转换操作@Overridepublic void translate() {/********** Begin *********///调用实现化角色的方法,读取数据handler.readData();/********** End *********///打印提示信息,表示转换成PDF格式的数据System.out.println("转换成PDF格式的数据");}
}
package step1;public class TXTConvertor extends FileConvertor{@Overridepublic void translate() {/********** Begin *********/handler.readData();/********** End *********/System.out.println("转换成TXT格式的数据");}
}
package step1;public class XMLConvertor extends FileConvertor{@Overridepublic void translate() {/********** Begin *********/handler.readData();/********** End *********/System.out.println("转换成XML格式的数据");}
}

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

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

相关文章

全链路压测的步骤及重要性

全链路压测是一种系统性的性能测试方法,旨在模拟真实用户场景下的完整操作流程,全面评估软件系统在不同压力下的性能表现。这种测试方法对于保证应用程序的高可用性、稳定性和可扩展性至关重要。 1. 全链路压测概述 全链路压测是在模拟实际用户使用场景的…

什么是PyQt?

什么是Qt? Qt是一个著名的跨平台C图形用户界面应用程序开发框架。它由Qt公司开发,于1995年首次发布。Qt支持各种桌面,嵌入式和移动平台。 Qt的特点包括: 跨平台支持:Qt应用程序可以编译到多种平台运行,包括Windows,Mac,Linux,Android,iOS等。这大大简化了跨平台应用程序的开…

2019ICPC南京站

A A Hard Problem 题意&#xff1a;给定一个正整数 n &#xff0c;你需要找出最小整数 k&#xff0c;满足&#xff1a;从{1,2,⋯,n}中任意选择长度为k的子集&#xff0c;存在两个不同的整数 u,v∈T, 且 u 是 v 的因数。 思路&#xff1a;打表找规律 #include <bits/std…

JSP:Tag文件的使用

需求&#xff1a;多个JSP页面可能需要使用一些相同的信息 例如:导航栏、标题等。 目标&#xff1a;提高这些相同信息的代码的复用性。 方法&#xff1a;将这些相同的元素形成一种特殊的文件&#xff0c;以便所有页面都可以使用&#xff0c;即&#xff1a;Tag文件 1、Tag对…

MySQL数据库入门到大牛_基础_10_创建和管理表(创建和管理数据库;创建表;修改表;重命名表;删除表;清空表,内容扩展)

前面我们完成了查询结构的介绍&#xff0c;本章介绍DDL和DCL中的COMMIT和ROLL BACK。 文章目录 1. 基础知识1.1 一条数据存储的过程1.2 标识符命名规则1.3 MySQL中的数据类型 2. 创建和管理数据库2.1 创建数据库2.2 使用数据库2.3 修改数据库2.4 删除数据库 3. 创建表3.1 创建…

猫罐头哪个牌子好吃?精选5款好评率高的猫罐头推荐!

很多新手养猫的姐妹们都会为选罐头感到焦虑&#xff01;因为每种猫罐头都有优缺点&#xff0c;每只猫咪的胃口也都不同&#xff0c;所以只有综合考虑选择适合自家猫咪的猫罐头的才是最好的。所以姐妹们在选罐头之前可以先做好功课&#xff0c;了解一下怎么选好的猫罐头。 作为开…

Jmeter监听器

Jmeter监听器 一、监听器介绍二、监听器的类型三、监听器详解3.1 察看结果树3.2 Summary Report3.3 聚合报告3.4 后端监听器3.5 Aggregate Graph3.6 Comparison Assertion Visualizer&#xff08;比较断言可视化器&#xff09;3.7 JSR223 Listener3.8 Response Time Graph3.9 S…

欧科云链研究院:从香港SFC最新文件看链上交易合规必备之选

出品&#xff5c;欧科云链研究院 作者&#xff5c;Hedy Bi 近日&#xff0c;香港证监会在其官网发布“致持牌法团、获证监会发牌的虚拟资产服务提供者及有联系实体的通函 - 打击洗钱&#xff0f;恐怖分子资金筹集经更新的《打击洗钱&#xff0f;恐怖分子资金筹集的自我评估查…

CICD 持续集成与持续交付(2)

目录 gitlab 部署 jenkins 部署 配置 实时触发 自动化构建docker镜像 通过ssh插件交付任务 添加jenkins节点 RBAC pipeline jenkins结合ansible参数化构建 安装ansible 新建gitlab项目 jenkins新建项目playbook gitlab 部署 虚拟机最小需求&#xff1a;4G内存 4核cpu 下载&…

【智能优化算法】从蚁群到动物园

目录 引言蚁群优化算法&#xff08;ACO&#xff09;ACO 机理ACO 模型描述ACO 移动策略 粒子群优化算法&#xff08;PSO&#xff09;PSO 机理PSO 模型描述 萤火虫群优化算法&#xff08;GSO&#xff09;GSO 机理GSO 模型描述 群智能优化算法 引言 21世纪&#xff0c;人类社会已经…

ElementUI用el-table实现表格内嵌套表格

文章目录 一、效果图二、使用场景三、所用组件元素&#xff08;Elementui&#xff09;四、代码部分 一、效果图 二、使用场景 &#x1f6c0;el-form 表单内嵌套el-table表格 &#x1f6c0;el-table 表格内又嵌套el-table表格 三、所用组件元素&#xff08;Elementui&#xff…

Kubeadm部署Kubernetes Containerd集群

文章目录 概述一、硬件系统二、基础配置设置主机名配置主机名与IP地址解析关闭防火墙与selinux时间同步(ntp)升级系统内核配置内核转发及网桥过滤*安装ipset及ipvsadm关闭SWAP分区 三、Containerd准备Containerd获取下载解压Containerd配置文件生成并修改Containerd启动及开机自…

5年经验之谈 —— 性能测试如何定位分析性能瓶颈?

你好&#xff0c;我是小牛&#xff0c;目前在一家准一线互联网大厂做测试开发工程师。 对于一般公司普通测试工程师来说&#xff0c;可能性能测试做的并不是很复杂&#xff0c;可能只是编写下脚本&#xff0c;做个压测&#xff0c;然后输出报告结果&#xff0c;瓶颈分析和调优…

【Hello Go】Go语言复合类型

复合类型 分类指针基本操作new函数指针作为函数的参数 数组概述操作数据数组初始化数组比较在函数之间传递数组 slice概述切片的创建和初始化切片操作切片和底层数组关系内建函数appendcopy 切片作为函数传参 map概述创建和初始化常用操作赋值遍历 删除map作函数参数 结构体结构…

Python (十三) 输出

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

基于Python+TensorFlow+Django的交通标志识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 随着交通网络的不断扩展和智能交通系统的发展&#xff0c;交通标志的自动识别变得愈发重要。本项目旨在利用Python编…

利用SVD对图像进行压缩

利用SVD对图像进行压缩 使用SVD能够对数据进行降维&#xff0c;对图像进行SVD&#xff0c;降维之后然后重构数据&#xff0c;还原后的图像就是压缩后的图像。 SVD SVD进行图像压缩所依据的数学原理就是矩阵的近似表示&#xff1a; A m n ≈ U m k ∑ k k V k n T A_{m\…

基于晶体结构算法优化概率神经网络PNN的分类预测 - 附代码

基于晶体结构算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于晶体结构算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于晶体结构优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

[Docker]八.Docker 容器跨主机通讯

一.跨主机通讯原理 在主机192.168.31.140上的docker0(172.17.0.0/16)中有一个容器mycentos( 172.17.0.2/16), 在主机192.168.31.81上的docker0(172.17.0.0/16)中有一个容器mycentos( 172.17.0.2/16),然后在主机192.168.31.140上ping主机192.168.31.81,发现ping不通要实现两个主…

vite构建项目不能使用require解决方案

在utils文件夹下创建一个getImgUrl.ts文件 /** vite的特殊性, 需要处理图片 */ export const require (imgPath: string) > {try {const handlePath imgPath.replace(, ..)console.log(handlePath::, imgPath)return new URL(handlePath, import.meta.url).href} catch (…