设计模式(三)-结构型模式(2)-桥接模式

一、为何需要桥接模式(Bridge)?

在软件设计中,存在有两个或多个不同维度的模块时,我们需要将这些模块使用到在一起,来实现一个完整的功能。所谓不同维度的意思就是这些模块所负责的职责是不同的,并且它们之间没有多大的关联性。

根据设计原则中的单一职责原则:各自模块应当保持各自的分工任务。比如在画图系统中存在形状和画笔的两个维度。形状负责的是它的模样(矩形、圆形、三角形等),而画笔负责的是铅笔、毛笔、圆珠笔之类的选型。

倘若在某个维度的类里对另一维度的类有改写的痕迹,就会对这个功能的扩展性以及维护性受到影响。

比如设计一个抽奖系统。员工、员工家属都参与抽奖,奖金分为一二三等奖,最后公布抽奖名单。其中中奖者和奖金是两个不同维度的模块。
以下例子就是个错误的设计,暴露了对代码的扩展和维护困难的弊端。

    // 方式一:将中奖者类和奖金类作为继承关系,互相受到影响public abstract class Bounds{public abstract string getOneGift();//一等奖礼品public abstract string getTwoGift();//二等奖礼品//扩展定义三等奖、安慰奖礼品等...}public class LuckyStaff:Bounds{public override string getOneGift(){return "iPhone 15 Pro";}public override string getTwoGift(){return "HUAWEI MatePad Pro 13.2";}//扩展实现 n 个 getXXXGift...}
//扩展其他中奖者:public class LuckyFamilyMember:Bounds...
//方式一:违反了单一职责原则:
//LuckyStaff 类重写或实现了 Bounds 类的方法,不应该直接处理 Bounds 的事情。//方式二:将中奖者和奖金具体类直接作为对象组合关系,互相受到影响public class LuckyStaff{private Bounds onebounds;private Bounds twobounds;//...public string getOneGift(){return onebounds.getOneGift();}public string getTwoGift(){return twobounds.getTwoGift();}//...}
//扩展其他中奖者:public class LuckyFamilyMember...
//方式二违反了依赖倒置原则和迪米特法则:
//违反依赖倒置原则:当修改 Bounds 类的方法(如移除方法),就会影响 LuckyStaff
//违反迪米特法则:Bounds 和 LuckyStaff 没有多大关联性,却在代码中 LuckyStaff 过于依赖 Bounds

从以上代码可看出,如果以后再扩展不同身份的中奖者和不同的奖金类别,就得需要在原来的代码结构中进行添加和修改,或者再创建一个类重新实现一遍。如果扩展的东西越多,后果将不堪设想。于是为了解决这个问题,这时候就需要用到桥接模式了。

所谓桥接模式就是,把不同维度的模块之间进行分离,保持各自的独立性,然后再把这些模块进行桥接。这样子即使更改了哪个模块,也不会受到影响。

不同维度模块的主要分离为:抽象化和实现化进行分离:
抽象化:抽象化和修正抽象化。(抽象化派生给修正抽象化)
实现化:实现化和具体实现化。(实现化派生给具体实现化)
关系:实现化服务于抽象化。
以下将会解释这个概念。

特点:
将抽象化与实现化进行脱耦(也就是分离),使得他们两个互相独立,不会影响到彼此。

结构:

抽象化接口(Abstraction):定义高层模块的接口。(比如有设置形状大小等的抽象方法。作为形状的基类接口)

修正抽象化类(Refined Abstraction):扩展抽象化,改变或实现抽象化的接口。(比如扩展为矩形类、三角形类,并实现大小的接口;作为 Abstraction 的派生类)

实现化接口(Implementor):定义底层模块的接口。(比如画笔粗细等的抽象方法;作为画笔的基类接口)

具体实现化类(Concrete Implementor):扩展实现化,对实现化接口进行实现。(比如扩展为铅笔类、毛笔类,并实现粗细的接口;作为 Implementor 的派生类)

