在上一篇咱們學習了工廠模式的應用,這一篇咱們會接着學習策略模式在實際項目當中的應用。java
首先,在講策略模式以前,咱們看一下平常生活中關於大型超市的會員策略:
商場每每根據不一樣的客戶制定不一樣的商品報價策略,好比針對普通會員用戶打9折扣,針對VIP會員打8折,針對超級會員用戶打5折...web
如今咱們要作一個報價管理的模塊,簡要點就是要針對不一樣的會員客戶,提供不一樣的折扣報價。算法
咱們通常的代碼多是這樣子的:spring
package com.MyMineBug.demoRun.strategy;
import java.math.BigDecimal;
public class MemberManagement {
public BigDecimal calculatePrice(String customType){
if ("普通會員用戶".equals(customType)) {
System.out.println("抱歉!普通會員用戶打9折!");
return new BigDecimal("90");
}else if ("VIP會員".equals(customType)) {
System.out.println("恭喜你!VIP會員打8折!");
return new BigDecimal("80");
}else if("超級會員用戶".equals(customType)){
System.out.println("恭喜你!超級會員用戶打5折!");
return new BigDecimal("50");
}
//普通用戶都是原價
return new BigDecimal("100");
}
}
複製代碼
通過測試,上面的代碼工做的很好,但是上面的代碼是有問題的。上面存在的問題:把不一樣客戶的報價的算法都放在了同一個方法裏面,使得該方法非常龐大(如今是隻是一個演示,因此看起來還不是很臃腫)。編程
若是後面繼續對其進行優化,多是會將每種會員提取出來,做爲一種單獨的算法,可是這樣會違反咱們的開閉原則。多線程
開閉原則:1.對於擴展是開放的(Open for extension)。這意味着模塊的行爲是能夠擴展的。當應用的需求改變時,咱們能夠對模塊進行擴展,使其具備知足那些改變的新行爲。也就是說,咱們能夠改變模塊的功能。app
2.對於修改是關閉的(Closed for modification)。對模塊行爲進行擴展時,沒必要改動模塊的源代碼或者二進制代碼。 ide
那有沒有什麼辦法使得咱們的報價管理便可擴展、可維護,又能夠方便的響應變化呢?固然有解決方案啦,就是咱們下面要講的策略模式。函數
策略模式定義了一系列的算法,並將每個算法封裝起來,使每一個算法能夠相互替代,使算法自己和使用算法的客戶端分割開來,相互獨立。學習
1.策略接口角色IStrategy:用來約束一系列具體的策略算法,策略上下文角色StrategyContext使用此策略接口來調用具體的策略所實現的算法。
2.具體策略實現角色ConcreteStrategy:具體的策略實現,即具體的算法實現。
3.策略上下文角色StrategyContext:策略上下文,負責和具體的策略實現交互,一般策略上下文對象會持有一個真正的策略實現對象,策略上下文還可讓具體的策略實現從其中獲取相關數據,回調策略上下文對象的方法。
這裏實現,咱們是經過spring註解的方式,將咱們策略的實現類注入到map裏面,項目啓動時,會自動將IStrategy的實現類注入到策略上下文StrategyContext,具體實現以下:
公共報價策略接口:
package com.MyMineBug.demoRun.strategy;
import java.math.BigDecimal;
public interface IStrategy {
/** * 計算價格 * @return */
public BigDecimal calculatePrice();
}
複製代碼
普通會員用戶報價策略實現:
package com.MyMineBug.demoRun.strategy;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
@Component("GeneralMember")
public class GeneralMember implements IStrategy{
@Override
public BigDecimal calculatePrice() {
return new BigDecimal("90");
}
}
複製代碼
VIP會員用戶策略實現:
package com.MyMineBug.demoRun.strategy;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
@Component("VipMember")
public class VipMember implements IStrategy{
@Override
public BigDecimal calculatePrice() {
return new BigDecimal("80");
}
}
複製代碼
超級會員用戶策略實現:
package com.MyMineBug.demoRun.strategy;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
@Component("SuperMember")
public class SuperMember implements IStrategy{
@Override
public BigDecimal calculatePrice() {
return new BigDecimal("50");
}
}
複製代碼
策略報價上下文:
package com.MyMineBug.demoRun.strategy;
/** * 策略管理器 * @author 18360 * */
import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class StrategyContext {
private final Map<String, IStrategy> strategyMap = new ConcurrentHashMap<String, IStrategy>();
/** * 注入全部實現了IStrategy接口的Bean * @param strategyMap */
@Autowired
public void StrategyInterface(Map<String, IStrategy> strategyMap) {
this.strategyMap.clear();
strategyMap.forEach((k, v)-> this.strategyMap.put(k, v));
}
/** * 計算價格 * @param memberLevel 會員等級 * @return 價格 */
public BigDecimal calculatePrice(String memberLevel) {
if(!StringUtils.isEmpty(memberLevel)){
return strategyMap.get(memberLevel).calculatePrice();
}
return null;
}
}
複製代碼
因爲是spring boot項目,因此咱們能夠在controller層模擬外部不一樣的會員用戶的報價,代碼以下:
package com.MyMineBug.demoRun.controller;
import java.math.BigDecimal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.MyMineBug.demoRun.strategy.StrategyContext;
@RestController
@RequestMapping
public class StrategyController {
@Autowired
private StrategyContext strategyContext;
@RequestMapping("/calculatePrice")
public BigDecimal calculatePrice(@RequestParam("memberLevel") String memberLevel) {
return strategyContext.calculatePrice(memberLevel);
}
@RequestMapping("/whello")
public String hello() {
return "hello run";
}
}
複製代碼
若是啓動spring boot項目報錯,須要在啓動入口加上掃描註解:@ComponentScan(basePackages = {"com.MyMineBug.demoRun"})
啓動服務後,在界面輸入http://localhost:8080/calculatePrice?memberLevel=GeneralMember
輸出:
就是把具體的算法實現從業務邏輯中剝離出來,成爲一系列獨立算法類,使得它們能夠相互替換。
不須要去管如何來實現算法,而是管如何組織和調用這些算法,從而讓咱們的程序結構更加的靈活、可擴展。
咱們前面的第一個報價管理的示例,發現每一個策略算法實現對應的都是在MemberManagement中calculatePrice方法中的if else語句裏面,咱們知道if else if語句裏面的代碼在執行的可能性方面能夠說是平等的,你要麼執行if,要麼執行else,要麼執行else if。
策略模式就是把各個平等的具體實現進行抽象、封裝成爲獨立的算法類,而後經過上下文和具體的算法類來進行交互。各個策略算法都是平等的,地位是同樣的,正是因爲各個算法的平等性,因此它們纔是能夠相互替換的。雖然咱們能夠動態的切換各個策略,可是同一時刻只能使用一個策略。
在多線程編程中,咱們常用線程池來管理線程,以減緩線程頻繁的建立和銷燬帶來的資源的浪費,在建立線程池的時候,常用一個工廠類來建立線程池Executors,實際上Executors的內部使用的是類ThreadPoolExecutor。它有一個最終的構造函數以下:
corePoolSize:線程池中的核心線程數量,即便這些線程沒有任務幹,也不會將其銷燬。
maximumPoolSize:線程池中的最多可以建立的線程數量。
keepAliveTime:當線程池中的線程數量大於corePoolSize時,多餘的線程等待新任務的最長時間。
unit:keepAliveTime的時間單位。
workQueue:在線程池中的線程尚未還得及執行任務以前,保存任務的隊列(當線程池中的線程都有任務在執行的時候,仍然有任務不斷的提交過來,這些任務保存在workQueue隊列中)。
threadFactory:建立線程池中線程的工廠。
handler:當線程池中沒有多餘的線程來執行任務,而且保存任務的多列也滿了(指的是有界隊列),對仍在提交給線程池的任務的處理策略。
RejectedExecutionHandler 是一個策略接口,用在當線程池中沒有多餘的線程來執行任務,而且保存任務的多列也滿了(指的是有界隊列),對仍在提交給線程池的任務的處理策略。
公共策略接口:
這個策略接口有四個實現類:
AbortPolicy:該策略是直接將提交的任務拋棄掉,並拋出RejectedExecutionException異常。
DiscardPolicy:該策略也是將任務拋棄掉(對於提交的任務無論不問,什麼也不作),不過並不拋出異常。
DiscardOldestPolicy:該策略是當執行器未關閉時,從任務隊列workQueue中取出第一個任務,並拋棄這第一個任務,進而有空間存儲剛剛提交的任務。使用該策略要特別當心,由於它會直接拋棄以前的任務。
CallerRunsPolicy:該策略並無拋棄任何的任務,因爲線程池中已經沒有了多餘的線程來分配該任務,該策略是在當前線程(調用者線程)中直接執行該任務。
類ThreadPoolExecutor中持有一個RejectedExecutionHandler接口的引用,以便在構造函數中能夠由外部客戶端本身制定具體的策略並注入。下面看一下其類圖:
策略模式的優勢:
1.策略模式的功能就是經過抽象、封裝來定義一系列的算法,使得這些算法能夠相互替換,因此爲這些算法定義一個公共的接口,以約束這些算法的功能實現。若是這些算法具備公共的功能,能夠將接口變爲抽象類,將公共功能放到抽象父類裏面。
2.策略模式的一系列算法是能夠相互替換的、是平等的,寫在一塊兒就是if-else組織結構,若是算法實現裏又有條件語句,就構成了多重條件語句,能夠用策略模式,避免這樣的多重條件語句。
3.擴展性更好:在策略模式中擴展策略實現很是的容易,只要新增一個策略實現類,而後在使用策略實現的地方,使用這個新的策略實現就行了。
策略模式的缺點:
1.客戶端必須瞭解全部的策略,清楚它們的不一樣: 若是由客戶端來決定使用何種算法,那客戶端必須知道全部的策略,清楚各個策略的功能和不一樣,這樣才能作出正確的選擇,可是這暴露了策略的具體實現。
2.增長了對象的數量: 因爲策略模式將每一個具體的算法都單獨封裝爲一個策略類,若是可選的策略有不少的話,那對象的數量也會不少。
3.只適合偏平的算法結構:因爲策略模式的各個策略實現是平等的關係(可相互替換),實際上就構成了一個扁平的算法結構。即一個策略接口下面有多個平等的策略實現(多個策略實現是兄弟關係),而且運行時只能有一個算法被使用。這就限制了算法的使用層級,且不能被嵌套。
策略模式的本質:分離算法,選擇實現。
若是以爲還不錯,請點個贊!!!
Share Technology And Love Life