◉◡◉ 您好,欢迎到访伊成个人站!

java设计模式之桥接模式

桥接模式介绍

桥接模式的定义:桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(interface)模式。

【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是”将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。这句话有三个关键词,也就是 抽象化 实现化 脱耦

桥接模式UML类图

“伊成博客”

模式构造

Abstraction:定义抽象接口,拥有一个Implementor类型的对象引用
RefinedAbstraction:扩展Abstraction中的接口定义
Implementor:是具体实现的接口,Implementor和RefinedAbstraction接口并不一定完全一致,实际上这两个接口可以完全不一样Implementor提供具体操作方法,而Abstraction提供更高层次的调用
ConcreteImplementor:实现Implementor接口,给出具体实现

举个例子

使用以上的类图结构,写一个实例demo 。

Abstraction抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 1 public abstract class Abstraction {
2
3 private Implementor imp;
4
5 //约束子类必须实现该构造函数
6 public Abstraction(Implementor imp) {
7 this.imp = imp;
8 }
9
10 public Implementor getImp() {
11 return imp;
12 }
13
14 //自身的行为和属性
15 public void request() {
16 this.imp.doSomething();
17 }
18
19 }

Implementor抽象类

1
2
3
4
5
6
1 public abstract class Implementor {
2
3 public abstract void doSomething();
4 public abstract void doAnything();
5
6 }

ConcreteImplementor具体实现类

1
2
3
4
5
6
7
8
9
10
11
12
 1 public class ConcreteImplementorA extends Implementor {
2
3 @Override
4 public void doSomething() {
5 System.out.println("具体实现A的doSomething执行");
6 }
7
8 @Override
9 public void doAnything() {
10 System.out.println("具体实现A的doAnything执行");
11 }
12 }

RefinedAbstraction 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 1 public class RefinedAbstraction extends Abstraction {
2
3 //覆写构造函数
4 public RefinedAbstraction(Implementor imp) {
5 super(imp);
6 }
7
8 //修正父类行为
9 @Override
10 public void request() {
11 super.request();
12 super.getImp().doAnything();
13 }
14
15 }

Client客户端

1
2
3
4
5
6
7
8
9
1 public class Client {
2
3 public static void main(String[] args) {
4 Implementor imp = new ConcreteImplementorA();
5 Abstraction abs = new RefinedAbstraction(imp);
6 abs.request();
7 }
8
9 }

运行结果
“伊成博客”

桥接模式的优缺点

优点

  • 抽象和实现分离。桥梁模式完全是为了解决继承的缺点而提出的设计模式
  • 优秀的扩展能力
  • 实现细节对客户透明。客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装

缺点

  • 增加系统的理解与设计难度。由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

使用场景

1.一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
2.对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
3.如果你不希望在抽象和实现部分采用固定的绑定关系,可以采用桥接模式,来把抽象和实现部分分开,然后在程序运行期间来动态的设置抽象部分需要用到的具体的实现,还可以动态切换具体的实现。

桥接模式使用案例

1.在JDK中的应用了桥接模式的类。

java.util.logging是JDK自带的日志包,可以将日志输出到文件、内存或者控制台,作用与我们常用的log4j类似。
包中的Handler类和Formatter类在设计上利用了桥接模式,首先看类关系图:
“伊成博客”

2.JDBC中的DriverManager接口也使用了桥接模式。

mysql的Driver类

1
2
3
4
5
6
7
8
9
10
11
12
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类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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类进行桥接的。

小结

1、桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。

2、对于两个独立变化的维度,使用桥接模式再适合不过了。

3、对于”具体的抽象类”所做的改变,是不会影响到客户。


The end.

支付宝打赏 微信打赏