【设计模式】适配器模式和桥接模式
创始人
2024-05-31 15:16:28
0

适配器模式

适配器模式 : 就是将一个类的接口变成客户端所期望的另一种接口,使得原本因为接口不匹配而无法一起工作的接口可以正常工作。属于结构型模式

比方说我有一个A牌子的奶瓶,然后买了个B牌子的奶嘴,不能匹配怎么办? 再买一个转换器就好了

在软件开发中,基本上任何问题都可以通过加一个中间层(抽象)来解决

适配器模式一般包含三种角色 :

目标角色Target : 也就是客户端期望的接口
源角色Adaptee: 已有的接口,内容满足要求,但是客户端无法接入
适配器 Adapter:

适配器可分为三类 :类适配器,对象适配器,接口适配器 (其核心思想都是让Adapter同时具有Target和Adaptee的特性)

以生活中的的电压为例,我们需要将民用的220V的交流电转换为手机可以接收的5V的直流电

public interface IDV5VTarget {int output5V();
}
public class AC220VAdaptee {public int output220V() {int output = 220;System.out.println("输出电压:" + output + "V");return output;}
}

类适配器: 基于继承实现,Adapter继承Adaptee

public class ClassAdapter extends AC220VAdaptee implements IDV5VTarget{@Overridepublic int output5V() {int output = super.output220V()/44;System.out.println("转换后的电压为" + output + "V");return output;}
}

对象适配器 : 基于组合实现,Adapter持有Adaptee对象

public class ObjectAdpater implements IDV5VTarget{private AC220VAdaptee ac220VAdaptee;public ObjectAdpater(AC220VAdaptee ac220VAdaptee) {this.ac220VAdaptee = ac220VAdaptee;}@Overridepublic int output5V() {int output =  this.ac220VAdaptee.output220V()/44;System.out.println("转换后的电压为:" + output + "V");return output;}
}

接口适配器 : 如果接口定义的方法过多,通过接口适配器可以让我们只实现需要转换的方法(直接实现接口的话就会有很多空实现的方法)

总结

优点 :

  1. 通过适配器模式,客户端可以使用统一的一套接口,这样实现起来更简洁明了
  2. 复用了现有的类和代码,减少代码的改动量

使用场景

适配器模式是⽤来做适配的,它使得不兼容的接⼝转换为可兼容的接⼝,让原本由于接⼝不兼容⽽不能⼀起⼯作的类可以⼀起⼯作。常用于补偿接口设计上的缺陷。

一般使用场景如下:

  • 封装有缺陷的接⼝设计
  • 统⼀多个类的接⼝设计
  • 替换依赖的外部系统
  • 兼容⽼版本接⼝
  • 适配不同格式的数据

桥接模式

桥接模式是将抽象部分与他的具体实现分离,使其都可以独立的发生变化而互不干扰,属于结构型模式。
桥接模式的核心在于解耦抽象和实现。

这里的“抽象”,指的并⾮“抽象类”或“接⼝”,⽽是被抽象出来的⼀套“类库”,它只包含⾻架代码,真正的业务逻辑需要委派给定义中的“实现”来完成。⽽定义中的“实现”,也并⾮“接⼝的实现类”,⽽是⼀套独⽴的“类库”。“抽象”和“实现”独⽴开发,通过对象之间的组合关系,组装在⼀起

经典的桥接模式的应用就是我们的JDBC驱动

{Class.forName("com.mysql.jdbc.Driver");//加载及注册JDBC驱动程序String url = "jdbc:mysql://localhost:3306/db?Connection con = DriverManager.getConnection(url);Statement stmt = con.createStatement();String query = "select * from test_table";ResultSet rs=stmt.executeQuery(query);while(rs.next()) {rs.getString(1);rs.getInt(2);}
}

如果我们使用不同的数据库,只需要将com.mysql.jdbc.Driver 换成 oracle.jdbc.driver.OracleDriver 就可以了

我们可以看下jdbc是怎么实现的

public class Driver extends NonRegisteringDriver implements java.sql.Driver {public Driver() throws SQLException {}static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}
}

Class.forName("com.mysql.jdbc.Driver")这里就是执行了Driver的静态代码块,将Driver注册到DriverManager里,然后我们从DriverManager里获取的connection实际是使用我们注册的driver获取到的

 public static Connection getConnection(String url,java.util.Properties info) throws SQLException {return (getConnection(url, info, Reflection.getCallerClass()));}private static Connection getConnection(//...for(DriverInfo aDriver : registeredDrivers) {if(isDriverAllowed(aDriver.driver, callerCL)) {try {Connection con = aDriver.driver.connect(url, info);if (con != null) {// Success!println("getConnection returning " + aDriver.driver.getClass().getName());return (con);}} catch (SQLException ex) {if (reason == null) {reason = ex;}}} else {println("    skipping: " + aDriver.getClass().getName());}}throw new SQLException("No suitable driver found for "+ url, "08001");}

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

具体类图如下:
在这里插入图片描述
以日常工作种的告警为例,我们的告警消息可以是通过邮件,短信,电话的方式发送,根据告警的重要程度又可以分为紧急,重要,普通。告警消息可以按照发送方式和紧急程度两个不同的维度进行拆分

先定义一个发送消息的接口和一个桥接的抽象角色

public interface IMessageSender {void sendMessage(String message);
}
public abstract class AbstractMessage {private IMessageSender messageSender;public AbstractMessage(IMessageSender messageSender) {this.messageSender = messageSender;}public void sendMessage(String message) {this.messageSender.sendMessage(message);}
}
public class EmailMessageSender implements IMessageSender{@Overridepublic void sendMessage(String message) {System.out.println("发送邮件"+ message + ".....");}
}public class PhoneMessageSender implements IMessageSender{@Overridepublic void sendMessage(String message) {System.out.println("拨打电话:" + message + ".....");}
}public class SmsMessageSender implements IMessageSender{@Overridepublic void sendMessage(String message) {System.out.println("发送短信" + message + ".....");}
}
public class NormalMessage extends AbstractMessage{public NormalMessage(IMessageSender messageSender) {super(messageSender);}@Overridepublic void sendMessage(String message) {super.sendMessage("普通" + message);}
}
public class UrgencyMessage extends AbstractMessage {public UrgencyMessage(IMessageSender messageSender) {super(messageSender);}@Overridepublic void sendMessage(String message) {super.sendMessage("紧急" + message);}
}

总结

桥接模式适合于以下几个场景:

  1. 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展
  2. 系统不希望使用继承或因为多层次继承导致类的个数急剧增加
  3. 需要在抽象和具体实现之间增加更多的灵活性

上一篇:Java开发 - 消息队列前瞻

下一篇:Canvas

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...