桥接模式介绍
我们用抽象来解耦客户端代码和实现,通常的做法是用继承(inheritance)的方式。我们定义一个接口或抽象类,然后基于接口或抽象类来创建继承层级,可能会存在多个实现。尽管第一眼看上去此方式从逻辑上好像没啥错误,但不总是不那么灵活。当用继承实现时,我们会自然的把抽象和实现绑定在一起。造成的结果是一端发生变化总会影响到另一方。一个更灵活的方式是分离抽象和实现,这就是桥接模式要做的事情。
桥接模式的参与者
为了理解桥接模式如何工作的,我们举一个消息发送的例子,客户端可以用我们的消息发送程序来发送不同类型的消息,比如文本或者邮件消息。我们一般直接的做法是创建一个接口或抽象类 Message
, 然后创建两个衍生类 TextMessage
和EmailMessage
,最后创建两个消息发送类,TextMessageSender
extends TextMessage
, EmailMessageSender
extends EmailMessage
。 继承层次如下图:
乍一看,上面的类层次设计好像没什么问题,再想一想此设计还是有些缺陷的,客户端代码和抽象层交互,实现层提供了消息发送的核心功能,所以此时实现层和抽象层是紧耦合的,这是基于继承设计的一个固有的缺点,打破了封装,作为一个 EmailMessageSender
开发者,你不得不要知道父类 EmailMessage
的内部细节,于是父类的封装就被打破了。
在需求变化时,这种设计是脆弱的,比如,我们要改变实现,运行客户端在发送信息前可选择加密信息,我们必须要修改抽象层为客户端提供加密功能。
还有一个复用性问题,如果我们只想复用实现层代码(消息发送)部分用于其他应用,我们不得不要把抽象层的代码带着一起打包。
桥接模式用两个类层次分离抽象和实现,这种方式很轻松解决了上面的问题,下面展示了不用和用桥接模式的类结构图
使用桥接模式,抽象层和实现层是 Has-A 的关系,否则是IS-A 的关系。Has-A 的关系是通过抽象层维护一个实现层引用来实现,也符合组合由于继承
的原则。
总结消息发送例子中各部分在桥接模式的角色:
- 抽象层:
Message
用抽象类实现,供客户端调用 - 细化的抽象:
TextMessage
和EmailMessage
是抽象层接口的实现类 - 实现层:
MessageSender
是实现层的接口 - 具体的实现层:
TextMessageSender
和EmailMessageSender
是实现层接口的实现类
桥接模式的代码实现
抽象层代码实现:
public abstract class Message {
MessageSender messageSender;
public Message(MessageSender messageSender){
this.messageSender=messageSender;
}
abstract public void send();
}
public class TextMessage extends Message{
public TextMessage(MessageSender messageSender){
super(messageSender);
}
@Override
public void send(){
messageSender.sendMessage();
}
}
public class EmailMessage extends Message{
public EmailMessage(MessageSender messageSender){
super(messageSender);
}
@Override
public void send(){
messageSender.sendMessage();
}
}
实现层代码实现:
public interface MessageSender {
public void sendMessage();
}
public class TextMessageSender implements MessageSender {
@Override
public void sendMessage(){
System.out.println("TextMessageSender: Sending text message...");
}
}
public class EmailMessageSender implements MessageSender{
@Override
public void sendMessage(){
System.out.println("EmailMessageSender: Sending email message...");
}
}
public class MessageTest {
@Test
public void testSend() throws Exception {
MessageSender textMessageSender=new TextMessageSender();
Message textMessage=new TextMessage(textMessageSender);
textMessage.send();
MessageSender emailMessageSender=new EmailMessageSender();
Message emailMessage=new TextMessage(emailMessageSender);
emailMessage.send();
}
}
测试用例输出:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running guru.springframework.gof.bridge.abstraction.MessageTest
TextMessageSender: Sending text message...
EmailMessageSender: Sending email message...
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec - in guru.springframework.gof.bridge.abstraction.MessageTest
##总结
桥接模式利用两个类层次解耦抽象和实现层,在平时开发时,如果发现类的层次太深时,可以考虑是否能用桥接模式来解耦,这样做可使你的代码面对更改时显得灵活和健壮。还有,利用桥接模式,可使你的代码更易单元测试,没有桥接模式,为了测试实现类,还必须要清楚超类的信息,有了桥接之后,你可以创建模拟对象来独立测试抽象层和实现层的代码。