本文参考自: 《JAVA设计模式》之桥接模式(Bridge)
1. 作用
将抽象化与实现化解耦,使二者可以独立的变化
2. 三个关键词
- 抽象化
- 实现化
- 解耦
抽象化
从众多的事物中抽取其共同的,本质的特征,而舍弃其非本质的特征的过程,就是抽象化。
实现化
抽象化给出的具体实现,就是实现化
解耦
所谓耦合,就是两个实体行为之间存在强关联。所谓解耦,就是将两个实体行为之间的强关联去掉,变成弱关联。
强关联,就是在编译时就已经确定,无法在运行时动态改变的关联。弱关联,就是可以在运行时,动态进行改变的关联。在Java语言中,继承关系是强关联,聚合关系是弱关联。
3. 桥梁模式的用意
将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用聚合关系而不是继承关系,从而使两者可以相对独立地变化。这就是桥梁模式的用意。
4. 桥梁模式的结构
桥梁模式中的四个结构
- 抽象化角色
- 修正的抽象化角色
- 实现化角色
- 具体的实现化角色
抽象化角色:对应上图中的Abstraction,一个具体事物高度抽象化出来的类,它保存着一个实现化对象的引用。
修正的抽象化角色:对应上图中的RefineAnstraction,可以有一个或多个,是对抽象化角色的拓展。
实现化角色:对应上图中的Implementor,是一个接口或者抽象类,给出实现化的接口,但是不具体实现。
具体的实现化角色:对应上图中的ConcreteImlpementorA和B,给出实现化角色的具体实现。
上面的描述比较抽象,结合后文中“桥梁模式的实际应用场景,可以更好地理解。”
桥梁模式的实现
抽象化角色
public class Abstraction {
private Implemetor implemetor;
public Abstraction(Implemetor implemetor) {
this.implemetor = implemetor;
}
public void opetareAbs() {
implemetor.operate();
}
}
修正的抽象化角色
public class RefineAbstration1 extends Abstraction{
public RefineAbstration1(Implemetor implemetor) {
super(implemetor);
}
public void otherOperations1() {
System.out.println("RefineAbstration1");
}
}
public class RefineAbstraction2 extends Abstraction{
public RefineAbstraction2(Implemetor implemetor) {
super(implemetor);
}
public void otherOperation2() {
System.out.println("RefineAbstract2");
}
}
实现化角色
public abstract class Implemetor {
abstract void operate();
}
具体的实现化角色
public class ConcreteImplemetor1 extends Implemetor{
@Override
public void operate() {
// TODO Auto-generated method stub
System.out.println("ConcreteImplemetor1");
}
}
public class ConcreteImplemetor2 extends Implemetor{
@Override
public void operate() {
// TODO Auto-generated method stub
System.out.println("ConcreteImplemetor2");
}
}
具体使用
public class BridgePatternMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
Abstraction refineAbstration1 = new RefineAbstration1(new ConcreteImplemetor1());
refineAbstration1.opetareAbs();
((RefineAbstration1)refineAbstration1).otherOperations1();
}
}
5. 桥梁模式的实际应用场景
考虑这样一个实际的业务功能:发送提示消息。基本上所有带业务流程处理的系统都会有这样的功能,比如OA上有尚未处理完毕的文件,需要发送一条消息提示他。
从业务上看,消息又分成普通消息、加急消息和特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是在消息上添加加急,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促;从发送消息的手段上看,又有系统内短消息、手机短信息、邮件等。
不使用模式的解决方案
a . 发送普通消息
先考虑实现一个简单点的版本,比如,消息只是实现发送普通消息,发送的方式只实现系统内短消息和邮件。其他的功能,等这个版本完成后,再继续添加。
b. 突然发送加急消息
发送加急消息同样有两种方式,系统内短消息和邮件方式。但是加急消息的实现不同于普通消息,加急消息会自动在消息上添加加急,然后在再发送消息;另外加急消息会提供监控的方法,让客户端可以随时通过这个方法来了解对于加急消息的处理进度。比如,相应的人员是否接收到这个信息,相应的处理工作是否已经展开。因此加急消息需要扩展出一个新的接口,除了基本的发送消息的功能,还需要添加监控功能。
c. 继续增加发送特急消息
特急消息不需要查看处理进程,只有没有完成,就直接催促,也就是说,对于特急消息,在普通消息的处理基础上,需要添加催促的功能。
d. 添加使用手机发送消息的处理方式
如果要添加一种新的发送消息的方式,是需要在每一种抽象的具体实现中,都添加发送手机消息的处理的。也就是说,发送普通消息、加急消息和特急消息的处理,都可以通过手机来发送。
明显看出,在后续迭代过程中,上述方式产生了过多的冗余代码,主要原因时,在初始构造时,未能很好的将“抽象”与“实现”解耦。
使用桥梁模式的解决方案
根据业务的功能要求,业务的变化具有两个维度,一个维度是抽象的消息,包括普通消息、加急消息和特急消息,这几个抽象的消息本身就具有一定的关系,加急消息和特急消息会扩展普通消息;另一个维度是在具体的消息发送方式上,包括系统内短消息、邮件和手机短消息,这几个方式是平等的,可被切换的方式。
现在出现问题的根本原因,就在于消息的抽象和实现是混杂在一起的,这就导致了一个纬度的变化会引起另一个纬度进行相应的变化,从而使得程序扩展起来非常困难。
要想解决这个问题,就必须把这两个纬度分开,也就是将抽象部分和实现部分分开,让它们相互独立,这样就可以实现独立的变化,使扩展变得简单。抽象部分就是各个消息的类型所对应的功能,而实现部分就是各种发送消息的方式。按照桥梁模式的结构,给抽象部分和实现部分分别定义接口,然后分别实现它们就可以了。
具体实现代码
抽象消息类:对应Abstraction
public class AbstractMessage {
protected MessageImpletor messageImpletor;
public AbstractMessage(MessageImpletor messageImpletor) {
this.messageImpletor = messageImpletor;
}
public void sendMessage(String msg,String user) {
messageImpletor.send(msg,user);
}
}
发送普通消息类:对应RefineAbstraction
public class NormalMessage extends AbstractMessage{
public NormalMessage(MessageImpletor messageImpletor) {
super(messageImpletor);
}
}
发送加急消息类:对应RefineAbstraction
public class UrgencyMessage extends AbstractMessage{
public UrgencyMessage(MessageImpletor messageImpletor) {
super(messageImpletor);
}
@Override
public void sendMessage(String msg, String user) {
// TODO Auto-generated method stub
msg = "urgency:"+msg;
super.sendMessage(msg, user);
}
public void watch() {
System.out.println("watching");
}
}
消息实现的统一接口:对应Implementor
public interface MessageImpletor {
public void send(String msg,String user);
}
Email发送消息的实现类:对应ConcreteImplementor
public class EmaiMessageImpletor implements MessageImpletor{
@Override
public void send(String msg,String user) {
// TODO Auto-generated method stub
System.out.println("send email message");
}
}
Phone发送消息的实现类:对应ConcreteImplementor
public class PhoneMessageImpletor implements MessageImpletor{
@Override
public void send(String msg,String user) {
// TODO Auto-generated method stub
System.out.println("send phone message");
}
}
SMS发送消息的实现类:对应ConcreteImplementor
public class SMSMessageImpletor implements MessageImpletor{
@Override
public void send(String msg,String user) {
// TODO Auto-generated method stub
System.out.println("send sms message");
}
}
具体使用
public class BridgePatternDemoMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
AbstractMessage message = new NormalMessage(new PhoneMessageImpletor());
message.sendMessage("msg", "user");
message = new UrgencyMessage(new SMSMessageImpletor());
message.sendMessage("msg", "user");
((UrgencyMessage)message).watch();
}
}
观察上面的例子会发现,采用桥梁模式来实现,抽象部分和实现部分分离开了,可以相互独立的变化,而不会相互影响。因此在抽象部分添加新的消息处理(特急消息),对发送消息的实现部分是没有影响的;反过来增加发送消息的方式(手机短消息),对消息处理部分也是没有影响的。
6. 桥梁模式的优点
-
分离抽象和实现部分
桥梁模式分离了抽象部分和实现部分,从而极大地提供了系统的灵活性。让抽象部分和实现部分独立出来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。 -
更好的扩展性
桥梁模式使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,从而大大提高了系统的可扩展性。