JS设计模式之桥接模式:搭建跨越维度的通路
引言
在软件开发中,我们经常遇到需要对不同的抽象类进行不同的实现的情况,而传统的对象嵌套并不是一个优雅且可扩展的解决方案,因此这正是桥接模式的用武之地。桥接模式通过将抽象与实现分离,使得它们可以独立变化,从而提供了更好的可扩展性和代码重用性。
在本篇文章中,我们将从桥接模式的基本概念出发,深入剖析其原理和实现方法。我们将学习如何创建抽象类和实现类,并通过桥接类将它们连接起来。同时,我们还将通过实际案例来展示桥接模式的应用,以及它与其他设计模式的关系。
一. 桥接模式的基本概念
1. 什么是桥接模式
桥接模式(Bridge Pattern
)是一种重要的结构型设计模式,它主要用于解耦抽象和实现之间的关系,使它们能够独立变化。在 JavaScript 中,桥接模式允许我们将抽象和具体实现部分分离,使它们可以独立进行扩展和演化。
在传统的 JavaScript 开发中,我们通常使用继承来实现抽象和具体实现之间的关系。例如,我们可以定义一个基类,然后通过创建子类来实现不同的具体实现。这种方式存在一些问题,例如子类的扩展会导致类的爆炸性增长,并且难以应对多个维度的变化。
而桥接模式则提供了一种更加灵活和可扩展的解决方案。它通过使用抽象接口和具体实现类之间的桥接,将它们解耦并以独立的方式进行演化。通过桥接模式,我们可以在运行时动态地将抽象和实现连接起来,而无需在编译时进行固定的绑定。
在桥接模式中,抽象类是一个具有抽象方法的类或接口,它定义了抽象部分的接口。具体实现类是实现抽象类的具体类,它实现了抽象部分的具体操作。桥接类充当桥梁,将抽象类和具体实现类连接起来,通过调用具体实现类的方法来实现抽象部分的功能。
2. 桥接模式的核心要素
JavaScript 桥接模式的核心要素包括以下三部分:
-
抽象类(或接口):抽象类定义了抽象部分的接口和行为,它是桥接模式中的核心概念。抽象类为实现提供了一个承的基础,它声明了抽象方法和属性,描述了抽象部分的功能。在 JavaScript 中,抽象类可以通过类(class)或者接口(interface)来定义。
-
实现类:实现类提供了抽象部分的具体实现。它是抽象类的具体子类,在实现类中实现了抽象类中声明的抽象方法和属性。实现类可以根据需求进行扩展和修改,不会影响到抽象类的设计。在 JavaScript 中,实现类可以是普通的 JavaScript 类。
-
桥接类:桥接类充当了抽象类和实现类之间的桥梁,将抽象部分的行为委托给具体实现。桥接类包含一个对实现类的引用,将抽象部分的方法调用委托给实现类。通过桥接类,抽象类和实现类可以独立地变化,它们之间解耦合,使得系统更加灵活和可扩展。
总结:JavaScript 桥接模式的核心要素包括抽象类(或接口)、实现类和桥接类。抽象类定义抽象部分的接口和行为,实现类提供抽象部分的具体实现,桥接类充当抽象类和实现类之间的桥梁,将抽象部分的行为委托给具体实现。这样可以实现抽象和实现的解耦,提供更好的可扩展性和灵活性。
3. UML 类图说明
-
抽象部分(
Abstraction
):是一个抽象类或接口,它定义了抽象部分的方法。在JavaScript中,我们使用构造函数或类来代表抽象部分。 -
扩充抽象部分(
RefAbstraction
):是抽象部分具体实现类。在JavaScript中,我们使用子类来代表扩充抽象部分,它继承了抽象部分并可以扩展额外的功能。 -
实现部分(
Implementor
):是一个接口或抽象类,它定义了实现部分的方法。在JavaScript中,我们使用对象字面量或构造函数来代表实现部分。 -
具体实现部分(
ConcreteImplementor
):是实现部分的具体实现类。在JavaScript中,我们使用具体对象或构造函数来代表具体实现部分。 -
客户端(
Client
):是使用桥接模式的代码部分。在JavaScript中,我们可以在客户端中创建抽象部分和实现部分的对象,并进行组合和调用。
以上是一个简单的桥接模式的用例图解析,通过使用桥接模式,我们可以将抽象部分和实现部分分离,使其可以独立地变化和扩展。
二. 实现桥接模式
步骤 1:定义抽象类(或接口)
首先,创建一个抽象类(或接口),它定义了抽象部分的接口和行为。这个抽象类可以是一个普通的 JavaScript 类,或者使用接口的方式。
// 定义抽象类
class AbstractClass {constructor(implementation) {this.implementation = implementation;}performAction() {// 委托实现类的具体方法this.implementation.action();}
}
步骤 2:定义实现类
要实现桥接模式,需要创建一个或多个实现类,它们提供了抽象部分的具体实现。这些实现类可以是普通的 JavaScript 类。
// 定义实现类A
class ConcreteImplementationA {action() {console.log("ConcreteImplementationA is performing action");}
}// 定义实现类B
class ConcreteImplementationB {action() {console.log("ConcreteImplementationB is performing action");}
}
步骤 3:创建桥接实例
通过将实现类实例传递给抽象类的构造函数,创建一个桥接实例。这样,抽象类引用了具体的实现类。
// 创建桥接实例
const instance1 = new AbstractClass(new ConcreteImplementationA());
instance1.performAction(); // 输出:ConcreteImplementationA is performing actionconst instance2 = new AbstractClass(new ConcreteImplementationB());
instance2.performAction(); // 输出:ConcreteImplementationB is performing action
在上面的代码中,我们进行了3个步骤,完整的实现了一个桥接模式的运用。
首先,我们定义了一个抽象类 AbstractClass
,它接收一个具体的实现类实例作为参数,并在 performAction
方法中调用实现类的 action
方法。
然后,我们定义了两个具体的实现类 ConcreteImplementationA
和 ConcreteImplementationB
,它们都实现了 action
方法,并有不同的实现。
最后,我们创建了两个桥接实例 instance1
和 instance2
。当调用 performAction
方法时,实际执行的是具体实现类中的 action
方法,输出了不同的结果。
通过这样的实现,我们使用桥接模式将抽象部分和实现部分进行了解耦,可以灵活地改变或扩展实现类的行为,而不会影响到其他部分的代码。
三. 桥接模式的优缺点
优点
-
解耦性强:桥接模式能将抽象部分和实现部分相互解耦,使它们可以独立变化。通过将抽象类和实现类分离,可以使它们可以独立进行修改和扩展,不会相互影响。
-
扩展性好:由于桥接模式能灵活地将抽象部分和实现部分组合起来,因此在需要新增实现类或抽象部分时,可以在不修改现有代码的情况下进行扩展。
-
可复用性高:桥接模式中的抽象类可以通过不同的实现类进行组合,可以更好地复用已有的代码。实现类也可以在不同的抽类中进行复用。
-
可以动态切换实现:桥接模式使得可以在运行时动态地切换具体的实现类,而不影响抽象类。这种动态切换可以在一定程度上实现运行时的灵活性和可定制性。
缺点
-
增加了系统的复杂性:使用桥接模式会增加一定的类和对象的数量,增加了系统的复杂性,会增加代码的阅读和维护难度。
-
引入了间接性:桥接模式通过一层间接来把抽象部分和实现部分连接起来,有时候可能会导致理解和调试困难。
综上所述,桥接模式在将抽象和实现部分相互解耦、提供灵活性和可扩展性方面具有明显的优势。但它也会增加系统的复杂性,并且引入了一层间接性。在具体应用时需要权衡利弊,根据实际需求决定是否使用桥接模式。
四. 应用案例
图形绘制应用
应用场景:假设有一个图形绘制应用,需要支持不同的图形形状(如矩形、圆形等)和不同的绘制颜色(如红色、蓝色等)的组合。这时可以使用桥接模式来实现。
首先,我们定义图形的抽象类 Shape
:
class Shape {constructor(color) {this.color = color;}draw() {console.log(`Drawing a ${this.color.fill()} ${this.constructor.name}`);}
}
然后,我们定义具体的图形类,如 Rectangle
和 Circle
:
class Rectangle extends Shape {constructor(color) {super(color);}
}class Circle extends Shape {constructor(color) {super(color);}
}
接下来,我们定义颜色的抽象类 Color
:
class Color {fill() {throw new Error("This method must be overridden");}
}
再然后,我们具体的颜色类,如 Red
和 Blue
:
class Red extends Color {fill() {return "red";}
}class Blue extends Color {fill() {return "blue";}
}
最后,我们可以通过桥接模式来创建图形,并指定具体的颜色:
const redRectangle = new Rectangle(new Red());
redRectangle.draw(); // 输出:Drawing a red Rectangleconst blueCircle = new Circle(new Blue());
blueCircle.draw(); // 输出:Drawing a blue Circle
在上面的实例中,我们使用桥接模式将图形的抽象部分(Shape
)和颜色的抽象部分(Color
)分离开来,并通过组合的方式将它们连接起来。
通过定义具体的图形类(Rectangle
和 Circle
)和具体的颜色类(Red
和 Blue
),我们可以根据需要灵活地组合不同的图形和颜色,实现各种绘制组合。
调用图形的 draw
方法,实际上会调用具体的颜色类的 fill
方法,输出相应的绘制信息。
这样,使用桥接模式可以让我们轻松扩展和修改图形的形状和颜色,而不会相互影响。同时,也提高了代码的可复用性和可维护性。
五. 桥接模式的最佳实践
-
明确定义抽象部分和实现部分:在设计桥接模式时,首先需要明确抽象部分和实现部分的功能和职责。抽象部分通常是高层模块,定义了抽象方法和属性,而实现部分是低层模块,负责实现具体的功能。
-
组合而非继承:桥接模式通常通过组合来连接抽象部分和实现部分,而不是使用继承。这样可以实现抽象部分和实现部分的解耦,同时也更加灵活地扩展和变化。
-
通过接口或抽象类定义抽象部分:在 JavaScript 中,可以使用接口或抽象类来定义抽象部分的方法和属性。这可以为具体的实现类提供一个共同的接口,方便统一调用。
-
应用适当的设计模式:桥接模式可以与其他设计模式配合使用,以实现更复杂的功能。例如,可以与工厂模式结合,用于创建不同的实现类实例。
-
考虑灵活性和可扩展性:桥接模式的优势之一是灵活性和可扩展性。因此,在设计时应该考虑到后续可能的变化和扩展。如需新增抽象部分或实现部分,应该可以轻松添加新的类而不影响现有的代码。
总之,JavaScript 桥接模式的最佳实践是明确定义抽象部分和实现部分、使用组合而不是继承、通过接口或抽象类定义抽象部分、应用适当的设计模式、考虑灵活性和可扩展性,以及合理命名和组织代码。这样能够使得代码结构清晰、可维护性高,并且能够轻松地扩展和修改功能。
总结
在本篇文章中,我们详细解析了 JavaScript 桥接模式的基础和应用,并总结了项目最佳实践。桥接模式通过将抽象部分和实现部分分离,可以让它们分别独立变化,以实现更灵活的扩展和变化。
当你需要设计具有多个不同维度的变化的系统时,桥接模式是一个很好的选择。它可以避免类的指数级增长,并且能够灵活组合不同的抽象和实现。
通过桥接模式,我们可以轻松地组合不同的抽象和实现,满足不同的需求。同时,桥接模式还提供了代码重用和可维护性的好处,使得我们的代码更加整洁和可扩展。
相关文章:

JS设计模式之桥接模式:搭建跨越维度的通路
引言 在软件开发中,我们经常遇到需要对不同的抽象类进行不同的实现的情况,而传统的对象嵌套并不是一个优雅且可扩展的解决方案,因此这正是桥接模式的用武之地。桥接模式通过将抽象与实现分离,使得它们可以独立变化,从…...

苹果电脑系统重磅更新——macOS Sequoia 15 系统 新功能一 览
有了 macoS Sequoia,你的工作效率将再次提升:快速调整桌面布局,一目了然地浏览网页重点,还可以通过无线镜像功能操控你的iPhone。 下面就来看看几项出色新功能,还有能够全面发挥这些功能的 App 和游戏。 macOS Sequo…...

DoppelGanger++:面向数据库重放的快速依赖关系图生成
doi:DoppelGanger: Towards Fast Dependency Graph Generation for Database Replay,点击前往 文章目录 1 简介2 架构概述3 依赖关系图3.1 符号和问题定义3.2 无 IT(k) 图3.3 无 OT 图表3.4 无 OTIT 图表3.5 无 IT[OT] 图表3.6 输出确定性保证 4 重复向后…...

Linux(含麒麟操作系统)如何实现多显示器屏幕采集录制
技术背景 在操作系统领域,很多核心技术掌握在国外企业手中。如果过度依赖国外技术,在国际形势变化、贸易摩擦等情况下,可能面临技术封锁和断供风险。开发国产操作系统可以降低这种风险,确保国家关键信息基础设施的稳定运行。在一…...
calibre-web默认左上角字体修改
calibre-web默认左上角字体修改 如图: 有些奇异,如果想变成正常的常规字体,需要修改: cps\static\css\style.css 下的代码: 默认是GrandHotel-Regular: 换成其他字体即可。其他字体在 calibre-web\cps\s…...

考研数据结构——C语言实现归并排序
包含头文件:程序首先包含了标准输入输出库stdio.h,以便使用printf等函数进行输入输出操作。 定义数组和数组大小:定义了一个宏N,其值为5,表示数组q的长度。数组q被初始化为{5, 3, 8, 4, 2},这是我们要排序…...
LDO功率管选取NMOS和PMOS区别
一、drop电压 LDO如果两个管子流过相同的电流, 假设将管子饱和并顶到接近线性区 NMOS的效率(VIN-VDSAT-VGS)/VIN PMOS的效率=(VIN-VDSAT)/VIN 根本原因是 nmos的gate电压比source高vth 如果输出电压(source)较高或者驱动电流要大,…...

【Linux】进程的标识符、状态(超详解)
目录 进程的概念 进程标识符PID 系统调用创建进程-fork初识 进程状态 R状态(运行状态) S,D状态(休眠状态) T,t状态 Z状态(僵尸进程) 孤儿进程 X状态(死亡状态&a…...

Elasticsearch 启动后在浏览器输入http://localhost:9200 访问失败
windows Elasticsearch 启动后在浏览器输入http://localhost:9200 访问失败 文章目录 前言本地下载安装了个elasticsearch,启动成功了,在本地访问http://localhost:9200 无法访问!!!难受了一下。 一、windows Elastics…...
javascript中new操作符的工作原理
在 JavaScript 中,new 操作符用于创建对象的实例。它可以让我们通过构造函数创建一个新的对象,并初始化该对象的属性和方法。尽管 new 操作符的使用很常见,但它在背后实际进行了几个步骤。下面详细解释 new 操作符具体做了哪些事情。 new 操…...

基于springboot+vue 旅游网站的设计与实现
基于springbootvue 旅游网站的设计与实现 摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱,…...

Ansible集群服务部署案例
案例描述 本案例共讲述了多个节点部署Elk集群日志分析系统,分别在三个节点使用ansible部署Kibana、Logstash以及Elasticsearch服务。 案例准备 1. 规划节点 IP 主机名 节点 192.168.100.25 ansible Ansible节点 192.168.100.35 node1 Elasticsearch/Kiba…...

探索AI编程新境界:aider库揭秘
文章目录 **探索AI编程新境界:aider库揭秘**背景:为何选择aider?简介:aider是什么?安装指南:如何安装aider?功能演示:aider的简单用法实战应用:aider在不同场景下的使用常…...
SQL Server 2012 ldf日志文接太大的截断和收缩日志处理
SQL Server 2012 ldf日志文接太大的截断和收缩日志处理操作 --- SQL Server 2012 ldf日志文接太大的截断和收缩日志处理 ----- 查看所有 database 列表及详情 select * from sys.databases;-- 切换到指定的操作数据库 use testdb;-- 查询当前数据库的日志文件ID和逻辑文件名 S…...

java日志门面之JCL和SLF4J
文章目录 前言一、JCL1、JCL简介2、快速入门3、 JCL原理 二、SLF4J1、SLF4J简介2、快速入门2.1、输出动态信息2.2、异常信息的处理 3、绑定日志的实现3.1、slf4j实现slf4j-simple和logback3.2、slf4j绑定适配器实现log4j 4、桥接旧的日志框架4.1、log4j日志重构为slf4jlogback的…...
Oracle DB运维常用的视图及数据字典
List item 本文介绍一些Oracle DB日常运维最常用到(使用频率很高)的视图及数据字典 用户有关的常用视图: 1、 查看当前用户的缺省表空间* SQL>select username,default_tablespace from user_users; 2、 查看当前用户的角色 SQL>sele…...
vue.config.js devServer中changeOrigin的作用
问题 vue开发时,为了解决前端跨域问题,通常在vue.config.js配置 devServer proxy devServer: {proxy:{/api: {target: http://b.com,changeOrigin: false},}, }官方文档http-proxy options对changeOrigin的解释 option.changeOrigin: true/false, Defa…...

基于Ubuntu 20.04 LTS上部署MicroK8s(最小生产的 Kubernetes)
目录 文章目录 目录简介Kubernetes简介MicroK8s简介Ubuntu系统MicroK8s的优势安装环境基本要求执行安装命令加入群组(使用非 root 用户访问)开启 dashboard 仪表盘查看服务名称查看仪表盘开放的端口打开浏览器检查状态打开你想要的服务(使用附加组件)开始使用 microk8s访问 Kub…...

Spring:项目中的统一异常处理和自定义异常
介绍异常的处理方式。在项目中,都会进行自定义异常,并且都是需要配合统一结果返回进行使用。 1.背景引入 (1)背景介绍 为什么要处理异常?如果不处理项目中的异常信息,前端访问我们后端就是显示访问失败的…...

有点快要跟不上时代的感觉
团队的群里面有一个同事突然问了下,下面的这个 JavaScript 如何进行优化 var startIndex (start undefined || start null) ? null : start[0].Value;看上面的代码就是典型的判断和返回的问题。 如果是要调试的话也不是做不出来,但可能要花点时间&a…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...