适合应用场景特点:

  • 可扩展性:不同维度的模块都潜在可扩展性,需要进行抽象化和实现化的分离。(比如抽象化为形状接口、实现化为画笔接口)
  • 服务与运用关系:抽象化属于高层模块,运用底层模块。实现化属于底层模块,服务高层模块。(比如画笔是为形状服务的)
  • 桥接作用:主要针对于不同维度的模块。修正抽象化通常包装一个实现化接口的派生实例。(派生指的是具体实现化)注重对象结构模型。(把形状和画笔进行桥接)

请添加图片描述

二、例子

需求:

年底快到了,公司将要组织一场年会活动,以感谢大家这一年来为公司做出了贡献。老板做出决定,需要在这场活动中有抽奖环节。于是领导把开发抽奖系统的任务交给你去做。具体需求是:奖金设置一等奖有1名、二等奖有两名、三等奖有三名。第一轮被抽到的幸运者获得一等奖,第二轮是二等奖,第三轮是三等奖。后续可能还有安慰奖啥的,领导和老板还在策划中。抽奖结束后,需要公布中奖结果。目前参加成员有员工和员工家属。至于老板是否也参加抽奖环节还未定,暂时先补个空,到时候再做决定。

需求分析:

  • 中奖者(员工、家属、老板(未定))和奖金(一等奖、二等奖、三等奖、安慰奖啥的(未定))是属于不同维度的模块。
  • 奖金服务于中奖者,作为底层模块;而中奖者作为高层模块,运用底层模块。但两者的定义互不影响。即修改中奖者不影响奖金的代码逻辑,反之亦然。
  • 本次抽奖活动在于老板是否参与还未定,以及奖金的类别后续在规划中,都属于可扩展的功能需求。

1、定义实现化接口和具体实现化类:

底层模块:奖金

//Implementor:实现化接口(奖金)interface IBonus{string getGift();}//ConcreteImplementor:具体实现化类(一等奖)class OneBonus : IBonus{public string getGift(){return "iPhone 15 Pro";}}//ConcreteImplementor:具体实现化类(二等奖)class TwoBonus : IBonus{public string getGift(){return "HUAWEI MatePad Pro 13.2";}}//ConcreteImplementor:具体实现化类(三等奖)class ThreeBonus : IBonus{public string getGift(){return "Xiaomi 14 Pro";}}//可能还存在安慰奖、参与奖啥的...

2、定义抽象化接口和修正抽象化类:

高层模块:中奖者

//Abstraction:抽象化接口(中奖者)interface ILucker{string Name { get; }void showResult();}//Refined Abstraction:修正抽象化类(员工中奖者)class LuckyStaff : ILucker{private IBonus iBonus;private string _name;public string Name { get { return _name; } }public LuckyStaff(string name,IBonus iBonus){this.iBonus = iBonus;_name = name;}public void showResult(){Console.WriteLine("恭喜 {0}\t 获得一部 {1} 手机 !", Name, iBonus.getGift());}}//Refined Abstraction:修正抽象化类(可扩展员工家属中奖者)class LuckyFamilyMember :ILucker{private IBonus iBonus;private string _name;public string Name { get { return _name; } }public LuckyFamilyMember(string name, IBonus iBonus){this.iBonus = iBonus;_name = name;}public void showResult(){Console.WriteLine("恭喜 {0}家属\t 获得一部 {1} 手机 !", Name, iBonus.getGift());}}

3、主程序(抽奖名单,公布结果):

