适配器模式
适配器模式 : 就是将一个类的接口变成客户端所期望的另一种接口,使得原本因为接口不匹配而无法一起工作的接口可以正常工作。属于结构型模式
比方说我有一个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{
@Override
public 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;
}
@Override
public int output5V() {
int output = this.ac220VAdaptee.output220V()/44;
System.out.println("转换后的电压为:" + output + "V");
return output;
}
}
接口适配器 : 如果接口定义的方法过多,通过接口适配器可以让我们只实现需要转换的方法(直接实现接口的话就会有很多空实现的方法)
总结
优点 :
- 通过适配器模式,客户端可以使用统一的一套接口,这样实现起来更简洁明了
- 复用了现有的类和代码,减少代码的改动量
使用场景
适配器模式是⽤来做适配的,它使得不兼容的接⼝转换为可兼容的接⼝,让原本由于接⼝不兼容⽽不能⼀起⼯作的类可以⼀起⼯作。常用于补偿接口设计上的缺陷。
一般使用场景如下:
- 封装有缺陷的接⼝设计
- 统⼀多个类的接⼝设计
- 替换依赖的外部系统
- 兼容⽼版本接⼝
- 适配不同格式的数据
桥接模式
桥接模式是将抽象部分与他的具体实现分离,使其都可以独立的发生变化而互不干扰,属于结构型模式。
桥接模式的核心在于解耦抽象和实现。
这里的“抽象”,指的并⾮“抽象类”或“接⼝”,⽽是被抽象出来的⼀套“类库”,它只包含⾻架代码,真正的业务逻辑需要委派给定义中的“实现”来完成。⽽定义中的“实现”,也并⾮“接⼝的实现类”,⽽是⼀套独⽴的“类库”。“抽象”和“实现”独⽴开发,通过对象之间的组合关系,组装在⼀起
经典的桥接模式的应用就是我们的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{
@Override
public void sendMessage(String message) {
System.out.println("发送邮件"+ message + ".....");
}
}
public class PhoneMessageSender implements IMessageSender{
@Override
public void sendMessage(String message) {
System.out.println("拨打电话:" + message + ".....");
}
}
public class SmsMessageSender implements IMessageSender{
@Override
public void sendMessage(String message) {
System.out.println("发送短信" + message + ".....");
}
}
public class NormalMessage extends AbstractMessage{
public NormalMessage(IMessageSender messageSender) {
super(messageSender);
}
@Override
public void sendMessage(String message) {
super.sendMessage("普通" + message);
}
}
public class UrgencyMessage extends AbstractMessage {
public UrgencyMessage(IMessageSender messageSender) {
super(messageSender);
}
@Override
public void sendMessage(String message) {
super.sendMessage("紧急" + message);
}
}
总结
桥接模式适合于以下几个场景:
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展
- 系统不希望使用继承或因为多层次继承导致类的个数急剧增加
- 需要在抽象和具体实现之间增加更多的灵活性