在講策略模式之前,我先來講一下我作業中遇到的一個案例:
Demo
作業中有一個專案是協同辦公軟體,里面有一個動態功能,需要將客戶對專案的不同操作的動態即時同步出來,假設有以下四個動態:新增了xx專案,更新了xx專案,洗掉了xx專案,還原了xx專案,
如果是有你來做,你會怎么做?
整理了最新2020整理收集的一線互聯網公司面試真題(都整理成檔案),有很多干貨,包含netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,我感覺在面試這塊講的非常清楚:
有需要的小伙伴可以戳這里免費領取,暗號:CSDN


我們很有可能寫出下面的代碼:
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<MessageModel> 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<MessageModel> messageModels) throws Exception {
return messageStrategy.batchSaveMessage(messageModels);
}
}
此時,只需要在具體的業務邏輯里呼叫即可,如圖所示:

好啦,策略模式就講到這,我們下期見~
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/227571.html
標籤:其他
