桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体模式(Handle and Body)模式或接口(Interface)模式。
将抽象部分与实现部分分离,使它们都可以独立的变化。————《设计模式》GOF
桥梁模式涉及的角色:
抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。在抽象对象里面的方法,需要调用实现部分的对象来完成。
修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。添加跟实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法,也可能需要调用实现部分的对象来完成。
实现化(Implementor)角色:定义实现部分的接口,这个接口不用和Abstraction里面的方法一致,通常是由Implementor接口提供基本的操作,而Abstraction里面定义的是基于这些基本操作的业务方法,也就是说Abstraction定义了基于这些基本操作的较高层次的操作。
具体实现化(Concrete Implementor)角色:真正实现Implementor接口的对象。
现在有一个示例,有两个纬度,一个纬度是抽象的消息,包括普通消息、加急消息和特急消息,这几个抽象的消息本身就具有一定的关系,加急消息和特急消息会扩展普通消息;另一个纬度在具体的消息发送方式上,包括站内短消息、Email和手机短信息,这几个方式是平等的,可被切换的方式。
要使用桥接模式来实现这个示例,首要任务就是要把抽象部分和实现部分分离出来,分析要实现的功能,抽象部分就是各个消息的类型所对应的功能,而实现部分就是各种发送消息的方式。先实现普通消息和加急消息的功能,发送方式先实现站内短消息和Email这两种。
MessageImplementor.java接口:
public interface MessageImplementor {
/**
* 发送消息方法
* @param message
* @param toUser
*/
public void send(String message,String toUser);
}
MessageSMS.java:
public class MessageSMS implements MessageImplementor{
@Override
public void send(String message, String toUser) {
System.out.println("使用站内短消息的方式,发送消息"+message+"给"+toUser);
}
}
MessageEmail.java:
public class MessageEmail implements MessageImplementor {
@Override
public void send(String message, String toUser) {
System.out.println("使用Email的方式,发送消息"+message+"给"+toUser);
}
}
消息类型有:
AbstractMessage.java的抽象类:
public abstract class AbstractMessage {
//持有接口对象的引用
private MessageImplementor impl;
public AbstractMessage(MessageImplementor impl){
this.impl = impl;
}
public void sendMessage(String message,String toUser){
this.impl.send(message, toUser);
}
}
具体实现类CommonMessage.java:
public class CommonMessage extends AbstractMessage {
public CommonMessage(MessageImplementor impl) {
super(impl);
}
public void sendMessage(String message, String toUser) {
super.sendMessage(message, toUser);
}
}
具体实现类UrgencyMessage.java:
public class UrgencyMessage extends AbstractMessage {
public UrgencyMessage(MessageImplementor impl) {
super(impl);
}
@Override
public void sendMessage(String message, String toUser) {
message = "加急"+message;
super.sendMessage(message, toUser);
}
//扩展新功能,添加消息监控程序
public Object watch(String messageId){
return null;
}
}
这样就实现了站内信和Email可以分别发送普通消息和加急消息的功能了,现在如何在消息的发送方式中添加一个手机发送,消息的类型中添加一个特急消息,扩展起来就很容易了。代码如下:
MessageMobile.java:
public class MessageMobile implements MessageImplementor {
@Override
public void send(String message, String toUser) {
System.out.println("使用手机短信的方式,发送消息"+message+"给"+toUser);
}
}
SpecialUrgencyMessage.java:
public class SpecialUrgencyMessage extends AbstractMessage {
public SpecialUrgencyMessage(MessageImplementor impl) {
super(impl);
}
@Override
public void sendMessage(String message, String toUser) {
message = "特急"+message;
super.sendMessage(message, toUser);
}
public void hurry(String messageId){
//执行催促的业务
}
}
同时,我们在实现特急消息的时候,还可以添加一些额外的方法来满足业务需求。
桥接模式是单向的,也就是只能是抽象部分的对象去使用具体实现部分的对象,而不能反过来,也就是单个桥。
只要让抽象部分拥有实现部分的接口对象,这就桥接上了,也就是说,桥接在程序上就体现成了在抽象部分拥有实现部分的接口对象,维护桥接就是维护这个关系。
桥接模式的意图:使得抽象和实现可以独立变化,都可以分别扩充。抽象部分不过是使用实现部分对外接口的程序罢了。
继承是扩展对象功能的一种常见手段,通常情况下,继承扩展的功能变化纬度都是一纬的,也就是变化的因素只有一类。对于出现变化因素有两类的,也就是有两个变化纬度的情况,继承实现就会很痛苦。如果用继承,最后的实际类应该是两个纬度上可变数量的乘积。如果用桥接模式的方式来实现这种有两个变化纬度的情况,最后实际的实现类应该是两个纬度上可变数量的和。
桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。桥接模式在java使用中也是很广泛的,如jdbc的连接在切换数据库时,就使用了桥接模式,可以做到只需要加载的驱动,就能达到实现切换对不同数据库的使用功能。