在开发过程中我们可能会遇到这样的一种情况,在处理问题的环节中可能要面临不同的选择,而这个选择掌控在调用者的手中,此时我们在代码中如何处理(例如一个发送消息的功能,需要支持webSocket发送和邮件发送)心想这不就是分支嘛这都不会就不要玩啦,随后写下了如下伪代码:
if(消息类型 == webSocket){
sendWebSocketMeaasge();
}else if(消息类型 == email){
sendEmailMeaasge();
}
sendWebSocketMeaasge(){
//发送WebSocket消息逻辑
}
sendEmailMeaasge(){
//发送邮件消息逻辑
}
就说功能实现没实现吧,过两天产品说又要支持发送短信消息,心想小事情这不就多个if的事吗,于是就有了如下伪代码:
if(消息类型 == webSocket){
sendWebSocketMeaasge();
}else if(消息类型 == email){
sendEmailMeaasge();
}else if(消息类型 == sms){
sendSmsMeaasge();
}
sendWebSocketMeaasge(){
//发送WebSocket消息逻辑
}
sendEmailMeaasge(){
//发送邮件消息逻辑
}
sendSmsMeaasge(){
//发送短信消息逻辑
}
虽然说功能实现了,但是对于一个优雅的coder来说是否有更好的实现方式,当然这就是今天的主角策略模式。
我们在策略模式中需要定义一系列的策略以及策略管理的工厂。通过使用策略模式,可以在运行时根据需要选择不同的策略,而不需要修改调用的代码,并且可以替换长长的if else。
针对上述场景我们首先定义一个发送消息的接口后续所有发送消息的策略都需要实现该接口:
public interface SendMessageInterface {
/**
* 获取策略名称
* @return 略名称
*/
String getSendTypeName();
/**
* 发送消息
* @param message 消息
*/
void sendMessage(String message);
}
紧接着定义各自的策略实现类sendMessage方法去实现各自的发送消息逻辑:
/**
* 发送WebSocket消息策略
*/
public class SendWebSocketMessage implements SendMessageInterface{
@Override
public String getSendTypeName() {
return "webSocket";
}
@Override
public void sendMessage(String message) {
System.out.println(getSendTypeName()+":"+message);
}
}
/**
* 发送邮件消息策略
*/
public class SendEmailMessage implements SendMessageInterface{
@Override
public String getSendTypeName() {
return "email";
}
@Override
public void sendMessage(String message) {
System.out.println(getSendTypeName()+":"+message);
}
}
/**
* 发送短信消息策略
*/
public class SendSmsMessage implements SendMessageInterface{
@Override
public String getSendTypeName() {
return "sms";
}
@Override
public void sendMessage(String message) {
System.out.println(getSendTypeName()+":"+message);
}
}
定义策略工厂管理策略:
public class MessageTypeFactory {
public static final Map<String,SendMessageInterface> MESSAGES_TYPE_MAP = new ConcurrentHashMap<>();
//实例化策略和注册
static {
SendWebSocketMessage sendWebSocketMessage = new SendWebSocketMessage();
SendEmailMessage sendEmailMessage = new SendEmailMessage();
SendSmsMessage sendSmsMessage = new SendSmsMessage();
MessageTypeFactory.register(sendWebSocketMessage.getSendTypeName(), sendWebSocketMessage);
MessageTypeFactory.register(sendEmailMessage.getSendTypeName(), sendEmailMessage);
MessageTypeFactory.register(sendSmsMessage.getSendTypeName(), sendSmsMessage);
}
public static void register(String name,SendMessageInterface type){
MESSAGES_TYPE_MAP.put(name,type);
}
public static void sendMessageByName(String name,String messages){
SendMessageInterface sendMessageInterface = MESSAGES_TYPE_MAP.get(name);
if (Objects.isNull(sendMessageInterface)) {
throw new RuntimeException("通知类型不存在");
}
sendMessageInterface.sendMessage(messages);
}
}
测试主方法:
public static void main(String[] args) {
MessageTypeFactory.sendMessageByName("webSocket","这是一条webSocket消息");
MessageTypeFactory.sendMessageByName("email","这是一条邮件消息");
MessageTypeFactory.sendMessageByName("sms","这是一条短信消息");
}
执行结果:
后续的扩展只需要新增策略类和在工厂注册即可,完美对扩展开放。
在实际项目中如果是spring项目则代码还可以优化,只需要修改接口为:
public interface SendMessageInterface {
/**
* 自动注册
*/
@PostConstruct
default void register() {
MessageTypeFactory.register(getSendTypeName(), this);
}
/**
* 获取策略名称
* @return 略名称
*/
String getSendTypeName();
/**
* 发送消息
* @param message 消息
*/
void sendMessage(String message);
}
可以看到增加了一个注册函数,spring在生成对象时会调用该方法将当前对象注册到工厂中,MessageTypeFactory中就不需要在静态代码块中实例化对象和注册了,这一切都交给spring去管理,新增加策略时只需新增策略类其他的代码丝毫不用修改。
优点:
- 符合设计原则,对扩展开放,扩展性好。
- 减少if else代码块。
缺点:
- 策略类会随着情况增多。
- 策略类需要对外部开放。