桥接模式
示例代码git地址:https://gitee.com/zyxscuec/Design-pattern.git
(1)概念
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
(2)适用场景
1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。
(3)代码示例
我们有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircle、GreenCircle。Shape 是一个抽象类,将使用 DrawAPI 的对象。BridgePatternDemo,我们的演示类使用 Shape 类来画出不同颜色的圆。
创建桥接实现接口。
package com.alibaba.design.bridgepattern;
/**
* @author zhouyanxiang
* @create 2020-08-2020/8/2-17:37
*/
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
创建实现了 DrawAPI 接口的实体桥接实现类。
- GreenCircle
package com.alibaba.design.bridgepattern;
/**
* @author zhouyanxiang
* @create 2020-08-2020/8/2-17:38
*/
public class GreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: green, radius: " + radius +", x: " +x+", "+ y +"]");
}
}
- RedCircle
package com.alibaba.design.bridgepattern;
/**
* @author zhouyanxiang
* @create 2020-08-2020/8/2-17:38
*/
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: red, radius: " + radius +", x: " +x+", "+ y +"]");
}
}
使用 DrawAPI 接口创建抽象类 Shape。
package com.alibaba.design.bridgepattern;
/**
* @author zhouyanxiang
* @create 2020-08-2020/8/2-17:39
*/
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
创建实现了 Shape 接口的实体类。
package com.alibaba.design.bridgepattern;
/**
* @author zhouyanxiang
* @create 2020-08-2020/8/2-17:39
*/
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
drawAPI.drawCircle(radius,x,y);
}
}
使用 Shape 和 DrawAPI 类画出不同颜色的圆。
package com.alibaba.design.bridgepattern;
/**
* @author zhouyanxiang
* @create 2020-08-2020/8/2-17:40
*/
public class BridgePatternDemo {
public static void main(String[] args) {
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
}
}
输出结果:
(4)该模式在源码中的体现
如果从桥接模式来看,java.sql.Driver就是一个接口
下面可以有MySQL的Driver,Oracle的Driver,这些就可以当做实现接口类。那么我们现在来看看MySQL中的Driver类
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!");
}
}
}
特别简短的代码,其实只调用了DriverManager中的registerDriver方法来注册驱动。当驱动注册完成后,我们就会开始调用DriverManager中的getConnection方法了
public class DriverManager {
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
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());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
}
}
上面是简化的代码,可以看到需要返回的是Connection对象。在Java中通过Connection提供给各个数据库一样的操作接口,这里的Connection可以看作抽象类。可以说我们用来操作不同数据库的方法都是相同的,不过MySQL有自己的ConnectionImpl类,同样Oracle也有对应的实现类。这里Driver和Connection之间是通过DriverManager类进行桥接的,不是像我们上面说的那样用组合关系来进行桥接。
(5)桥接模式的优缺点
-
优点:
1、抽象和实现的分离。
2、优秀的扩展能力。
3、实现细节对客户透明。
-
**缺点:**桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。