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();
}
}
运行结果: