java 桥接模式 实际应用,java桥接模式的应用场景
00-1010 1.什么是桥接模式:2。UML结构图3。代码实现4。JDBC源代码分析-桥接模式1。源代码分析2。源代码类3。对JDBC的看法。
00-1010桥接,顾名思义,是用来连接两个部分的,这样这两个部分就可以互相通信了。桥接模式的作用是桥接分离的抽象部分和实现部分。现实生活中,一件物品搭配不同的配饰,会产生不同的作用和效果。比如赛车,如果配硬胎或者软胎,可以在干路上行驶,但如果要在雨天行驶,就需要配雨胎。这种需要根据不同路面匹配不同轮胎的情况,从软件设计的角度分析,就是一个系统由于自身的逻辑会在两个或者更多的维度上发生变化,而为了应对。桥接模式将系统的抽象部分和实现部分分开,这样它们都可以独立地改变。与以上相对应,赛车的类型可以相对变化,轮胎的类型也可以相对变化,形成交叉关系。最终的结果是,一种赛车可以成功的产生一种结果,一种行为对应一种轮胎。
桥接模式将系统的抽象部分与实现部分分离和解耦,使它们可以独立变化。为了使抽象部分和实现部分独立变化,桥模式用组合关系代替继承关系,抽象部分拥有实现部分的接口对象,这样就可以通过这个接口对象调用具体实现部分的功能。也就是说,桥模式下的桥是单向关系,只有抽象部分可以使用实现部分的对象,而不是相反。
桥接方式符合“开闭原则”,提高了系统的可扩展性。如果在两个变化的维度中任意扩展一个维度,则没有必要修改原系统。并且实现细节对客户来说是不透明的,所以实现细节是可以隐藏的。但是,由于聚合关系是在抽象层次上建立的,因此需要开发人员针对抽象进行编程,这增加了理解和设计系统的难度。
因此,桥接模式一般适用于以下应用场景:
(1)系统需要在组件的抽象角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系,使它们可以通过桥接方式在抽象层次建立关联关系;
当(2)不想使用继承或者系统类的数量因为多级继承而急剧增加时
(3)的类有两个独立的维度,它们都需要扩展。
目录
抽象角色抽象:定义一个抽象接口,包括对实现的角色的引用,抽象角色的方法需要调用实现的角色;扩展抽象角色RefinedAbstraction:抽象角色的子类,一般对抽象部分的方法进行完善和扩展,实现父类中的业务方法,通过组合/聚合关系调用已实现角色中的业务方法。实现的角色实现者:定义具体行为和特征的应用接口,由扩展的抽象角色使用。一般基本操作由实现角色提供,抽象角色在实现一些基本操作的基础上定义业务方法。具体角色的具体实现者:完善具体角色中定义的具体逻辑。
一、什么是桥接模式:
Implementor 接口类:
公共接口实现者{ void operation impl();}ConcreteImplementor 接口实现类:
类公共具体实现者a实现者{ @ override public void operation impl(){//具体实现}}类公共具体实现者b实现者实现者{ @ override public void operation impl(){//具体实现} }Abstraction 抽象类:
公共抽象类抽象{私有实现者实现者;公共抽象(实现者Implementor){ this。实施者=实施者;} public void operation(){ implementor。操作impl();} }RefinedAbstraction 抽象类的具体实现:
trong>
public class RefinedAbstraction extends Abstraction{ public RefinedAbstraction(Implementor implementor) { super(implementor); } public void refinedOperation() { //对 Abstraction 中的 operation 方法进行扩展 }}
看了这段通用代码之后,桥接模式的结构应该就很清楚了,需要注意的是 RefinedAbstraction 根据实际情况是可以有多个的。 当然上面的 UML 类图和通用代码只是最常用的实现方式而已,在实际使用中可能会有其他的情况,比如 Implementor 只有一个类的情况,虽然这时候可以不去创建 Implementor 接口,精简类的层次,但是我建议还是需要抽象出实现部分的接口。
四、JDBC源码解析-桥接模式
Java 中,我们使用 JDBC 连接数据库时,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是使用了桥接模式,JDBC 提供统一接口,每种类型的数据库提供各自的实现,然后由桥接类创建一个连接数据库的驱动,使用某一个数据库的时候只需要切换一下就行。接下来我们就对 JDBC 的源码做下剖析:
通过原生JDBC API连接MySQL数据库,则有如下示例代码:
Class.forName("com.mysql.cj.jdbc.Driver");Connection conn = DriverManager.getConnection("jdbc:mysql://<host>:<port>/<database>");
短短两行代码难以看出桥接模式的结构,下面先对源码进行一定的分析,理解各个类和接口之间的关系:
1、源码分析
(1)Class.forName() 方法:
该方法将返回与给定字符串名的类或接口相关联的 java.lang.Class 类对象,用于在程序运行时动态加载该类或该接口到当前线程中,如果 Class.forName() 加载的是一个类,也会执行类中包含的static { } 静态代码段
(2)com.mysql.cj.jdbc.Driver 类:
MySQL 将具体的 java.sql.Driver 接口的实现放到了 NonRegisteringDriver
中,com.mysql.cj.jdbc.Driver
类仅包含一段静态代码
其中最关键的是静态代码段中的 DriverManager.registerDriver(new Driver())
,它会在客户端调用Class.forName()
方法加载 com.mysql.cj.jdbc.Driver
类的同时被执行,Driver 类自身的一个实例被注册到 DriverManager
(即保存到 DriverManager 的静态字段 registeredDrivers 内),注册过程的源码如下:
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);}
registeredDrivers
静态字段的类型是实现了 List 接口的 CopyOnWriteArrayList
类,它能够保存进一步封装 java.sql.Driver
接口的 DriverInfo 类实例,DriverInfo 类的声明代码如下:
class DriverInfo { final Driver driver; DriverAction da; DriverInfo(Driver driver, DriverAction action) { this.driver = driver; da = action; } // ……}
DriverInfo 还包装了 DriverAction,DriverAction 会在Driver被取消注册时被调用,在 MySQL 的 Driver 在向 DriverManager 进行注册时,DriverAction 被设置为 null
(3)DriverManager 类:
由上面的分析可得,Class.forName() 方法调用后,com.mysql.cj.jdbc.Driver 类被加载,并执行static { } 静态代码段,将 com.mysql.cj.jdbc.Driver 类实例注册到 DriverManager 中。然后,客户端会调用 DriverManager.getConnection() 方法获取一个 Connection 数据库连接实例,该方法的部分源码如下:
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException { // …… 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()); } } // ……}
DriverManager.getConnection() 方法会遍历 registeredDrivers 静态字段,获取字段内保存的每一个 Driver 来尝试响应客户端的数据库连接请求,若所有 Driver 都连接数据库失败,则提示连接失败信息
(4)Connection接口:
Connection 代表和特定数据库的连接会话,能够执行SQL语句并在连接的上下文中返回执行结果。因此,DriverManager.getConnection() 方法返回的 Connection 数据库连接实例根据不同的数据库有不同的实现,MySQL 的 Connection 接口实现关系
2、源码类
对 Driver 和 Connection 进行抽象
桥接模式通过聚合关系代替继承关系,实现抽象化和实现化部分的解耦。以上述 JDBC 在 MySQL 中的简略类图为例,抽象化部分有 DriverManager,实现化部分有 Driver 接口和 Connection 接口。对于不同的数据库,Driver接口和Connection接口都有自己独特的实现类。
但是,和 Driver 接口不同的是,Connection 接口与 DriverManager 类的关系只是联系较弱的依赖关系,并不符合桥接模式的定义和特点。
桥接模式中的实现化角色 (Implementor) 对应上图的 Driver 接口,具体实现化 (Concrete Implementor) 角色对应 MysqlDriver、OracleDriver 和 MariadbDriver,扩展抽象化 (Refined Abstraction) 角色对应 DriverManager,不具有抽象化 (Abstraction) 角色作为扩展抽象化角色的父类
3、对 JDBC 的观点
(1)观点:JDBC采用的是策略模式而不是桥接模式
jdbc是桥接模式还是策略模式?
因为这确实和策略模式十分相似,如果把桥接模式的抽象部分简化来看,不去设计Abstraction,也就是用 Refined Abstraction 代替 Abstraction,那么就类似于策略模式的 Context 来使用接口的对象。
但是,桥接模式和策略模式的目的是不一样的,策略模式属于对象行为模式(描述对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责),它的目的是封装一系列的算法,使得算法可以相互替代,并在程序运行的不同时刻选择合适的算法。而桥接模式属于对象结构模式(描述如何将对象按某种布局组成更大的结构),它的目的是将抽象与实现分离,使它们可以独立变化
因此,从设计的目的来看,JDBC采用的并不是策略模式,在一段程序中数据库驱动并不存在频繁地相互替换
以上就是详解Java设计模式之桥接模式的详细内容,更多关于Java桥接模式的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。