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

news2024/11/16 20:48:51

一、桥接模式

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{
    //重写父类的抽象方法,实现具体的数据读取操作
    @Override
    public void readData() {
        System.out.println("从Oracle数据库中读取数据");
    }
}
package step1;

public class SQLServerHandle extends DataHandler{
    @Override
    public 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{
    
    //重写父类的抽象方法,实现具体的文件转换操作
    @Override
    public void translate() {
        /********** Begin *********/
         
        //调用实现化角色的方法,读取数据
        handler.readData();

        /********** End *********/
        //打印提示信息,表示转换成PDF格式的数据
        System.out.println("转换成PDF格式的数据");
    }
}
package step1;

public class TXTConvertor extends FileConvertor{
    
    @Override
    public void translate() {
        /********** Begin *********/
        
        handler.readData();
        /********** End *********/
        System.out.println("转换成TXT格式的数据");
    }
}
package step1;

public class XMLConvertor extends FileConvertor{
     
    @Override
    public void translate() {
        /********** Begin *********/
        
         handler.readData();
        /********** End *********/
        System.out.println("转换成XML格式的数据");
    }
}

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

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

相关文章

如何在AIX操作系统上修改Java环境变量

AIX操作系统是IBM的Unix操作系统,通常用于企业级应用和服务器环境。在AIX上配置Java环境变量是执行Java应用程序和开发Java代码的重要步骤。本文将详细介绍如何在AIX上修改Java环境变量,并提供具体示例来帮助你完成这个任务。 步骤1:确定Java…

vue3组件外使用route

1.vue3组件外使用route 在写vue3项目时,有时候我们会把组件内部分逻辑代码分离到外部js中,然后在组件里通过import导入。此时如果我们要在组件外使用route对象,方式与组件内不同: 组件内: import { useRoute } from…

CANopen权威指南【CAN总线协议】

1这个总线定义是老外发明的。 想要使用,就必须按照协议去配置数据帧。 CIA301和cia402协议,实际就是寄存器地址上某一段的定义。 下载地址: CAN in Automation (CiA): Technical documents 注册下载也是非常快的。【没什么难度】 就是资…

基于Python(Pandas+Pyecharts)实现全国热门旅游景点数据可视化【500010037】

导入模块 import jieba import pandas as pd from collections import Counter from pyecharts.charts import Line,Pie,Scatter,Bar,Map,Grid from pyecharts.charts import WordCloud from pyecharts import options as opts from pyecharts.globals import ThemeType from…

【C++】map multimap

文章目录 1.map介绍2.map的使用3.multimap介绍4.multimap的使用 1.map介绍 map的文档 翻译: map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。 在map中,键值key通常用于排序和惟一地标识元素&#x…

机器学习8:在病马数据集上进行算法比较(ROC曲线与AUC)

ROC曲线与AUC。使用不同的迭代次数(基模型数量)进行 Adaboost 模型训练,并记录每个模型的真阳性率和假阳性率,并绘制每个模型对应的 ROC 曲线,比较模型性能,输出 AUC 值最高的模型的迭代次数和 ROC 曲线。 …

编译器优化代码研究

《Effective C》条款21: /** * 结论:对自定义类型对象表达式objA*objB objC; * 定义friend MyInt operator*(const MyInt& lhs,const MyInt& rhs) * 编译器优化后:operator*()函数内直接在调用接收处构造(此处的匿名临时对象)&am…

万宾科技智能井盖传感器的特性一览

在不断发展的智慧城市技术领域,科学技术的创新永无止境。智能井盖传感器是科学进步带来的高科技产品,为促进城市生命线并保障地上地下连接点安全提供保障。它就在我们脚下,正在悄然改变城市基础设施和公共服务。智能井盖传感器成为现代城市规…

SD-WAN技术:重新定义网络连接方式

随着数字化转型的不断加速,企业对网络的需求呼之欲出。传统的WAN网络由于配置复杂、成本高昂以及带宽利用率低等问题而面临挑战。这时SD-WAN技术的出现正好派上了用场,通过其虚拟化、自动化和智能化的技术手段,大幅度提高了企业网络性能和可靠…

最新AIGC创作系统ChatGPT系统源码,支持最新GPT-4-Turbo模型,支持DALL-E3文生图,图片对话理解功能

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

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

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

系统之家U盘重装系统Win10方法步骤

用户发现自己电脑上的Win10系统出现问题了,想要通过重装系统来解决问题。但是,用户还不清楚具体重新安装Win10系统的步骤,接下来小编给大家详细介绍利用U盘完成Win10系统重装的方法,在这里用户需要下载系统之家装机大师软件&#…

Kubeadm部署Kubernetes Containerd集群

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

金蝶云星空套打设计平台导出套打模板和导入套打模板

文章目录 金蝶云星空套打设计平台导出套打模板和导入套打模板A环境导出套打模板B环境导入套打模板 金蝶云星空套打设计平台导出套打模板和导入套打模板 A环境导出套打模板 导出后: B环境导入套打模板 不要在已设计好的模板导入,会被覆盖 一定记得&am…

Java —— String类

目录 1. String类的重要性 2. 常用方法 2.1 字符串构造 2.2 String对象的比较 2.3 字符串查找 2.4 转化 1. 数值和字符串转化 2. 大小写转换 3. 字符串转数组 4. 格式化 2.5 字符串替换 2.6 字符串拆分 2.7 字符串截取 2.8 其他操作方法 2.9 字符串常量池 2.9.1 创建对象的思考…

程序员如何“升级打怪”?我用了这几个“歪瓜”!

不会吧?不会吧?计算机本命专业出身、以及半路出家的,混了几年了,还在新手村?对得起这几年摸的鱼? 思考一下:如何从小白一跃为大师,从此走上人生巅峰、迎娶白富美?变强只…

ArcGIS如何处理并加载Excel中坐标数据?

做GIS行业的各位肯定免不了跟数据打交道,其中数据的处理说复杂也复杂,因为我们要花时间去做数据的转换及调整工作,那说简单也简单,因为我们有很多的工具可以使用,那么今天我就给大家带来处理Excel中的GIS数据中的其中一…

时间序列预测(9) — Informer源码详解与运行

目录 1 源码解析 1.1 文件结构 1.2 mian_informer.py文件 1.3 模型训练 1.4 模型测试 1.5 模型预测 2 Informer模型 2.1 process_one_batch 2.2 Informer函数 2.3 DataEmbedding函数 2.4 ProbAttention稀疏注意力机制 2.5 Encoder编码器函数 2.6 Decoder解码器函数…

2023.11.20 关于 Spring MVC 详解

目录 MVC 工作流程 Spring MVC 掌握三个功能 创建 Spring MVC 项目 推荐安装插件 EditStarters 安装步骤 使用方法 实现连接功能 基础注解 RequestMapping 指定 GET 和 POST 方法类型 ResponseBody 获取参数 传递 单个 或 多个参数 参数重命名 RequestParam …

【优秀毕设】基于vue+ssm+springboot的网上购物商城系统设计

摘 要 随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,网上商城购物系统当然也不能排除在外。网上商城购物系统是以实际运用为开发背景,运用软件工程原理和开发方…