0、背景


加入一个手机分为多种款式,不同款式分为不同品牌。这些详细分类下分别进行操作。

如果传统做法,需要将手机,分为不同的子类,再继续分,基本属于一个庞大的多叉树,然后每个叶子节点进行相同名称、但是细节不同的功能实现。

问题

  1. 类爆炸:类的增加基本没有任何优化,多一个就要妥妥的增加类;
  2. 违反单一原则:增加一个品牌,影响每种类型下的这个品牌,增加一个类型,影响每个品牌的这个类型。


一、桥接模式


解决上面说的问题的方式就是使用桥接模式。

桥接(Bridge)模式是指,将实现和抽象放在两个不同的类层次中,使得两个层次可以独立改变。

桥接模式是一种结构型设计模式,基于类的最小设计原则,让不同的类承担不同的职责。(单一原则)

桥接模式的主要特点是,把抽象 Abstraction 和行为实现 Implementation 分离开,从而保持各部分的独立和应对他们的功能扩展。

类图如下所示:

其中,承担 桥接功能 的抽象类就是 Abstraction。

看起来还是很抽象,结合上面的手机问题,画出对应的类图,再结合来看:

也就是说,比起原来的树状结构,用桥接模式,将行为实现放在一边,有自己的接口;将抽象出的另一个维度放在一边,有自己的抽象父类和子类,然后将上层的抽象类和实现接口相关联。

这样的话,前面遇到的问题:

  • 添加手机样式,就在左边添加抽象类定义就可以;
  • 添加品牌,在右边添加实现类就可以。
  • 其他部分都不会被影响。

观察上面的图,桥接的意味也很明显,当 QuanPingPhone 使用一个 call 方法的时候,继承的其实是父类的方法,而这里面组合了另一个性质,比如是 Oppo 手机的 call 方法,那么就是通过桥接一路引过去的。 (忽略我的英语)

按照这个思路我们来写一个实现:

/*
品牌,接口
*/
public interface Brand {
void open();
void close();
void call();
}
public class Xiaomi implements Brand{
@Override
public void open() {
System.out.println("小米手机开机");
}
@Override
public void close() {
System.out.println("小米手机关机");
}
@Override
public void call() {
System.out.println("小米手机打电话");
}
}
public class Vivo implements Brand{
@Override
public void open() {
System.out.println("vivo手机开机");
}
@Override
public void close() {
System.out.println("vivo手机关机");
}
@Override
public void call() {
System.out.println("vivo手机打电话");
}
}
/*
抽象层:手机,通过品牌接口聚合不同品牌
*/
public abstract class Phone {
//品牌
private Brand brand; public Phone(Brand brand) {
this.brand = brand;
}
protected void open(){
this.brand.open();
}
protected void close(){
this.brand.close();
}
protected void call(){
this.brand.call();
}
}
/*
折叠手机,继承抽象类手机
*/
public class BoldPhone extends Phone{
//构造器
public BoldPhone(Brand brand) {
super(brand);
}
public void open(){
super.open();//实际上通过父类的open,桥接到了brand下面的某个子类的open
System.out.println("打开的是折叠手机");
}
public void close(){
super.close();
System.out.println("关闭的是折叠手机");
}
public void call(){
super.call();
System.out.println("打电话的的是折叠手机"); }
}
/*
全面屏手机,继承抽象类手机
*/
public class ScreenPhone extends Phone{
public ScreenPhone(Brand brand) {
super(brand);
}
public void open(){
super.open();//实际上通过父类的open,桥接到了brand下面的某个子类的open
System.out.println("打开的是全面屏手机");
}
public void close(){
super.close();
System.out.println("关闭的是全面屏手机");
}
public void call(){
super.call();
System.out.println("打电话的的是全面屏手机");
}
}

调用试试:

/*
客户端
*/
public class Client {
public static void main(String[] args) {
//获取一个手机,需要的是样式+手机两个实现类
//通过Phone这个抽象类,增加一个样式为BoldPhone的手机,桥接到品牌为Xiaomi
Phone phone = new BoldPhone(new Xiaomi());
phone.open();
phone.call();
phone.close();
}
}

这样的话,如果扩展起来,比如新增加一种样式的手机,我们只需要继承Phone再写一个子类,其余都不需要更改,如果新增加一个品牌的手机,只需要实现Band接口再写一个实现类,其余都不需要更改。


二、使用桥接模式的源码:JDBC


JDBC 源码里:

MySQL 的 Connection 接口实现的是 java.sql.Connection 接口,同时 Oracle 数据库也一样可以实现 java.sql.Connection 接口,他们向下都可以有更多的实现子类。

然后 DriverManager 相当于桥接模块,依赖和聚合 java.sql.Connection 接口,供客户端调用。

和我们上面所说的略微不同,就是 DriverManager 不是抽象类,而是直接的具体实现。


三、桥接模式的注意事项


  1. 桥接模式实现了抽象和实现部分的分离,提高了系统的灵活性,让抽象部分和实现部分独立开来,有助于系统的分层设计。
  2. 高层只需要知道抽象部分和实现部分的接口,而不需要知道其他具体实现;
  3. 替代了多层继承,大大减少了子类的数量;
  4. 桥接模式的引入,增加了系统的设计和理解难度,由于聚合关联关系建立在抽象层,要求开发者针能对抽象层进行设计和编程;
  5. 适用范围有局限性

