当一件事情有多个维度的变化的可能性时,桥接模式就派上用场了。如下图,一个消息系统中,发送消息的方式 和消息的类型都是可以独立变化的,两个维度的变化互不影响,比如消息可以通过邮件发送,而消息类型可以使普通消息,加急消息, 还可以通过短信来发送普通消息或者加急消息,这样的系统就很适合桥接模式来实现。
每个独立变化的维度分别抽象出一个独立的接口,然后通过一个“桥”来持有这两个维度的顶层接口,来实现自己的业务逻辑:
消息发送接口:
package com.lchtest.pattern.birdge.message;
public interface IMessage {
// 发送消息:消息内容和接收者
void send(String msg, String reciever);
}
消息发送方式:
package com.lchtest.pattern.birdge.message;
/**
* 消息发送方式1 邮件消息
*/
public class EmailMessage implements IMessage {
@Override
public void send(String msg, String reciever) {
System.out.println(String.format("使用邮件发送消息 %s 给 %s ", msg, reciever));
}
}
package com.lchtest.pattern.birdge.message;
/**
* 消息发送方式2-系统消息
*/
public class SystemMessage implements IMessage {
@Override
public void send(String msg, String reciever) {
System.out.println(String.format("使用内部消息系统发送消息 %s 给 %s ", msg, reciever));
}
}
建立桥接:
package com.lchtest.pattern.birdge.message;
public abstract class AbstractMessage {
private IMessage message;
public AbstractMessage(IMessage message) {
this.message = message;
}
void send(String msg, String reciever){
this.message.send(msg, reciever);
}
}
消息级别:
package com.lchtest.pattern.birdge.message;
/**
* 消息紧急程度- 普通消息
*/
public class NormalMessage extends AbstractMessage {
public NormalMessage(IMessage message) {
super(message);
}
@Override
void send(String msg, String reciever) {
// 普通消息,直接调用父类方法发送消息
super.send(msg, reciever);
}
}
package com.lchtest.pattern.birdge.message;
/**
* 消息紧急程度- 紧急消息
*/
public class UrgencyMessage extends AbstractMessage {
public UrgencyMessage(IMessage message) {
super(message);
}
@Override
void send(String msg, String reciever) {
// 加急消息特殊处理
String urgencyMessage = "【加急】" + msg;
super.send(urgencyMessage, reciever);
}
}
测试类:
package com.lchtest.pattern.birdge.message;
public class Test {
public static void main(String[] args) {
IMessage message = new EmailMessage();
AbstractMessage abstractMessage = new NormalMessage(message);
AbstractMessage urgencyMessage = new UrgencyMessage(message);
abstractMessage.send("便携式测温仪采购申请","采购经理");
urgencyMessage.send("紧急事务处理款项申请","老板");
message = new SystemMessage();
abstractMessage = new UrgencyMessage(message);
abstractMessage.send("便携式测温仪采购申请","公司老板");
}
}
以JDBC API的抽象维度和实现维度 为例,来说明源码中的桥接是怎么使用的
package com.lchtest.pattern.birdge;
import lombok.Data;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JDBCBridge {
public static void main(String[] args) {
try {
// Driver类没有实现,只是一个抽象
Class.forName("com.mysql.jdbc.Driver");
// DriverManager就是这个桥,把目标数据库的driver进行了实现
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "lchadmin");
String sql = "select * from user where id= ?";
PreparedStatement pstmt = connection.prepareStatement(sql); // 预编译sql
pstmt.setInt(1, 1); // 参数下标1,参数值1
pstmt.execute(); // 执行查询
ResultSet rs = pstmt.getResultSet(); // 获取结果集
while (rs.next()){
User user = new User();
user.setId(rs.getInt("id"));
user.setAge(rs.getInt("age"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
System.out.println(user.toString()); //User(id=1, username=lisi, password=12345, age=19)
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Data
class User {
private int id;
private String username;
private String password;
private int age;
}
/*CREATE TABLE `user` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) NOT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4*/
Class.forName代码的作用
Class.forName(“com.mysql.jdbc.Driver”) 加载Driver类的时候就会执行如下的静态代码块
最终调用了下面的方法:
// List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
代码的核心就是把Driver的实例放到了registeredDrivers 这个list里面去了,而不管是哪种数据库的Driver实例,都给封装成了DriverInfo对象,
DriverManager.getConnection的底层逻辑
getConnection核心逻辑如下,遍历那个registeredDrivers ,调用driver自己的connect方法,而这个driver就是之前的com.mysql.jdbc.Driver 的实例,
调用的connect是 driver的父类NonRegisteringDriver中的connect方法,返回的Connection对象也是实现了java.sql.Connection接口的实例。