策略模式(Strategy Pattern)从某种意义上可以说是面向对象程序设计特性之一的多态的一种应用,它描述的是为了达成某种目的而存在多种可供选择的实现方式的情况,我们将这些可供选择的实现方式命名为策略,各个策略具体的实现内容不尽相同,但是它们达成的结果都是一致的。
1. 定义
策略模式是一种行为型设计模式,它的定义是:“定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。”
2. 类图
在策略模式中,有这样几种角色:
- 抽象策略角色(Strategy):描述了每个策略需要完成的功能,通常使用接口来定义。
- 具体策略角色(Concrete Strategy):抽象策略角色的实现类,具体功能的实现方,可能会定义一些抽象策略角色无法抽象的属性或方法
- 上下文角色(Context):屏蔽高层模块对策略的直接访问,起到封装作用,它通常会聚合一个抽象策略角色,利用多态来使用不同的具体策略
策略模式的类图如下:
3. 示例
通知中心作为一个系统的基础能力,可以作为基础设施层单独开发部署。它所提供的功能相对简单:向用户发送消息通知,然而消息通知的渠道可以有很多,可以是邮件、短信、电话、站内信等等。同时,发送消息通知功能所需要的参数也相对易于抽象:发送人、收件人、发送渠道、发送内容。
所以,通知中心实现多渠道消息触达功能使用策略模式来实现再合适不过了。
首先定义通知中心所需要的信息的数据结构,代码如下:
public class Message {
// 发送人
private String sender;
// 收件人
private String receiver;
// 内容
private String context;
public Message(String sender, String receiver, String context) {
this.sender = sender;
this.receiver = receiver;
this.context = context;
}
// 省略 setter or getter 方法
}
然后定义抽象策略接口,即消息通知接口,它提供的方法就是实现发送消息的功能,其代码如下:
public interface Notify {
void doNotify(Message msg);
}
然后定义两种具体策略类,如短信、电话,其代码如下:
public class Sms implements Notify {
@Override
public void doNotify(Message msg) {
System.out.printf("[%s]使用[短信]通知[%s], 通知内容为[%s]\n", msg.getSender(), msg.getReceiver(), msg.getContext());
}
}
public class Call implements Notify {
@Override
public void doNotify(Message msg) {
System.out.printf("[%s]使用[电话]通知[%s], 通知内容为[%s]\n", msg.getSender(), msg.getReceiver(), msg.getContext());
}
}
然后定义上下文角色,即通知中心类,它聚合了一个 Notify 接口,并通过调用其方法 doNotify 来调用具体策略的实现,其代码如下:
public class NoticeCenter {
private Notify notify;
public void sendMessage(Message message) {
// 真正进行消息触达的方法
notify.doNotify(message);
}
// 省略 setter or getter 方法
}
然后编写测试代码:
public class Test {
public static void main(String[] args) {
Message message = new Message("老张", "大A", "明天要涨啊!!!");
// 使用短信通知
NoticeCenter noticeCenter = new NoticeCenter();
noticeCenter.setNotify(new Sms());
noticeCenter.sendMessage(message);
// 更换策略,使用语音电话通知
noticeCenter.setNotify(new Call());
noticeCenter.sendMessage(message);
}
}
输出结果为:
[老张]使用[短信]通知[大A], 通知内容为[明天要涨啊!!!]
[老张]使用[电话]通知[大A], 通知内容为[明天要涨啊!!!]
如果后面由新增了新的通知渠道,如邮件,只需要新增一个类,实现抽象策略接口即可,如下:
public class Email implements Notify {
@Override
public void doNotify(Message msg) {
System.out.printf("[%s]使用[邮件]通知[%s], 通知内容为[%s]\n", msg.getSender(), msg.getReceiver(), msg.getContext());
}
}
如果想要使用新策略进行消息触达,只需要将通知中心聚合的 notify 属性进行变更即可。
4. 使用场景
策略模式的使用场景是:
- 客户端并不关心具体策略的实现细节,同时策略可能存在多种拓展实现
- 在不同情况下需要使用不同策略实现,同时策略可以被抽象
5. 小结
本文讲述了策略模式,它的定义是——定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
策略模式的优点是:
- 基于多态特性,策略可以根据入参不同实现自由切换
- 避免使用多重判断条件,比如可以基于策略对冗长的 if-else 判断语句进行重构
- 扩展性良好,如果有新的策略实现,只需要新建一个类实现抽象策略接口即可
策略模式的缺点是:
- 每种策略都是一个单独的类,数量多且不可复用
- 违背迪米特法则:所有的策略类都需要对外暴露,因为高层模块必须知道有哪些策略,然后才能决定使用哪个策略
迪米特法则:一个对象应该对其他对象有最少的了解
6. 参考资料
- 《设计模式之禅》(第2版)
最后,本文收录于个人语雀知识库: 我所理解的后端技术,欢迎来访。
文档信息
- 本文作者:Planeswalker23
- 本文链接:https://planeswalker23.github.io/2022/02/06/%E6%88%91%E6%89%80%E7%90%86%E8%A7%A3%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%B3%BB%E5%88%97-%E7%AC%AC14%E7%AF%87-%E5%9F%BA%E4%BA%8E%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E5%AE%9E%E7%8E%B0%E5%A4%9A%E6%B8%A0%E9%81%93%E6%B6%88%E6%81%AF%E8%A7%A6%E8%BE%BE/
- 版权声明:本作品系原创,作者保留所有权利,未经作者允许,禁止转载和演绎。