应用场景:

  1. jdbc:多种驱动,多种数据库;
  2. 银行转账:因为转账动作要分很多类,用户也分很多类,所以他们之间不适合多层继承;
  3. 消息管理:消息类型不同,消息传送方式不同。

设计模式:桥接模式及代码示例、桥接模式在jdbc中的体现、注意事项的更多相关文章

  1. JAVA设计模式-工厂模式(代码示例)

    结构 MySort.java 实际业务中我们可能会使用任意一种排序方法 package pers.zander.edu.arithmetic.sort; /** * 排序接口 * @author * ...

  2. 《大话设计模式》ruby版代码:建造者模式

    需求: 画一个小人,有头,有身体,两手两脚即可. 初始代码: # -*- encoding: utf-8 -*- #小人一 puts '这是第一个小人' puts '小人一:头' puts '小人一: ...

  3. 《大话设计模式》ruby版代码:外观模式

    需求: 股民买卖股票 初步代码: # -*- encoding: utf-8 -*- #股票1 class Stock1 def buy puts '股票1买入' end def sell puts ...

  4. 《大话设计模式》ruby版代码:策略模式

    需求: 商场收银软件,根据客户购买物品的单价和数量,计算费用,会有促销活动,打八折,满三百减一百之类的. 一,使用工厂模式. # -*- encoding: utf-8 -*- #现金收费抽象类 cl ...

  5. activemq---点对点/发布订阅模式简单代码示例

    activemq 消息模式流程: ConnnectionFactory --> Connection --> Session --> Message ---ConnectionFac ...

  6. 设计模式(c#)代码总结

    设计模式分为三种类型 创建型模式:简单工厂.工厂方法模式.抽象工厂模式.建造者模式.原型模式.单例模式 结构型模式:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式. 行为型模式 ...

  7. 借助全新 MATLAB® 适配器代码示例读取英特尔® 实感™ 摄像头数据流

    下载源代码请访问原文地址:借助全新 MATLAB® 适配器代码示例读取英特尔® 实感™ 摄像头数据流 简介 该可下载代码示例简要介绍了如何使用英特尔® 实感™ SDK 和 MATLAB 的图像采集工具 ...

  8. 019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例

    019 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 13 数据类型转换的代码示例 本文知识点:Java中的数据类型转换案例 学习视频有误,导致没法写文,文章内容 ...

  9. 设计模式(八):Bridge桥接模式 -- 结构型模式

    1. 概述 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度 ...

随机推荐

  1. CDQ分治 & 整体分治

    Part 1:CDQ分治 CDQ分治讲解博客 可以把CDQ分治理解为类似与归并排序求逆序对个数的一种分治算法(至少我现在是这么想的).先处理完左右两边各自对答案的贡献,在处理跨越左右两边的对答案的贡献 ...

  2. 题解 洛谷 P4632 【[APIO2018] New Home 新家】

    首先考虑可以用二分答案来解决询问,可以二分一个长度\(len\),若在区间\([x-len,x+len]\)内包含了所有\(k\)种的商店,那么这个\(len\)就是合法的,可以通过二分来求其最小值. ...

  3. Asp.Net Core 中的“虚拟目录”

    写在前面 现在部署Asp.Net Core应用已经不再限制于Windows的IIS上,更多的是Docker容器.各种反向代理来部署.也有少部分用IIS部署的,IIS部署确实是又快又简单,图形化操作三下 ...

  4. Java环境变量设置:Path、CLASSPATH、JAVA_HOME的作用分别是什么?

    1.Path 作用是指定命令搜索路径,在i命令行下面执行命令如javac编译java程序时,它会到PATH变量所指定的路径中查找百看是否能找到相应的命令程序.        需要把jdk安装目录下的b ...

  5. 性能分析(1)- Java 进程导致 CPU 使用率升高,问题怎么定位?

    性能分析小案例系列,可以通过下面链接查看哦 ps:这些分析小案例不能保证百分比正确,是博主学习过程中的总结,仅做参考 前提 本机有一个很占用 CPU 的项目,放在了 Tomcat 下启动着 如何定位 ...

  6. 浅谈NTLM Hash

    认识Windows Hash 早期SMB协议在网络上传输明文口令.后来出现LAN Manager 挑战/响应验证机制(LM),其很容易破解,因此微软提出了WindowsNT挑战/响应验证机制(NTLM ...

  7. Spring Security OAuth2之resource_id配置与验证

    一.resource_id的作用 Spring Security OAuth2 架构上分为Authorization Server认证服务器和Resource Server资源服务器.我们可以为每一个 ...

  8. ImportError: /lib64/libm.so.6: version `CXXAB_1.3.8.' not found (required by /usr/local/python37/lib/python3.7/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so)

    问题背景 使用在AI项目中,由于需要用到tensorflow,scipy,sklearn等这些库,所以需要libstdc++库. 问题原因 这个问题的出现与写的代码无关,只与操作系统的libstdc+ ...

  9. 以细胞为例 说一下dfs和bfs的思路

    今天发现很少写dfs.. dfs主要思想是递归 bfs主要靠队列 先说一下这个题我被阻了半个小时的地方: 1读数一定要注意scanf的吃回车 2注意数据类型为char,判断时是'0' dfs: #in ...

  10. c++ string 类型 大小写转换 

    还是用以前的库函数就行的,toupper(int c)小写变大写和tolower(int c)大写变小写 可以直接这么干 string s = "ABCDEFG"; for( in ...