文章目录
- 第八章_桥接模式
- 1.介绍
-
- 1.1定义
- 1.2使用场景
-
- 1.2.1生活场景
- 1.2.2java场景
- 1.3角色
- 2.举例
-
- 2.1生活举例
- 2.2JDK源码举例
-
- 2.2.1总览
- 2.2.2Driver
- 2.2.3DriverManager
- 3.优缺点
- 4.桥接模式与适配器模式
-
- 4.1桥接模式和适配器模式的区别和联系
- 4.2适配器与桥接模式的联合
第八章_桥接模式服务器托管网
顺口溜:适装桥组享代外
1.介绍
1.1定义
桥接模式:(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
关键代码:抽象类依赖实现类。
1.2使用场景
1.2.1生活场景
设计一种遥控车玩具,这种遥控车可以 在不同的地形上行驶,比如在公路上、草地上和沙滩上。同时,这些遥控车还可以有不同的轮胎类型,比如普通轮胎、越野轮胎和雪地轮胎。
1.2.2java场景
JDBC驱动程序;
AWT中的Peer架构;
银行日志管理:
- 格式分类:操作日志、交易日志、异常日志
- 距离分类:本地记录日志、异地记录日志
人力资源系统中的奖金计算模块:
- 奖金分类:个人奖金、团体奖金、激励奖金。
- 部门分类:人事部门、销售部门、研发部门。
OA系统中的消息处理:
- 业务类型:普通消息、加急消息、特急消息
- 发送消息方式:系统内消息、手机短信、邮件
1.3角色
- 抽象类(Abstraction):定义抽象接口,通常包含对实现接口的引用。
- 抽象具体类(Refined Abstraction):对抽象的扩展,可以是抽象类的子类或具体实现类。
- 实现(Implementor):定义实现接口,提供基本操作的接口。
- 具体实现(Concrete Implementor):实现实现接口的具体类。
- 桥接类:聚合具体实现类和实现接口的具体类
2.举例
2.1生活举例
在设计一种遥控车玩具,这种遥控车可以在不同的地形上行驶,比如在公路上、草地上和沙滩上。
同时,这些遥控车还可以有不同的轮胎类型,比如普通轮胎、越野轮胎和雪地轮胎。
在这个例子中,遥控车的行驶地形可以看作是抽象部分,而==轮胎类型可以看作是实现部分==。采用桥接模式的设计,你可以将遥控车的行驶地形和轮胎类型进行解耦,使它们可以独立地变化和扩展。
行驶地形抽象类 TerrainType:定义了遥控车行驶地形的抽象方法
// 抽象类 TerrainType:行驶地形
abstract class TerrainType {
//行驶
abstract void setTerrain();
}
// 具体类 OnRoadTerrain
class OnRoadTerrain extends TerrainType {
@Override
void setTerrain() {
System.out.println("在公路上行驶");
}
}
// 具体类 OffRoadTerrain
class OffRoadTerrain extends TerrainType {
@Override
void setTerrain() {
System.out.println("在草地上行驶");
}
}
// 具体类 BeachTerrain
class BeachTerrain extends TerrainType {
@Override
void setTerrain() {
System.out.println("在沙滩上行驶");
}
}
轮胎类型接口TireType:定义了遥控车轮胎类型的抽象方法
// 接口 TireType:轮胎类型
interface TireType {
void setTire();
}
// 具体类 StandardTire
class StandardTire implements TireType {
@Override
public void setTire() {
System.out.println("使用普通轮胎");
}
}
// 具体类 OffRoadTire
class OffRoadTire implements TireType {
@Override
public void setTire() {
System.out.println("使用越野轮胎");
}
}
// 具体类 SnowTire
class SnowTire implements TireType {
@Override
public void setTire() {
System.out.println("使用雪地轮胎");
}
}
桥接类:
RemoteControlCar
将具体的行驶地形类和轮胎类型类进行桥接,通过组合关系将它们连接起来。这样,你就可以根据不同的需求组合不同的行驶地形和轮胎类型,灵活地创建出适应不同场景的遥控车玩具。
// 桥接类 RemoteControlCar
class RemoteControlCar {
private TerrainType terrain; //行驶地形抽象类
private TireType tire; //轮胎类型接口
public RemoteControlCar(TerrainType terrain, TireType tire) {
this.terrain = terrain;
this.tire = tire;
}
public void drive() {
this.terrain.setTerrain();
}
public void changeTire() {
this.tire.setTire();
}
}
// 测试桥接模式
public class BridgePatternExample {
public static void main(String[] args) {
TerrainType onRoad = new OnRoadTerrain();
TerrainType offRoad = new OffRoadTerrain();
TerrainType beach = new BeachTerrain();
TireType standard = new StandardTire();
TireType offRoadTire = new OffRoadTire();
TireType snowTire = new SnowTire();
RemoteControlCar car1 = new RemoteControlCar(onRoad, standard);
RemoteControlCar car2 = new RemoteControlCar(offRoad, offRoadTire);
RemoteControlCar car3 = new RemoteControlCar(beach, snowTire);
car1.drive();
car1.changeTire();
car2.drive();
car2.changeTire();
car3.drive();
car3.changeTire();
}
}
2.2JDK源码举例
MySQL 的 Connection 接口实现的是 java.sql.Connection 接口
同时 Oracle 数据库也一样可以实现 java.sql.Connection 接口,他们向下都可以有更多的实现子类。
然后 DriverManager 相当于桥接模块,聚合 java.sql.Connection 接口和Driver接口,供客户端调用。
2.2.1总览
public class JDBCTest {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
//STEP 1: Register JDBC driver //注册mysql的Driver,即com.mysql.jdbc.Driver类就会被加载,注册到registeredDrivers这个特殊的list中
Class.forName("com.mysql.jdbc.Driver");
//STEP 2: Open a connection //获取mysql规范的连接,从registeredDrivers中来获取已经注册的driver
conn = DriverManager.getConnection("jdbc:mysql://192.168.108.145/test", "root", "root");
//STEP 3: Execute a query
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from userinfo limit 1");
//STEP 4: Get results
while(rs.next()){
System.out.println(rs.getString("id"));
}
rs.close();
}catch(SQLException se){
......
}服务器托管网//end try
}
}
2.2.2Driver
mysql的Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// Register ourselves with the DriverManager
static {
try {
//进行了驱动的注册
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
//Construct a new driver and register it with DriverManager
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
2.2.3DriverManager
DriverManager相当于桥接类,提供Connection,从registeredDrivers中来获取已经注册的driver,聚合起来获取Connection
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");
}
}
3.优缺点
优点:
(1)实现了抽象和实现部分的分离
桥接模式分离了抽象部分和实现部分,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,分别定义接口,这有助于系统进行分层设计,从而产生更好的结构化系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。(2)可动态的切换实现
由于桥接模式实现了抽象和实现的分离,所以在实现桥接模式时,就可以实现动态的选择和使用具体的实现。(3)更好的可扩展性
由于桥接模式把抽象部分和实现部分分离了,从而分别定义接口,这就使得抽象部分和实现部分可以分别独立扩展,而不会相互影响,大大的提供了系统的可扩展性。(4)实现细节对客户端透明,可以对用户隐藏实现细节。
缺点:
(1)桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
(2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性。
4.桥接模式与适配器模式
4.1桥接模式和适配器模式的区别和联系
适配器模式和桥接模式都是 间接引用对象,因此可以使系统更灵活,在实现上都涉及从自身以外的一个接口向被引用的对象发出请求。
两种模式的区别在于使用场合的不同,适配器模式主要解决两个已经有接口间的匹配问题,这种情况下被适配的接口的实现往往是一个黑匣子。我们不想,也不能修改这个接口及其实现。同时也不可能控制其演化,只要相关的对象能与系统定义的接口协同工作即可。适配器模式经常被用在与第三方产品的功能集成上,采用该模式适应新类型的增加的方式是开发针对这个类型的适配器,如下图所示:
桥接模式则不同,参与桥接的接口是稳定的,用户可以扩展和修改桥接中的类,但是不能改变接口。桥接模式通过接口继承实现或者类继承实现功能扩展。如下图所示:
按照GOF的说法,桥接模式和适配器模式用于设计的不同阶段,桥接模式用于设计的前期,即在设计类时将类规划为逻辑和实现两个大类,是他们可以分别精心演化;而适配器模式用于设计完成之后,当发现设计完成的类无法协同工作时,可以采用适配器模式。然而很多情况下在设计初期就要考虑适配器模式的使用,如涉及到大量第三方应用接口的情况。
4.2适配器与桥接模式的联合
在实际应用中,桥接模式经常和适配器模式同时出现,如下图所示:
这种情况经常出现在需要其他系统提供实现方法时,一个典型的例子是工业控制中的数据采集。不同工控厂家提供的底层数据采集接口通常不同,因此在做上层软件设计无法预知可能遇到何种接口。为此需要定义一个通用的采集接口,然后针对具体的数据采集系统开发相应的适配器。数据存储需要调用数据采集接口获得的数据,而数据可以保存到关系数据库、实时数据库或者文件中,。数据存储接口和数据采集结构成了桥接,如下图所示:
同样的结构也经常出现在报表相关的应用中,报表本身结构和报表输出方式完全可以分开,如下图所示:
如上图所示,报表输出可以单独抽象出来与报表的具体形式分开。但报表输出又依赖于具体的输出方式,如果需要输出为PDF格式,则需要调用与PDF相关的API,而这是设计所无法控制的,因此这里需要适配器模式。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net