设计模式 桥接模式

来源:互联网 发布:cf淘宝商城 编辑:程序博客网 时间:2024/05/29 07:30

参考原文地址:https://www.oschina.net/question/1435262_140274

业务场景:

1.发送提示消息

流程处理的系统都会有这样的功能,比如某人有新的工作了,需要发送一条消息提示他。

从消息类型上看,消息又分成普通消息、加急消息和特急消息多种,不同的消息类型,业务功能处理是不一样的,比如加急消息是

在消息上添加加急,而特急消息除了添加特急外,还会做一条催促的记录,多久不完成会继续催促。

从发送消息的手段上看,又有系统内短消息、手机短消息、邮件等等。

如何实现呢?

2.不用模式的解决方案

实现简化版本:

先考虑一个比较简单的版本,消息类型只实现普通消息,发送方式先只实现系统内短消息和邮件两种方式。其他的功能,等到‘

下次迭代实现。化繁为简。

(1)经过上述分析我们知道,发送消息具有系统内短消息和邮件两种方式,但是这两种方式都具有发送消息这个共同的特点,
所以可以考虑设计一个接口,外部类可以调用接口方法,而不同的消息发送方式提供不同的实现。UML图大致如下:

我们来看看简单的代码示例

这是统一的消息接口
/** * 消息的统一接口 */public interface Message {    /**     * 发送消息     * @param message 要发送的消息内容     * @param toUser 消息发送的目的人员     */    public void send(String message,String toUser);}

下面是两个不同实现--短信方式
/** * 以站内短消息的方式发送普通消息 */public  class CommonMessageSMS implements Message{    public void send(String message, String toUser) {       System.out.println("使用站内短消息的方式,发送消息'"+message+"'给"+toUser);    }}

下面是Email发送消息的方式
/** * 以Email的方式发送普通消息 */public class CommonMessageEmail implements Message{    public void send(String message, String toUser) {       System.out.println("使用Email的方式,发送消息'"+message+"'给"+toUser);    }}
功能增加
接下来,我们需要添加加急消息这种消息类型,它同样拥有短信,Email两种发送方式。
注意,加急消息和普通消息不同,加急消息会自动在消息上添加加急信息然后发送消息。另外加急消息会提供监控的方法,让
客户端可以随时通过这个方法来了解对于加急消息处理的进度,比如:相应的人员是否接收到这个信息,相应的工作是否已经开展
等等。因此加急消息需要扩展出一个新的接口,除了基本的发送消息的功能,还需要添加监控的功能。
此时的系统结构图如下图所示:

从UML图很直观的可以看到,你需要添加一个接口,已经这个接口的两个实现类。

分析问题

上面这样实现,好像也能满足基本的功能要求,可是这么实现好不好呢?有没有什么问题呢?咱们继续向下来添加功能实现
1.继续添加特急消息的处理
特急消息不需要查看处理进程,只要没有完成,就直接催促,也就是说,对于特急消息,在普通消息处理的基础上,需要添加
催促的功能。同样,特急消息具有系统内短信和邮件两种发送消息的方式。此时系统的大体结构如下所示:

通过这两次功能迭代,细心的你可能已经发现,这样的做法是不是扩展性太低了?如果我还需要加入另外多种消息类型呢,那么你就必须为你添加的消息
类型实现自己的发送方式,而且每个消息类型还有自己的特点,如特急消息就有催促特点,这是一件多么麻烦的事情。
更痛苦的是,如果你需要添加一种发送方式,比如添加手机消息这种发送消息的方式,来看看现在的实现:

问题总结
采用继承的方式来实现扩展有个明显的缺点:扩展消息类型不太容易,不同消息类型具有不同的业务,这意味着你需要不同的
实现,在这种情况下,每个消息类型,需要实现每种消息发送方式。
更可怕的是,如果需要加入一种新的消息发送方式,那么会要求所有的消息类型都要加入这种新的消息发送方式。
那么,我们应该怎么解决这个问题呢?

解决方案
下面引出我们的解决方法------桥接模式
定义:将抽象部分与实现部分分离,使得他们都可以独立的变化。
思路:根据示例的功能需求,我们能够分析出来变化的两个纬度,第一个纬度是抽象的消息类型(普通消息,加急消息,特急消息),另一个纬度在具体的消息发送方式上,包括站内短信,邮件消息,手机消息,这几个方式是平等的。我们从这两个纬度可以组合出来九种不同的可能性来:

现在出现问题的根本原因,就在于消息的抽象和实现是混杂在一起的,这就导致了,一个纬度的变化,会引起另一个纬度进行相应的变化,从而使得程序扩展起来非常困难。
要想解决这个问题,就必须把这两个纬度分开,也就是将抽象部分和实现部分分开,让它们相互独立,这样就可以实现独立的变化,使扩展变得简单。

桥接模式通过引入实现的接口,把实现部分从系统中分离出去;那么,抽象这边如何使用具体的实现呢?肯定是面向实现的接口来编程了,为了让抽象这边能够很方便的与实现结合起来,把顶层的抽象接口改成抽象类,在里面持有一个具体的实现部分的实例。
这样一来,对于需要发送消息的客户端而言,就只需要创建相应的消息对象,然后调用这个消息对象的方法就可以了,这个消息对象会调用持有的真正的消息发送方式来把消息发送出去。也就是说客户端只是想要发送消息而已,并不想关心具体如何发送。

桥接模式结构:



Abstraction:抽象部分接口(抽象类),通常含有一个实现部分的对象引用,在抽象部分的方法里,通常需要调用实现部分对象的方法      来完成。这个对象里面的方法,通常都是跟具体业务相关的方法。
RefinedAbstraction:扩展抽象部分的接口,通常在这些对象里面,定义跟实际业务相关的方法,这些方法的实现通常会使用Abstraction中定义的方法,也可能需要调用实现部分的对象来完成。
Implementor:定义实现部分的接口,这个接口不用和Abstraction里面的方法一致,通常是由Implementor接口提供基本的操作,而Abstraction里面定义的是基于这些基本操作的业务方法,也就是说Abstraction定义了基于这些基本操作的较高层次的操作。
ImplementorA:实现Implementor接口的类,含有具体的实现逻辑,Implementor亦是。

使用桥接模式后的系统结构:

这样是不是很清晰呢!!!!!
假设现在我们需要添加一种消息发送方式,只需要添加一个类然后实现消息发送方式的接口就可以了。
而且你想想,这是不是有点低耦合,高内聚的意思呢?模式是个好模式,就看你下不下功夫了!


















0 0
原创粉丝点击