1. 小聲嗶嗶
策略模式在23種設計模式中屬於行爲型模式,策略模式定義了一系列算法,並將每個算法封裝起來,使他們可以相互替換,且算法的變化不會影響到使用算法的客戶,使用場景如下:
- 如果在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式可以動態地讓一個對象在許多行爲中選擇一種行爲。
- 一個系統需要動態地在幾種算法中選擇一種。
- 如果一個對象有很多的行爲,如果不用恰當的模式,這些行爲就只好使用多重的條件選擇語句來實現。
文中的代碼庫:https://gitee.com/Coline/JobWanted
模塊位置:design模塊的com.coline.design.strategy包中
2. 主菜開盤
2.1. 簡單的策略模式
場景:用戶旅行有不同的方式,騎車和駕車,使用不同的旅行策略有不同的旅行步驟和體驗。
首先看下UML圖:
具體代碼如下:
- 接口:TravelStrategyService用於定義出行策略
package com.coline.design.startegy.intf;
/**
* @author: Coline
* @ClassName: TravelStrategyService
* @Date: 2020/2/7 23:13
* @Description: 旅行方式接口,用於實現策略模式
*/
public interface TravelStrategyService {
/**
* 出行方式
*/
void travelType();
}
- 具體策略類:DriveCarTravelStrategyImpl,CyclingTravelStrategyImpl實現了TravelStrategyService接口,並具體實現了travelType方法,travelType方法可以理解爲具體的業務方法。
package com.coline.design.startegy.impl;
import com.coline.design.startegy.intf.TravelStrategyService;
/**
* @author: Coline
* @ClassName: DriveCarTravelStrategy
* @Date: 2020/2/7 23:21
* @Description: 駕車出行策略
*/
public class DriveCarTravelStrategyImpl implements TravelStrategyService {
@Override
public void travelType() {
System.out.println("-------------DriveCarTravelStrategyImpl---------------");
System.out.println("get car");
System.out.println("drive car and enjoy the scenery");
}
}
package com.coline.design.startegy.impl;
import com.coline.design.startegy.intf.TravelStrategyService;
/**
* @author: Coline
* @ClassName: CyclingTravelStrategy
* @Date: 2020/2/7 23:19
* @Description: 騎車出行策略
*/
public class CyclingTravelStrategyImpl implements TravelStrategyService{
@Override
public void travelType() {
System.out.println("--------------CyclingTravelStrategyImpl---------------");
System.out.println("get bike");
System.out.println("Cycling and enjoy the scenery");
}
}
- 上下文:Context類,Context上下文角色,也叫Context封裝角色,起承上啓下的作用,屏蔽高層模塊對策略、算法的直接訪問,封裝可能存在的變化。
package com.coline.design.startegy.simplycontext;
import com.coline.design.startegy.intf.TravelStrategyService;
/**
* @author: Coline
* @ClassName: Context
* @Date: 2020/2/7 23:32
* @Description: 策略模式中必備的策略執行類
*/
public class Context {
TravelStrategyService travelStrategyService;
public void setTravelStrategyService(TravelStrategyService travelStrategyService) {
this.travelStrategyService = travelStrategyService;
}
public void doAction(){
this.travelStrategyService.travelType();
}
}
- 測試類:SimplyStrategyTest測試類
package com.coline.design.startegy.test;
import com.coline.design.startegy.impl.CyclingTravelStrategyImpl;
import com.coline.design.startegy.impl.DriveCarTravelStrategyImpl;
import com.coline.design.startegy.intf.TravelStrategyService;
import com.coline.design.startegy.simplycontext.Context;
import org.junit.Test;
/**
* @author: Coline
* @ClassName: SimplyStrategyTest
* @Date: 2020/2/7 23:38
* @Description: 簡單測試策略模式,本應放在test包中,爲了模塊規劃暫放此處
*/
public class SimplyStrategyTest {
@Test
public void testStrategy(){
TravelStrategyService driveCarTravelStrategyImpl = new DriveCarTravelStrategyImpl();
Context context = new Context();
context.setTravelStrategyService(driveCarTravelStrategyImpl);
context.doAction();
TravelStrategyService cyclingTravelStrategyImpl = new CyclingTravelStrategyImpl();
context.setTravelStrategyService(cyclingTravelStrategyImpl);
context.doAction();
}
}
運行結果如下:
2.2. 項目中使用策略模式
項目中使用策略模式的思路,結合工廠設計模式,將不同的出行策略先在工廠類中初始化,初始化的容器我們選擇Map,方便後續獲取具體策略實現,代碼如下:
package com.coline.design.startegy.factory;
import com.coline.design.startegy.intf.TravelStrategyService;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author: Coline
* @ClassName: TravelStrategyFactory
* @Date: 2020/2/7 23:24
* @Description: 正式項目中使用類工廠模式獲取對象(真正的工廠模式的工廠類是創建對象)
*/
public class TravelStrategyFactory {
/**
* 用於存放具體的策略對象,策略對象的初始化方式爲實現InitializingBean接口,
* 在afterPropertiesSet方法中將對象設置到Map中
*/
private static ConcurrentHashMap<String, TravelStrategyService> strategyMap = new ConcurrentHashMap<>();
public static void setStrategyImpl(String type, TravelStrategyService travelStrategyService) {
strategyMap.put(type, travelStrategyService);
}
public static TravelStrategyService getStrategyImpl(String type) {
return strategyMap.get(type);
}
}
首先這個工廠模式的實現類並不是真正意義上的工廠類,因爲具體創建對象是交由Spring容器處理,且初始化Map的工作也是不是在該類中處理(其實可以通過自定義標籤掃描加載需要初始化的策略對象,限於篇幅不在這邊實現)
那麼問題來了,工廠類中的Map中存放的對象的設置在哪裏進行呢,最簡單的方法就是實現Spring本身的InitializingBean接口,在容器初始化的時候設置策略對象到Map中。代碼如下:
package com.coline.design.startegy.impl;
import com.coline.design.startegy.factory.TravelStrategyFactory;
import com.coline.design.startegy.intf.TravelStrategyService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
/**
* @author: Coline
* @ClassName: CyclingTravelStrategy
* @Date: 2020/2/7 23:19
* @Description: 騎車出行策略
*/
@Service
public class CyclingTravelStrategyImpl implements TravelStrategyService, InitializingBean {
@Override
public void travelType() {
System.out.println("--------------CyclingTravelStrategyImpl---------------");
System.out.println("get bike");
System.out.println("Cycling and enjoy the scenery");
}
/**
* Spring容器加載時將本策略對象設置到{@link TravelStrategyFactory.strategyMap}
*/
@Override
public void afterPropertiesSet() throws Exception {
TravelStrategyFactory.setStrategyImpl("bike", this);
}
}
package com.coline.design.startegy.impl;
import com.coline.design.startegy.factory.TravelStrategyFactory;
import com.coline.design.startegy.intf.TravelStrategyService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
/**
* @author: Coline
* @ClassName: DriveCarTravelStrategy
* @Date: 2020/2/7 23:21
* @Description: 駕車出行策略
*/
@Service
public class DriveCarTravelStrategyImpl implements TravelStrategyService, InitializingBean {
@Override
public void travelType() {
System.out.println("-------------DriveCarTravelStrategyImpl---------------");
System.out.println("get car");
System.out.println("drive car and enjoy the scenery");
}
/**
* Spring容器加載時將本策略對象設置到{@link TravelStrategyFactory.strategyMap}
*/
@Override
public void afterPropertiesSet() throws Exception {
TravelStrategyFactory.setStrategyImpl("Drive", this);
}
}
測試類:
package com.coline.design.startegy.test;
import com.coline.design.startegy.factory.TravelStrategyFactory;
import com.coline.design.startegy.impl.CyclingTravelStrategyImpl;
import com.coline.design.startegy.impl.DriveCarTravelStrategyImpl;
import com.coline.design.startegy.intf.TravelStrategyService;
import com.coline.design.startegy.model.Traveler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author: Coline
* @ClassName: SpringStrategyTest
* @Date: 2020/2/8 21:00
* @Description: 測試類,用於測試策略模式在項目中的使用
* 我的博客:https://blog.csdn.net/u011294519/article/details/104228067
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
TravelStrategyFactory.class,
CyclingTravelStrategyImpl.class,
DriveCarTravelStrategyImpl.class
})
public class SpringStrategyTest {
private Traveler traveler;
@Before
public void setTraveler(){
traveler = new Traveler();
traveler.setTravelType("bike");
}
@Test
public void testTravelStrategy(){
TravelStrategyService travelStrategyService = TravelStrategyFactory.getStrategyImpl(traveler.getTravelType());
travelStrategyService.travelType();
}
}
運行結果: