在讲策略模式之前,我先来讲一下我工作中遇到的一个案例:
Demo
工作中有一个项目是协同办公软件,里面有一个动态功能,需要将客户对项目的不同操作的动态即时同步出来。假设有以下四个动态:新增了xx项目,更新了xx项目,删除了xx项目,还原了xx项目。
如果是有你来做,你会怎么做?
我们很有可能写出下面的代码:
public class Message {
public void changeMessage(int flag){
if (flag.equals(MessageEnum.PROJECT_UPDATE.getCode())){
// 动态显示:更新了xx项目
messageEntity.setContent(userName+MessageEnum.PROJECT_UPDATE.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
if (flag.equals(MessageEnum.PROJECT_INSERT.getCode())){
// 动态显示:新增了xx项目
messageEntity.setContent(userName+MessageEnum.PROJECT_INSERT.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
if (flag.equals(MessageEnum.PROJECT_DELETE.getCode())){
//动态显示:删除了xx项目
messageEntity.setContent(userName+MessageEnum.PROJECT_DELETE.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
if (flag.equals(MessageEnum.PROJECT_BACK.getCode())) {
// 动态显示:还原了xx项目
messageEntity.setContent(userName+MessageEnum.PROJECT_BACK.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
}
}
经过测试,上面的代码工作的很好,可是上面的代码是有问题的。上面存在的问题:把不同动态信息落存的算法都放在了同一个方法里面,使得该方法很是庞大(现在是只是一个演示,所以看起来还不是很臃肿,假如还有)。
下面看一下上面的改进,我们把不同动态的算法都单独作为一个方法
public class Message {
public void changeMessage(int flag,MessageModel message){
if (flag.equals(MessageEnum.PROJECT_UPDATE.getCode())){
// 动态显示:更新了xx项目
updateMessage(message)
}
if (flag.equals(MessageEnum.PROJECT_INSERT.getCode())){
// 动态显示:新增了xx项目
saveMessage(message)
}
if (flag.equals(MessageEnum.PROJECT_DELETE.getCode())){
//动态显示:删除了xx项目
deleteMessage(MessageModel message)
}
if (flag.equals(MessageEnum.PROJECT_BACK.getCode())) {
// 动态显示:还原了xx项目
backMessage(MessageModel message)
}
}
/**
* 显示[新增了xx项目]的算法
*/
private void saveMessage(MessageModel message) {
messageEntity.setContent(userName+MessageEnum.PROJECT_INSERT.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
/**
* 显示[更新了xx项目]的算法
*/
private void updateMessage(MessageModel message) {
messageEntity.setContent(userName+MessageEnum.PROJECT_UPDATE.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
/**
* 显示[删除了xx项目]的算法
*/
private void deleteMessage(MessageModel message) {
messageEntity.setContent(userName+MessageEnum.PROJECT_DELETE.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
/**
* 显示[还原了xx项目]的算法
*/
private void c {
messageEntity.setContent(userName+MessageEnum.PROJECT_BACK.getMessage()+" "+message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
}
}
上面的代码比刚开始的时候要好一点,它把每个具体的算法都单独抽出来作为一个方法,当某一个具体的算法有了变动的时候,只需要修改响应的动态算法就可以了。
但是改进后的代码还是有问题的,那有什么问题呢?
1.当我们新增一个动态信息的时候,首先要添加一个该动态信息的算法方法,然后再changeMessage方法中再加一个else if的分支,是不是感觉很是麻烦呢?而且这也违反了设计原则之一的开闭原则(open-closed-principle).
开闭原则:
对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。
对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。
2.我们经常会面临这样的情况,如果要修改if else里面的一个分支信息,那么我们得先找到具体的分支信息,了解他的逻即,在对代码进行修改,很是麻烦!!!
那有没有什么办法使得我们的动态模块即可扩展、可维护,又可以方便的响应变化呢?当然有解决方案啦,就是我们下面要讲的策略模式。
定义:
策略模式定义了一系列的算法,并将每一个算法封装起来,使每个算法可以相互替代,使算法本身和使用算法的客户端分割开来,相互独立。
结构:
1.策略接口角色IStrategy:用来约束一系列具体的策略算法,策略上下文角色ConcreteStrategy使用此策略接口来调用具体的策略所实现的算法。
2.具体策略实现角色ConcreteStrategy:具体的策略实现,即具体的算法实现。
3.策略上下文角色StrategyContext:策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。
策略模式代码的一般通用实现:
策略接口
package strategy.examp01;
//策略接口
public interface IStrategy {
//定义的抽象算法方法 来约束具体的算法实现方法
public void algorithmMethod();
}
具体的策略实现:
package strategy.examp01;
// 具体的策略实现
public class ConcreteStrategy implements IStrategy {
//具体的算法实现
@Override
public void algorithmMethod() {
System.out.println("this is ConcreteStrategy method...");
}
}
package strategy.examp01;
// 具体的策略实现2
public class ConcreteStrategy2 implements IStrategy {
//具体的算法实现
@Override
public void algorithmMethod() {
System.out.println("this is ConcreteStrategy2 method...");
}
}
策略上下文:
package strategy.examp01;
/**
* 策略上下文
*/
public class StrategyContext {
//持有一个策略实现的引用
private IStrategy strategy;
//使用构造器注入具体的策略类
public StrategyContext(IStrategy strategy) {
this.strategy = strategy;
}
public void contextMethod(){
//调用策略实现的方法
strategy.algorithmMethod();
}
}
外部客户端:
package strategy.examp01;
//外部客户端
public class Client {
public static void main(String[] args) {
//1.创建具体测策略实现
IStrategy strategy = new ConcreteStrategy2();
//2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
StrategyContext ctx = new StrategyContext(strategy);
//3.调用上下文对象的方法来完成对具体策略实现的回调
ctx.contextMethod();
}
}
策略模式的优点:
- 策略模式的功能就是通过抽象、封装来定义一系列的算法,使得这些算法可以相互替换,所以为这些算法定义一个公共的接口,以约束这些算法的功能实现。如果这些算法具有公共的功能,可以将接口变为抽象类,将公共功能放到抽象父类里面。
- 策略模式的一系列算法是可以相互替换的、是平等的,写在一起就是if-else组织结构,如果算法实现里又有条件语句,就构成了多重条件语句,可以用策略模式,避免这样的多重条件语句。
- 扩展性更好:在策略模式中扩展策略实现非常的容易,只要新增一个策略实现类,然后在使用策略实现的地方,使用这个新的策略实现就好了。
策略模式的缺点:
- 客户端必须了解所有的策略,清楚它们的不同:
如果由客户端来决定使用何种算法,那客户端必须知道所有的策略,清楚各个策略的功能和不同,这样才能做出正确的选择,但是这暴露了策略的具体实现。
- 增加了对象的数量:
由于策略模式将每个具体的算法都单独封装为一个策略类,如果可选的策略有很多的话,那对象的数量也会很多。
- 只适合偏平的算法结构:
由于策略模式的各个策略实现是平等的关系(可相互替换),实际上就构成了一个扁平的算法结构。即一个策略接口下面有多个平等的策略实现(多个策略实现是兄弟关系),并且运行时只能有一个算法被使用。这就限制了算法的使用层级,且不能被嵌套。
实操
那么我们对动态模块采用策略模式的方式进行修改(以动态展示[新增了XX项目]为例)
公共动态策略接口
/**
* @ClassName MessageStrategy
* @Description 动态策略接口(此处采用策略模式)
* @Author HeX
* @Date 2020/2/26 15:23
* @Version 1.0
**/
public interface MessageStrategy {
/**
* 新增动态
* @param messageModels
* @return
* @throws Exception
*/
int batchSaveMessage(List messageModels) throws Exception;
}
[新增xxx项目]策略实现
/**
* @ClassName SaveProjectStrategy
* @Description 保存【新增项目动态】策略
* @Author HeX
* @Date 2020/2/26 15:27
* @Version 1.0
**/
public class SaveProjectMessageStrategy implements MessageStrategy {
@Override
public int batchSaveMessage(MessageModel message) throws Exception {
// 新增了项目
messageEntity.setContent(userName + MessageEnum.PROJECT_INSERT.getMessage() + " " + message.getName());
messageEntity.setSource(MessageSourceEnum.SOURCE_PROJECT.getCode());
messageEntity.setTitle(MessageEnum.MESSAGE_TITLE.getMessage());
return 0;
}
}
动态策略上下文
/**
* @ClassName MessageContext
* @Description 动态上下文角色
* @Author HeX
* @Date 2020/2/26 15:58
* @Version 1.0
**/
public class MessageContext {
/**
* 持有一个具体的动态策略
*/
private MessageStrategy messageStrategy;
/**
* 注入动态策略
* @param messageStrategy
*/
public MessageContext(MessageStrategy messageStrategy){
this.messageStrategy = messageStrategy;
}
/**
* 回调具体动态策略的方法
* @param messageModels
* @return
* @throws Exception
*/
public int saveMessage(List messageModels) throws Exception {
return messageStrategy.batchSaveMessage(messageModels);
}
}
此时,只需要在具体的业务逻辑里调用即可,如图所示:
好啦,策略模式就讲到这,我们下期见~
零基础学习Java编程,考研加入我的十年Java学习园地。