桥接模式之消息发送
桥接模式基本概念
用来解决上述问题的一个合理的解决方案,就是使用桥接模式(Bridge)。那么什么是桥接模式呢?
一、桥接(Bridge)模式基本概念
1.1 定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
在上面的例子中我们之前分析了有两个维度:发送消息的方式和消息的类型。
发送消息的方式:就是微信、手机短信、邮件。
消息的类型:普通消息,加急消息、特急消息。
那么这里的话,抽象部分实现部分是什么呢?消息发送的核心功能就是发送消息了,所以实现就是消息的发送方式(微信/手机短信/邮件)。那么抽象部分就是消息类型(普通/加急/特急)。
1.2 应用桥接模式来解决的思路
在上面的例子中我们之前分析了有两个维度:具体的消息发送的方式(微信/邮件/手机短信)和抽象的消息类型(普通/加急/加急)。
这两个纬度一共可以组合出9种不同的可能性来(3×3=9)。
我们在前面的编码问题就是:消息的抽象和实现是混杂在一起的,这就导致了,一个纬度的变化,会引起另一个纬度进行相应的变化,从而使得程序扩展起来非常困难。
在桥接模式中解决的思路就是:把这两个纬度分开,也就是将抽象部分和实现部分分开,让它们相互独立,这样就可以实现独立的变化,使扩展变得简单。
桥接模式通过引入实现的接口,把实现部分从系统中分离出去;那么,抽象这边如何使用具体的实现呢?肯定是面向实现的接口来编程了,为了让抽象这边能够很方便的与实现结合起来,把顶层的抽象接口改成抽象类,在里面持有一个具体的实现部分的实例。
这样一来,对于需要发送消息的客户端而言,就只需要创建相应的消息对象,然后调用这个消息对象的方法就可以了,这个消息对象会调用持有的真正的消息发送方式来把消息发送出去。也就是说客户端只是想要发送消息而已,并不想关心具体如何发送。
1.3 类图
1.4 主要角色
(1)抽象类(Abstraction):维护了Implementor即他的实现类,二者是聚合关系,Abstraction充当桥接类。
(2)扩展抽象类(RefinedAbstraction):扩展抽象部分的接口,可以有多个。
(3)实现接口(Implementor):定义实现部分的接口
(4)具体实现(ConcreteImplementor):Implementor接口的具体实现,可以有多个。
到这里我们对于桥接模式有了一个基本的了解,接下来我们用这个模式对于我们之前的代码进行优化。
桥接模式之消息发送
在上面我们对于桥接模式的定义有了一个基本的了解,接下来我们要重构我们之前的代码。
一、消息发送4.0:桥接模式
接下来我们根据桥接模式的定义来进行编码,主要分为两大部分抽象部分和实现部分。我们看下最终完成的类图:
1.1 实现接口(Implementor)
实现部分这里的例子中就是定义消息发送方式的一个接口即可,这里取名为MessageImplementor:
package com.kfit.bridge.message.v4;
/\*\*
\*
\* Implementor
\*
\* 定义实现类的接口,该接口不一定要与Abstraction的接口完全一致。事实上这两个接口可以完全不同。
\* 一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些基本操作的较高层次的操作。
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public interface MessageImplementor {
/\*\*
\* 所有的消息类型都需要有发送消息的方法
\* @param toUser : 消息要发送给谁
\* @param message :发送消息的内容
\*/
void send(String toUser,String message);
}
复制代码
1.2 具体实现(ConcreteImplementor)
在这个例子中,我们主要有两种发送方式微信和手机短信,我们看下具体的实现:
微信消息发送方式-WeixinMessage:
package com.kfit.bridge.message.v4;
/\*\*
\* 发送消息的方式 - 微信发送消息:际项目中应该是微信公众号的消息
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class WeixinMessage implements MessageImplementor {
@Override
public void send(String toUser, String message) {
//调用微信的SDK进行发送消息
System.out.println("\[微信消息\] "+toUser+":"+message);
}
}
复制代码
手机短信发送方式-SMSMessage:
package com.kfit.bridge.message.v4;
/\*\*
\* 发送消息的方式 - 手机短信
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class SMSMessage implements MessageImplementor {
@Override
public void send(String toUser, String message) {
//调用短信平台SDK进行发送消息
System.out.println("\[手机短信消息\] "+toUser+":"+message);
}
}
复制代码
1.3 抽象类(Abstraction)
在例子中抽象部分就是消息的类型:普通消息、加急消息、特急消息。
AbstractMessage:
package com.kfit.bridge.message.v4;
/\*\*
\*
\*定义抽象类的接口。维护一个指向Implementor类型对象的指针。
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public abstract class AbstractMessage {
/\*\*
\* 持有一个实现部分的对象
\*/
protected MessageImplementor impl;
public AbstractMessage(MessageImplementor impl) {
this.impl = impl;
}
/\*\*
\* 发送消息,转调实现部分的方法
\* send 这个方法不要求和MessageImplementor的一样。
\*
\* @param toUser 消息发送的目的人员
\* @param message 要发送的消息内容
\*/
public void send(String toUser,String message){
this.impl.send(toUser,message);
}
}
复制代码
1.4 扩展抽象类(RefinedAbstraction)
这里看下普通消息和加急消息的:
普通消息方式-CommonMessage:
package com.kfit.bridge.message.v4;
/\*\*
\*
\* 普通消息的实现
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class CommonMessage extends AbstractMessage{
public CommonMessage(MessageImplementor impl) {
super(impl);
}
@Override
public void send(String toUser, String message) {
//对于普通消息,什么都不干,直接调父类的方法,把消息发送出去就可以了
super.send(toUser, message);
}
}
复制代码
加急消息方式-UrgencyMessage:
package com.kfit.bridge.message.v4;
/\*\*
\*
\* 加急消息的实现
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class UrgencyMessage extends AbstractMessage{
public UrgencyMessage(MessageImplementor impl) {
super(impl);
}
@Override
public void send(String toUser, String message) {
message ="\[加急\]"+message;
super.send(toUser, message);
}
/\*\*
\* 扩展新功能:监控某消息的处理过程。
\* @param messageId :消息编号
\* @return
\*/
public Object watch(String messageId) {
//获取相应的数据,返回监控结果
return "\[watch\]收到,一定会去参加的";
}
}
复制代码
1.5 小测
到这里我们就可以进行测试一下了:
package com.kfit.bridge.message.v4;
/\*\*
\*
\* 发送消息测试
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class Me {
public static void main(String\[\] args) {
//定义消息发送方式,如果要手机短信,直接换一个实现类即可:SMSMessage
MessageImplementor messageImplementor = new WeixinMessage();
/\*\*
\* 定义消息类型,可以和发送方式随意组合。
\*/
//创建一个普通消息对象
AbstractMessage message = new CommonMessage(messageImplementor);
message.send("鹏仔", "2021年x月x日,我要结婚了,记得来参加我的婚礼");
//加急消息
message = new UrgencyMessage(messageImplementor);
message.send("鹏仔", "2021年x月x日,我要结婚了,记得来参加我的婚礼");
//... 其它人...
}
}
复制代码
打印结果:
要想使用手机短信发送替换到具体的实现类即可。
1.6 添加新的消息类型:加入特急消息
那我们看看桥接模式是否可以解决我们上面碰到的问题:一个维度的变化会影响另外一个维度变化。
我们现在要加入特急消息,只需要定义新的扩展抽象类即可,然后可以进行使用了。
SpecialUrgencyMessage:
package com.kfit.bridge.message.v4;
/\*\*
\* 特急消息
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class SpecialUrgencyMessage extends AbstractMessage{
public SpecialUrgencyMessage(MessageImplementor impl) {
super(impl);
}
@Override
public void send(String toUser, String message) {
message = "\[特急\]"+message;
super.send(toUser, message);
}
/\*\*
\* 扩展新功能:催促
\* @return
\*/
public void urge() {
System.out.println("\[urge\]->赶紧来...");
}
}
复制代码
使用很简答:
//特急消息
message = new SpecialUrgencyMessage(messageImplementor);
message.send("鹏仔", "2021年x月x日,我要结婚了,记得来参加我的婚礼");
复制代码
1.6 添加新的消息发送方式:加入邮件发送方式
我们要加入新的发送方式也很简单,只需要在新增一个具体实现即可。
EmailMessage:
package com.kfit.bridge.message.v4;
/\*\*
\* 发送消息的方式 - 邮件发送方式
\*
\* @author 悟纤「公众号SpringBoot」
\* @date 2020-11-27
\* @slogan 大道至简 悟在天成
\*/
public class EmailMessage implements MessageImplementor {
@Override
public void send(String toUser, String message) {
//调用邮件服务
System.out.println("\[邮件\] "+toUser+":"+message);
}
}
复制代码
1.7 小结
采用桥接模式来实现过后,抽象部分和实现部分分离开了,可以相互独立的变化,而不会相互影响。因此在抽象部分添加新的消息类型,对发送消息的实现部分是没有影响的;反过来增加发送消息的方式,对消息类型也是没有影响的。