桥接模式
定义
形象局部和具体实现局部拆散
- 让他们能够独立的变动
- 通过组合的形式建设两个类之间的关系而不是继承
结构型模式
生存中的场景
桥
连贯了两个维度的货色
网络连接
桥接模式
虚构网卡和物理网卡连在一起
通用的写法
外面有几个要害角色
Abstraction——抽象化角色
它的主要职责是定义出该角色的行为,同时保留一个对实现化角色的援用,该角色个别是抽象类。
Implementor——实现化角色
它是接口或者抽象类,定义角色必须的行为和属性。
RefinedAbstraction——修改抽象化角色
它援用实现化角色对抽象化角色进行修改。
ConcreteImplementor——具体实现化角色
它实现接口或抽象类定义的办法和属性。
public class RefinedAbstraction extends Abstraction {public RefinedAbstraction(IImplementor implementor) {super(implementor);
}
@Override
public void operation() {super.operation();
System.out.println("refined operation");
}
}
public class Test {public static void main(String[] args) {
// 定义一个实现化角色
Implementor imp = new ConcreteImplementor1();
// 定义一个抽象化角色
Abstraction abs = new RefinedAbstraction(imp);
// 执行行文
abs.request();}
}
}
测试用例
public class Test {public static void main(String[] args) {
// 来一个实现化角色
IImplementor imp = new ConcreteImplementorA();
// 来一个抽象化角色,聚合实现
Abstraction abs = new RefinedAbstraction(imp);
// 执行操作
abs.operation();}
}
各位可能要问,为什么要减少一个构造函数?答案是为了揭示子类,你必须做这项工作,指定实现者,特地是曾经明确了实现者,则尽量清晰明确地定义进去。
案例
形象工厂的问题
以前咱们在写形象工厂模式的时候是这样来做的
从上图能够看到,形象工厂是用来帮咱们创立不同的课程有雷同的共性的问题
例如 Java 课程和 python 课程都须要记笔记和录制视频,所以咱们能够通过形象工厂 CourseFactory
来创立,然而这种形式是通过继承来做的,并且咱们的产品和工厂都绑的太死了,所以咱们能够通过桥梁模式来帮咱们解绑,用组合和聚合来代理继承。
桥接模式来松绑
能够看到通过 AbstractCourse
给笔记和视频搭建了一个桥梁
public class AbstractCourse implements ICourse {
private INote note;
private IVideo video;
public void setNote(INote note) {this.note = note;}
public void setVideo(IVideo video) {this.video = video;}
@Override
public String toString() {
return "AbstractCourse{" +
"note=" + note +
", video=" + video +
'}';
}
}
这样咱们就通过桥梁 AbstractCourse
来帮咱们建设起了笔记和视频的分割
音讯
咱们能够通过桥接模式来构建音讯类型和发送音讯的关系
public abstract class AbastractMessage {
private IMessage message;
public AbastractMessage(IMessage message) {this.message = message;}
void sendMessage(String message, String toUser) {this.message.send(message, toUser);
}
}
应用如下
public class Test {public static void main(String[] args) {IMessage message = new SmsMessage();
AbastractMessage abastractMessage = new NomalMessage(message);
abastractMessage.sendMessage("加班申请","王总");
message = new EmailMessage();
abastractMessage = new UrgencyMessage(message);
abastractMessage.sendMessage("加班申请","王总");
}
}
感觉桥接模式和装璜者有点像
源码中的桥接模式
JDBC
初始化
当咱们执行如下代码的时候
/**
* JDBC 规定了这些步骤,然而没有具体实现。<br>
* 这些具体实现跟形象步骤怎么衔接起来呢?通过 DriverManager 来桥接 <br/>
*/
try {
//1. 加载驱动 ---> 建设桥
Class.forName("com.mysql.jdbc.Driver"); // 反射机制加载驱动类,执行动态块代码,调用 Driver 的构造方法 <br>
// 2. 获取连贯 Connection
// 主机: 端口号 / 数据库名 --> 后面建设桥了 这里就用
Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/ds0", "admin", "admin");
// 3. 失去执行 sql 语句的对象 Statement
Statement stmt = conn.createStatement();
// 4. 执行 sql 语句,并返回后果
ResultSet rs = stmt.executeQuery("select *from table");
}catch (Exception e){e.printStackTrace();
}
}
会执行对应的动态代码块
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!");
}
}
}
java.sql.DriverManager#registerDriver(java.sql.Driver, java.sql.DriverAction)
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();}
println("registerDriver:" + driver);
}
能够看到帮咱们把对应的驱动对象包装了一层变成了 DriverInfo,并且寄存起来了
获取连贯
java.sql.DriverManager#getConnection(java.lang.String, java.util.Properties, java.lang.Class<?>)
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {println("trying" + aDriver.driver.getClass().getName());
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());
}
}
能够看到获取连贯的时候,从之前曾经存起来的驱动外面来获取连贯,并且返回进来
通过一个汇合帮咱们构建了一个桥梁
总结
实用场景
-
形象和实现拆散
-
可能帮咱们解决继承的问题
- 单继承
- 裸露过多属性给子类
- 或者因为多继承导致类的个数剧增
- 无奈喜更细化场景
-
-
优良的扩大能力
- 咱们桥梁都是面向形象的
- 减少桥也是可能疾速减少的
-
一个类存在两个或者多个独立变动的维度
- 这两个维度都须要独立进行扩大
-
接口或者抽象类不稳固的场景
- 如果通过继承,批改的会十分多
-
重用性要求高的场景
- 继承会导致受到父类的限度,粒度不会太细
长处
-
拆散了形象局部和具体实现局部
- 咱们通过桥梁能够解决继承的毛病
- 进步了零碎的扩大
- 合乎开闭准则
- 合乎合成复用准则
毛病
- 减少了零碎的了解和设计难度
- 须要正确的识别系统中两个独立变动的维度
和其余设计模式的关联
桥接模式更加重视模式
桥接模式更加重视连贯,适配器模式更加重视适配
留神
不能说继承不好,它十分好,然而有毛病,咱们能够取长补短,对 于比拟明确不发生变化的,则通过继承来实现;若不能确定是否会产生 变动的,那就认为是会发生变化,则通过桥梁模式来解决,这才是一个完满的世界。
问题
想一下桥接模式怎么不应用继承的状况下怎么把两个维度联合起来的呢,能够举个例子或者对于上述案例加以阐明也行
-
在 java 当中除了继承就是组合和聚合了
-
如果通过继承
- 那么子类会持有很多父类的货色,并且很容易重写父类的办法,违反里氏替换准则
-
如果通过组合和聚合
- 子类想要领有办法很简略,桥梁打过来,取得这个办法就行了
- 例如
咱们团体组要性能是赚钱,赚钱须要先生产而后销售
crop
以后是抽象类具体的生产产品,和销售交给旗下的子公司HouseCrop
卖房子的和IpodCorp
卖 Ipod 的来卖钱 -
public class Client {public static void main(String[] args) {System.out.println("------- 房地产公司是这样运行的 ------"); // 先找到我的公司 HouseCorp houseCorp = new HouseCorp(); // 看我怎么挣钱 houseCorp.makeMoney(); System.out.println("\n"); System.out.println("------- 服装公司是这样运行的 -------"); ClothesCorp clothesCorp = new ClothesCorp(); clothesCorp.makeMoney();} }
很显著当初有一个问题,咱们的工厂和具体的产品绑的太死了,比如说然而咱们不想用形象工厂那么能够通过桥接模式来解决,用组合来把他们构建起来
能够让山寨的工厂也能生产房子和 ipod 其实就是通过桥梁模式让咱们的具体公司可能和产品关联起来
-
咱们平时写 JDBC 代码的时候有没有去推敲一下这句代码
DriverManager.getConnection("jdbc:mysql://localhost:3306/ds0", "admin", "admin");
是怎么拿到 mysqlConnection。
我的笔记仓库地址 gitee 快来给我点个 Star 吧