使用策略模式前先回顾一下策略模式是什么,只想看在spring中如何使用,可直接跳转到第三章。
一、策略模式是什么?
定义
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户(满足开闭原则)。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
结构
策略模式的主要角色如下:
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
简单类图如下:
定义一个抽象策略(Strategy)类接口Strategy:
public interface Strategy {
void show();
}
定义具体策略角色(Concrete Strategy):
public class StrategyA implements Strategy {
public void show() {
System.out.println("StrategyA.show()");
}
}
//为中秋准备的促销活动B
public class StrategyB implements Strategy {
public void show() {
System.out.println("StrategyB.show()");
}
}
//为圣诞准备的促销活动C
public class StrategyC implements Strategy {
public void show() {
System.out.println("StrategyC.show()");
}
}
定义环境角色(Context)类SalesMan:
public class SalesMan {
//持有抽象策略角色的引用
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
//调用具体实现类
public void salesManShow(){
strategy.show();
}
}
public class Client {
public static void main(String[] args) {
SalesMan salesMan = new SalesMan(new StrategyA());
salesMan.salesManShow();
System.out.println("==============");
salesMan.setStrategy(new StrategyB());
salesMan.salesManShow();
System.out.println("==============");
salesMan.setStrategy(new StrategyC());
salesMan.salesManShow();
}
}
二、策略模式的优缺点与使用场景
优缺点
优点:
- 策略类之间可以自由切换
由于策略类都实现同一个接口,所以使它们之间可以自由切换。 - 易于扩展
增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“ - 避免使用多重条件选择语句(if else),充分体现面向对象设计思想。
缺点:
客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
策略模式将造成产生很多策略类(可以通过使用享元模式在一定程度上减少对象的数量)
使用场景
- 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
- 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
- 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
- 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
- 总之当出现如 if…else 语句、switch…case 语句时,就可以使用策略模式消除多重条件语句。
三、在Spring中使用策略模式
如现在有一个计算折扣的业务场景,并且需要动态的使用多种折扣策略计算。
1.抽象策略类
代码如下(示例):
import java.util.List;
import java.util.Map;
public interface DiscountService {
List getDiscountResult(Map<String, Object> params);
}
2.具体策略类
代码如下(示例):
import com.qbh.design.strategy.DiscountService;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Component
public class DiscountAServiceImpl implements DiscountService {
@Override
public List getDiscountResult(Map<String, Object> params) {
//进行折扣相关业务处理
System.out.println("执行折扣A");
return Collections.emptyList();
}
}
@Component
public class DiscountBServiceImpl implements DiscountService {
@Override
public List getDiscountResult(Map<String, Object> params) {
//进行折扣相关业务处理
System.out.println("执行折扣B");
return Collections.emptyList();
}
}
@Component
public class DiscountCServiceImpl implements DiscountService {
@Override
public List getDiscountResult(Map<String, Object> params) {
//进行折扣相关业务处理
System.out.println("执行折扣C");
return Collections.emptyList();
}
}
3.环境类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class DiscountContext {
//当一个接口有多个实现类时,Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
@Autowired
private Map<String, DiscountService> discountServiceMap;
/**
*
* @param key 实现类名
* @param params 参数
* @return
*/
public List execute(String key, Map<String, Object> params) {
DiscountService service = discountServiceMap.get(key);
return service.getDiscountResult(params);
}
}
在以上 DiscountContext 类中,Spring 会自动将 DiscountService 接口的实现类注入到discountServiceMap这个Map中。
前提是实现 DiscountService 接口的实现类得是交给Spring 容器管理的。
discountServiceMap的key值就是实现类的 bean id,也可以用@Component(“value”)的方式设置,像我上面直接用默认的方式的话,就是首字母小写。value值则为对应的策略实现类。
4.枚举
import cn.hutool.core.util.ObjectUtil;
public enum DiscountEnum {
strategyA("discountA", "discountAServiceImpl"),
strategyB("discountB", "discountBServiceImpl"),
strategyC("discountC", "discountCServiceImpl"),;
private String name;
private String discountImplName;
public static String getDiscountImplName(String name) {
if (ObjectUtil.isEmpty(name)) {
return null;
}
for (DiscountEnum aEnum : DiscountEnum.values()) {
if (aEnum.getName().equals(name)) {
return aEnum.getDiscountImplName();
}
}
return null;
}
DiscountEnum(String name, String discountImplName) {
this.name = name;
this.discountImplName = discountImplName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDiscountImplName() {
return discountImplName;
}
public void setDiscountImplName(String discountImplName) {
this.discountImplName = discountImplName;
}
}
5.服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements ITestService {
@Autowired
private DiscountContext discountContext;
@Override
public void test(String name,Map<String, Object> params) {
discountContext.execute(DiscountEnum.getDiscountImplName(name),params);
}
}