class Program{static void Main(string[] args){OneBonus oneBounds = new OneBonus();TwoBonus twoBonus = new TwoBonus();ThreeBonus threeBnus = new ThreeBonus();//抽奖名单...List<ILucker> lucker = new List<ILucker>(){//第一轮抽奖new LuckyFamilyMember("CRongQ",oneBounds),//第二轮抽奖new LuckyStaff("张三",twoBonus),new LuckyStaff("小蓝",twoBonus),//第三轮抽奖new LuckyStaff("小红",threeBnus),new LuckyFamilyMember("李四",threeBnus),new LuckyStaff("赵五",threeBnus)};Console.WriteLine("抽奖结果:\n");foreach (var item in lucker){item.showResult();}Console.ReadLine();}}

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

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

相关文章

lua安装

lua安装 1.Lua介绍 特点&#xff1a;轻量、小巧。C语言开发。开源。 设计的目的&#xff1a;嵌入到应用程序当中&#xff0c;提供灵活的扩展和定制化的功能。 luanginx&#xff0c;luaredis。 2.windows安装lua windows上安装lua&#xff1a; 检查机器上是否有lua C:\U…

【《漫画算法》笔记】找数组中出现奇数次的元素

给定数组&#xff0c;只有一个元素出现过奇数次 思路&#xff1a; 考虑到异或运算的性质——两个等值数的异或结果等于0&#xff0c;两个不等值的数异或结果为1&#xff0c; 将给定数组的所有元素异或起来&#xff0c;最终结果就是那个“唯一出现过计数次”的数。 给定数组&a…

10129 - Play on Words (UVA)

题目链接如下&#xff1a; Online Judge 欧拉道路的题。 有向图满足欧拉道路有两个条件&#xff1a;1&#xff0c;图是连通的&#xff08;无向边意义上&#xff09;&#xff1b;2&#xff0c;最多只能有两个点的出度不等于入度&#xff0c;而且其中一个点的出度比入度大1&am…

Kubernetes 的用法和解析 -- 4

一.Deployment 资源详解 如果Pod出现故障&#xff0c;对应的服务也会挂掉&#xff0c;所以Kubernetes提供了一个Deployment的概念 &#xff0c;目的是让Kubernetes去管理一组Pod的副本&#xff0c;也就是副本集 &#xff0c;这样就能够保证一定数量的副本一直可用&#xff0c;…

概率论复习

第一章&#xff1a;随机概率及其概率 A和B相容就是 AB 空集 全概率公式与贝叶斯公式&#xff1a; 伯努利求概率&#xff1a; 第二章&#xff1a;一维随机变量及其分布&#xff1a; 离散型随机变量求分布律&#xff1a; 利用常规离散性分布求概率&#xff1a; 连续性随机变量…

华为OD试题四(字符串统计、计算误码率、计算最大乘积)

1. 字符串统计 题目描述&#xff1a; 给定两个字符集合&#xff0c;一个为全量字符集&#xff0c;一个为已占用字符集。 已占用的字符集中的字符不能再使用&#xff0c;要求输出剩余可用字符集。 输入描述&#xff1a; 1、输入为一个字符串&#xff0c;一定包含符号。前的为全…

记录每日LeetCode 746.使用最小花费爬楼梯 Java实现

题目描述&#xff1a; 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低…

Windows使用VNC Viewer远程桌面Ubuntu【内网穿透】

文章目录 前言1. ubuntu安装VNC2. 设置vnc开机启动3. windows 安装VNC viewer连接工具4. 内网穿透4.1 安装cpolar【支持使用一键脚本命令安装】4.2 创建隧道映射4.3 测试公网远程访问 5. 配置固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址5.3 测试…

hypervisor display显卡节点card0生成过程

ditsi 配置 lagvm/LINUX/android/vendor/qcom/proprietary/devicetree/qcom direwolf-g9ph.dts #include "direwolf-vm-la.dtsi" direwolf-vm-la.dtsi #include "display/quin-vm-display-la.dtsi" quin-vm-display-la.dtsi //对应/sys/class/drm/card…

推荐几款值得收藏的3DMAX插件

推荐几款值得收藏的3DMAX插件 StairGenerator StairGenerator一键楼梯插件&#xff0c;不需要花费太多的时间&#xff0c;轻松从2D平面图生成3D楼梯模型&#xff0c;生成的楼梯模型细节丰富真实。 【主要功能】 1.简单&#xff1a;轻松实现2D到3D建模。 2.具有最详细三维结…

高级桌面编程(一)

前言 学习心得&#xff1a;C# 入门经典第8版书中的第15章《高级桌面编程》 创建控件并设置样式 1 样式 Style WPF 当中我们可以对每一个控件进行完全的自定义。我们可以随意更改控件外观和功能。提供我们能完成这样的效果与控件的样式&#xff08;Style&#xff09;有着不可分…

【mysql8的一个利器, CTE查询】

今天因为一个比较头疼的mysql查询问题, 去Stack Overflow提问, 然后底下有人答复说既然用了mysql8, 不要用子查询, 直接用CTE查询. CTE查询是啥, 我直接百度了下, 然后发现, 这玩意太好用了, 但是说实话, 我可以肯定大多数人基本都不会去了解这些新出的功能,毕竟经手的项目大多…

人工智能革命:共同探索AIGC时代的未来

一、引言 随着大数据和强大的计算能力的兴起&#xff0c;人工智能技术&#xff08;AI&#xff09;正在快速发展&#xff0c;并为各个领域带来革命性的变化。人工智能与智能计算技术&#xff08;AIGC&#xff09;的融合不仅为企业、科研机构和普通用户提供了巨大的机遇&#xff…

60.Sentinel源码分析

Sentinel源码分析 1.Sentinel的基本概念 Sentinel实现限流、隔离、降级、熔断等功能&#xff0c;本质要做的就是两件事情&#xff1a; 统计数据&#xff1a;统计某个资源的访问数据&#xff08;QPS、RT等信息&#xff09; 规则判断&#xff1a;判断限流规则、隔离规则、降级规…

数字孪生Web3D智慧机房可视化运维云平台建设方案

前言 进入信息化时代&#xff0c;数字经济发展如火如荼&#xff0c;数据中心作为全行业数智化转型的智慧基座&#xff0c;重要性日益凸显。与此同时&#xff0c;随着东数西算工程落地和新型算力网络体系构建&#xff0c;数据中心建设规模和业务总量不断增长&#xff0c;机房管理…

函数装饰器基础

1 函数装饰器基础 1.1 函数调用 描述 根据之前文章《python函数属性和注解》得知&#xff0c;python函数定义时生成函数对象并赋值给了函数名。 调用方式&#xff1a; &#xff08;1&#xff09;直接调用方式为&#xff1a;通过函数名圆括号()调用函数&#xff0c;比如f1(…

Xcode 15 Assertion failed: (false “compact unwind compressed function offset doesn‘t fit in 24

xcode 真机运行报错&#xff1a;Assertion failed: (false && "compact unwind compressed function offset doesnt fit in 24 bits"), function operator(), file Layout.cpp, line 5758 如下图&#xff1a; 解决办法&#xff1a; 在 targets-->Build …

力扣---最长回文子串(动态规划)

目录 ​编辑 题目 思路步骤&#xff1a; 代码 我的其他博客 题目 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&…

【C++】标准库中的string类

目录 一、介绍 二、常用接口 1.构造函数和赋值操作 2.string类对象的容量操作 3.string类对象的访问及遍历操作 4.string类对象的修改操作 5.string类非成员函数 放在最后 一、介绍 在C中&#xff0c;std::string 是标准库提供的字符串类。它属于 <string> 头文件…

ROS机器人入门

http://www.autolabor.com.cn/book/ROSTutorials/ 1、ROS简介 ROS 是一个适用于机器人的开源的元操作系统。其实它并不是一个真正的操作系统&#xff0c;其 底层的任务调度、编译、寻址等任务还是由 Linux 操作系统完成&#xff0c;也就是说 ROS 实际上是运 行在 Linux 上的次级…