桥接模式

本文参考自: 《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. 桥梁模式的优点
  • 分离抽象和实现部分
    桥梁模式分离了抽象部分和实现部分,从而极大地提供了系统的灵活性。让抽象部分和实现部分独立出来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。
  • 更好的扩展性
    桥梁模式使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,从而大大提高了系统的可扩展性。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容