设计模式 -- 桥接模式(Bridge)
Posted Hello
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 -- 桥接模式(Bridge)相关的知识,希望对你有一定的参考价值。
写在前面的话:读书破万卷,编码如有神
--------------------------------------------------------------------
主要内容包括:
- 初始桥接模式,包括: 定义、结构和说明、参考实现
- 体会桥接模式,包括: 场景问题、不用模式的解决方案、使用模式的解决方案
- 理解桥接模式,包括: 认识桥接模式、谁来桥接、典型例子-JDBC、广义桥接-Java中无处不在桥接、桥接模式的优缺点
- 思考桥接模式,包括: 桥接模式的本质、对设计原则的体现、何时选用
参考内容:
1、《研磨设计模式》 一书,作者:陈臣、王斌
---------------------------------------------------------------------
1、初始化桥接模式
1.1、定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
1.2、结构和说明
说明:
- Abstraction: 抽象部分的接口。通常在这个对象中,要维护一个实现部分的对象引用,在抽象对象里面的方法,需要调用实现部分的对象来完成。
- RefinedAbstraction:扩展抽象部分的接口。通常在这些对象中,定义根实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法。
- Implementor: 定义实现部分的接口。这个接口不用和Abstraction中的方法一致,通常是由Implementor接口提供基本的操作,而Abstraction里面定义的是基于这些基本操作的业务方法,也就是说Abstraction定义了基于这些基本操作的较高层次的操作。
- ConcreteImplementor: 真正实现Implementor接口的对象。
桥接模式通过引入实现的接口,把实现部分从系统中分离出去。那么,抽象这边持有一个具体的实现部分的实例就可以使用具体的实现了。
1.3、参考实现
1 /** 2 * 定义实现部分的接口,可以与抽象部分接口的方法不一样 3 */ 4 public interface Implementor { 5 6 /** 7 * 实现抽象部分需要的某些具体功能 8 */ 9 public void operationImpl(); 10 } 11 12 /** 13 * 定义抽象部分的接口 14 */ 15 public abstract class Abstraction { 16 17 //持有一个实现部分的对象 18 private Implementor impl; 19 20 /** 21 * 构造方法 22 * @param impl 实现部分的对象 23 */ 24 public Abstraction(Implementor impl){ 25 this.impl = impl; 26 } 27 28 /** 29 * 实现一定的功能,可能需要转调实现部分的具体方法 30 */ 31 public void operation(){ 32 this.impl.operationImpl(); 33 } 34 } 35 36 /** 37 * 真正的具体实现对象 38 */ 39 public class ConcreteImplementorA implements Implementor { 40 41 @Override 42 public void operationImpl() { 43 //真正的实现 44 } 45 } 46 47 /** 48 * 真正的具体实现对象 49 */ 50 public class ConcreteImplementorB implements Implementor { 51 52 @Override 53 public void operationImpl() { 54 //真正的实现 55 } 56 } 57 58 /** 59 * 扩充由Abstraction定义的接口功能 60 */ 61 public class RefinedAbstraction extends Abstraction { 62 63 public RefinedAbstraction(Implementor impl) { 64 super(impl); 65 } 66 67 /** 68 * 实现一定的功能 69 */ 70 public void otherOperation(){ 71 72 } 73 }
---------------------------------------------------------------------
2、体会桥接模式
2.1、发送提示消息
考虑这样一个实际功能: 发送提示消息。从业务上看,提示消息分为:普通消息、加急消息、特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是在消息上添加加急,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促; 从发送消息的手段上看,又有系统内短消息、手机短消息、邮件消息等。
2.2、不用模式的解决方案
(1)简化版本
先考虑一个简化版本,消息只发送普通消息,发送方式有:系统内短消息、邮件消息两种。
由于发送普通消息会有两种不同的实现方式,为了让外部统一操作,因此,把消息设计成接口,然后由两个不同的实现类分别实现系统内短消息方式和邮件发送消息的方式。
系统结构如下图:
示例代码:
1 /** 2 * 消息的统一接口 3 */ 4 public interface Message { 5 /** 6 * 发送消息 7 * @param message 要发送的消息内容 8 * @param toUser 消息发送的目的人员 9 */ 10 public void send(String message,String toUser); 11 } 12 13 /** 14 * 以站内短消息的方式发送普通消息 15 */ 16 public class CommonMessageSMS implements Message { 17 18 @Override 19 public void send(String message, String toUser) { 20 System.out.println("使用站内短消息的方式,发送消息: " + message + ",给"+toUser); 21 } 22 } 23 24 /** 25 * 以Email的方式发送普通消息 26 */ 27 public class CommonMessageEmail implements Message{ 28 29 @Override 30 public void send(String message, String toUser) { 31 System.out.println("使用Email消息的方式,发送消息: " + message + ",给"+toUser); 32 } 33 }
(2)实现发送加急消息
接着上面的实现,添加发送加急消息的功能,也有两种发送的方式,同样是站内短消息和Email的方式。(加急消息会自动在消息上添加加急,然后再发送消息,另外加急消息会提供监控的方法,让客户端可以随时查看加急消息的处理进度。因此加急消息需要扩展一个新的接口,除了基本的发送消息的功能,还需要添加监控的功能。)
系统结构如下图:
示例代码:
1 /** 2 * 加急消息的抽象接口 3 */ 4 public interface UrgencyMessage extends Message { 5 6 /** 7 * 监控某消息的处理过程 8 * @param messageId 被监控的消息的编号 9 * @return 10 */ 11 public Object watch(String messageId); 12 } 13 14 /** 15 * 以站内短消息的方式发送加急消息 16 */ 17 public class UrgencyMessageSMS implements UrgencyMessage { 18 19 @Override 20 public void send(String message, String toUser) { 21 message = "[加急]:" + message; 22 System.out.println("使用站内短消息的方式,发送消息: " + message + ",给"+toUser); 23 } 24 25 @Override 26 public Object watch(String messageId) { 27 //获取相应的数据,组织成监控的数据对象,然后返回 28 return null; 29 } 30 } 31 32 /** 33 * 以站内Email的方式发送加急消息 34 */ 35 public class UrgencyMessageEmail implements UrgencyMessage { 36 37 @Override 38 public void send(String message, String toUser) { 39 message = "[加急]:" + message; 40 System.out.println("使用Email消息的方式,发送消息: " + message + ",给"+toUser); 41 } 42 43 @Override 44 public Object watch(String messageId) { 45 //获取相应的数据,组织成监控的数据对象,然后返回 46 return null; 47 } 48 }
2.3、有何问题
看了上面的实现,好像也能满足基本的功能要求,可是这么实现好不好呢?有没有什么问题呢?
下面继续添加新的功能:
(1)添加特急消息的处理(特急需要需要添加一个催促功能)
系统结构如下图:
观察上面的系统结构示意图,会发现一个很明显的问题,那就是通过这种继承的方式来扩展消息处理,会非常不方便。会看到在实现加急消息处理的时候,必须要实现站内短消息和Email两种处理方式;而在实现特急消息处理的时候,又必须实现站内短消息和Email这两种处理方式。
这就意味着,以后每扩展一下消息处理,都必须要实现这两种处理方式,是不是很痛苦?如果再要添加新的消息发送方式呢?
(2)添加发送手机消息的处理方式
根据目前的实现来看,如果要添加一种新的发送消息的方式,是需要在每一种抽象的具体实现中,都要添加发送手机消息的处理。也就是说,发送普通消息、加急消息、特急消息的处理,都可以通过手机来发送。
系统结构如下图:
把这个问题总结一下:采用通过继承来扩展的实现方式,有个明显的缺点,扩展消息的种类不太容易。不同种类的消息具有不同的业务,在这种情况下每个种类的消息,都需要实现所有不同的消息发送方式。更可怕的是,如果要加入新的消息发送方式或者新的消息类型的话,需要修改的地方太多了。
2.4、使用桥接模式来解决问题
(1)解决思路
分析上面的示例,可以看出示例的变化具有两个维度,一个维度是抽象的消息这边,包括:普通消息、加急消息、特急消息;另一个维度是在具体的消息发送方式上,包括:站内短消息、E-mail消息、手机消息。这两个维度一共可以组合出9种不同的可能性来。
它们的关系如下图:
(ps:现在出现问题的根本原因,就在于消息的抽象和实现是混杂在一起的,这就导致一个维度的变化会引起另一个维度进行相应的变化,从而使得程序扩展起来非常困难。)
要解决这个问题,就必须把这两个维度分开,也就是将抽象部分和实现部分分开,让他们相互独立,这样就可以实现独立变化了。
(2)示例代码
使用桥接模式来重写实现示例,首要任务就是要把抽象部分和实现部分分离出来,分析要实现的功能。抽象部分就是各个消息的类型所对应的功能,而实现部分就是各种发送消息的方式。
(a)先从简单的功能开始
消息类型:普通消息、加急消息; 发送方式: 站内短消息、E-mail。
使用桥接模式来实现这些功能的程序结构如下图:
示例代码如下:
1 (1)实现部分定义的接口 2 /** 3 * 实现发送消息的统一接口 4 */ 5 public interface MessageImplementor { 6 7 /** 8 * 发送消息 9 * @param message 要发送的消息内容 10 * @param toUser 消息发送的目的人员 11 */ 12 public void send(String message,String toUser); 13 } 14 (2)抽象部分定义的接口 15 /** 16 * 抽象的消息对象 17 */ 18 public abstract class AbstractMessage { 19 20 /** 21 * 持有一个实现部分的对象 22 */ 23 protected MessageImplementor impl; 24 25 /** 26 * 构造方法 27 * @param impl 28 */ 29 public AbstractMessage(MessageImplementor impl){ 30 this.impl = impl; 31 } 32 33 /** 34 * 发送消息,转调实现部分的功能 35 * @param message 要发送的消息内容 36 * @param toUser 消息发送的目的人员 37 */ 38 public void sendMessage(String message,String toUser){ 39 this.impl.send(message, toUser); 40 } 41 } 42 (3)站内短消息的实现 43 /** 44 * 以站内短消息的方式发送消息 45 */ 46 public class MessageSMS implements MessageImplementor { 47 48 @Override 49 public void send(String message, String toUser) { 50 System.out.println("使用站内短消息的方式,发送消息: " + message + ",给"+toUser); 51 } 52 } 53 54 (4)Email方式的实现 55 /** 56 * 以Email的方式发送消息 57 */ 58 public class MessageEmail implements MessageImplementor { 59 60 @Override 61 public void send(String message, String toUser) { 62 System.out.println("使用Email消息的方式,发送消息: " + message + ",给"+toUser); 63 } 64 } 65 66 (5)普通消息的实现 67 /** 68 * 普通消息的实现 69 */ 70 public class CommonMessage extends AbstractMessage { 71 72 public CommonMessage(MessageImplementor impl) { 73 super(impl); 74 } 75 76 @Override 77 public void sendMessage(String message, String toUser) { 78 //对于普通消息,什么都不干,直接调用父类的方法,把消息发送出去就可以了。 79 super.sendMessage(message, toUser); 80 } 81 } 82 (6)加急消息的实现 83 /** 84 * 加急消息 85 */ 86 public class UrgencyMessage extends AbstractMessage { 87 88 public UrgencyMessage(MessageImplementor impl) { 89 super(impl); 90 } 91 92 @Override 93 public void sendMessage(String message, String toUser) { 94 message = "[加急]:"+message; 95 super.sendMessage(message, toUser); 96 } 97 98 /** 99 * 扩展新功能:监控某消息的处理过程 100 * @param messageId 101 * @return 102 */ 103 public Object watch(String messageId){ 104 return null; 105 } 106 } 107 108 (7)测试客户端 109 public class Client { 110 public static void main(String[] args) { 111 MessageSMS sms = new MessageSMS(); 112 CommonMessage cm = new CommonMessage(sms); 113 UrgencyMessage um = new UrgencyMessage(sms); 114 cm.sendMessage("你有一个回复", "小明"); 115 um.sendMessage("你有一个回复", "小明"); 116 117 MessageEmail email = new MessageEmail(); 118 CommonMessage cm2 = new CommonMessage(email); 119 UrgencyMessage um2 = new UrgencyMessage(email); 120 cm2.sendMessage("你有一个包裹", "小明"); 121 um2.sendMessage("你有一个包裹", "小明"); 122 } 123 } 124 125 运行结果: 126 使用站内短消息的方式,发送消息: 你有一个回复,给小明 127 使用站内短消息的方式,发送消息: [加急]:你有一个回复,给小明 128 使用Email消息的方式,发送消息: 你有一个包裹,给小明 129 使用Email消息的方式,发送消息: [加急]:你有一个包裹,给小明
(b)添加功能,消息类型增加:特急消息;发送方式增加:手机短信。
示例代码如下:
1 /** 2 * 特急消息 3 */ 4 public class SpecialUrgencyMessage extends AbstractMessage { 5 6 public SpecialUrgencyMessage(MessageImplementor impl) { 7 super(impl); 8 } 9 10 @Override 11 public void sendMessage(String message, String toUser) { 12 message = "[特急]:" + message; 13 super.sendMessage(message, toUser); 14 } 15 16 public void hurry(String messageId){ 17 //执行催促业务 18 } 19 } 20 21 /** 22 * 手机短消息的方式发送消息 23 */ 24 public class MessageMobile implements MessageImplementor { 25 26 @Override 27 public void send(String message, String toUser) { 28 System.out.println("使用手机短消息的方式,发送消息:" + message+",给 "+toUser); 29 } 30 } 31 32 33 客户端测试 34 public class Client { 35 public static void main(String[] args) { 36 MessageSMS sms = new MessageSMS(); 37 CommonMessage cm = new CommonMessage(sms); 38 UrgencyMessage um = new UrgencyMessage(sms); 39 cm.sendMessage("你有一个回复", "小明"); 40 um.sendMessage("你有一个回复", "小明"); 41 System.out.println("-------------------------"); 42 MessageEmail email = new MessageEmail(); 43 CommonMessage cm2 = new CommonMessage(email); 44 UrgencyMessage um2 = new UrgencyMessage(email); 45 cm2.sendMessage("你有一个包裹", "小明"); 46 um2.sendMessage("你有一个包裹", "小明"); 47 System.out.println("-------------------------"); 48 MessageMobile mobile = new MessageMobile(); 49 CommonMessage cm3 = new CommonMessage(mobile); 50 UrgencyMessage um3 = new UrgencyMessage(mobile); 51 SpecialUrgencyMessage sum3 = new SpecialUrgencyMessage(mobile); 52 cm3.sendMessage("有人赞了你", "小东"); 53 um3.sendMessage("有人赞了你", "小东"); 54 sum3.sendMessage("有人赞了你", "小东"); 55 } 56 } 57 58 运行结果: 59 使用站内短消息的方式,发送消息: 你有一个回复,给小明 60 使用站内短消息的方式,发送消息: [加急]:你有一个回复,给小明 61 ------------------------- 62 使以上是关于设计模式 -- 桥接模式(Bridge)的主要内容,如果未能解决你的问题,请参考以下文章