策略模式
原理
Strategy Design Pattern 策略模式
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
定义一族算法类,将每个算法分别封装起来,让它们可以互相替换
策略模式实际上是解耦策略的定义、创建、使用
- 策略的定义指的是策略类和策略接口,策略模式的骨架
- 策略的创建指的是通过工厂类或工厂的工厂将策略的创建和使用通过查表法关联到一起
- 策略的使用在业务逻辑中通过工厂类获取策略的运行时状态并执行,运行时可根据配置或用户输入动态指定
经典应用
分创建,定义,使用,每次拓展需要新增策略类和修改工厂类,利用反射可以实现扩展开放对修改关闭。
代码
// 定义
public interface Strategy {
void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
//具体的算法...
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
//具体的算法...
}
}
// 创建
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
// 根据 type 获取策略类
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return strategies.get(type);
}
}
// 使用
// 运行时动态确定,根据配置文件的配置决定使用哪种策略
public static void main(String[] args) throws Exception {
Strategy strategy = null;
Properties props = new Properties();
props.load(new FileInputStream("./config.properties"));
String type = props.getProperty("eviction_type");
strategy = StrategyFactory.getStrategy(type);
strategy.algorithmInterface();
//...
}
// 非运行时动态确定,在代码中指定使用哪种策略
public static void main(String[] args) {
//...
Strategy strategy = new ConcreteStrategyA();
strategy.algorithmInterface();
//...
}
函数式接口和枚举策略模式
策略模式的简单应用场景:
- map + 函数接口实现,新增策略类则新增方法修改 map
// 接口用于暴露调用方法
public interface TaskCenterService {
Boolean sendFunction(Integer id, TaskSendRewardDto taskSendRewardDto);
}
// 实现类
@Service
public class TaskCenterServiceImpl implements TaskCenterService {
private Map<Integer, Function<TaskSendRewardDto,Boolean>> sendGiftFunction = new HashMap<>(); ;
public TaskCenterServiceImpl(){
//0银币/1金币/2礼物/3道具
sendGiftFunction.put(0,this::sendSilver);
sendGiftFunction.put(1,this::sendGold);
sendGiftFunction.put(2,this::sendGift);
sendGiftFunction.put(3,this::sendDepProp);
}
private Boolean sendSilver(TaskSendRewardDto taskSendRewardDto){
return true;
}
private Boolean sendGold(TaskSendRewardDto taskSendRewardDto){
return true;
}
private Boolean sendGift(TaskSendRewardDto taskSendRewardDto){
return true;
}
private Boolean sendDepProp(TaskSendRewardDto taskSendRewardDto){
return true;
}
@Override
public Boolean sendFunction(Integer id, TaskSendRewardDto taskSendRewardDto) {
return sendGiftFunction.get(id).apply(taskSendRewardDto);
}
}
// 使用
@RequiredArgsConstructor
@Component
@Slf4j
public class DepPropAward implements IAwardDistribute {
@NonNull
private TaskCenterService taskCenterService;
@Override
public void distribute(AwardInfo awardInfo) throws AwardDistributeException {
TaskSendRewardDto dto = new TaskSendRewardDto();
try {
BeanUtils.copyProperties(dto, awardInfo);
} catch (IllegalAccessException | InvocationTargetException e) {
log.error("DepPropAward-copyProperties-error:", e);
throw new AwardDistributeException(e);
}
taskCenterService.sendFunction(3, dto);
}
}
public enum JumpRuleEnum {
INDEX_PAGE("首页", 1) {
@Override
public String backUrl(JumpParamDto dto) {
return null;
}
},
STICKER_DETAILS_PAGE("贴纸详情", 28) {
@Override
public String backUrl(JumpParamDto dto) {
return null;
}
},
HEAD_SCULPTURE_PAGE("头像框", 29) {
@Override
public String backUrl(JumpParamDto dto) {
return null;
}
},
SYSTEM_PAGE("系统消息", 98) {
@Override
public String backUrl(JumpParamDto dto) {
return null;
}
};
private String msg;
private Integer type;
public static JumpParamEnum genJumpEnum(String name) {
return JumpRuleEnum.valueOf(name);
}
JumpRuleEnum(String msg, Integer type) {
this.msg = msg;
this.type = type;
}
/**
* 抽象方法 枚举策略子类必须实现
*/
public abstract String backUrl(JumpParamDto dto);
}
// 使用
String url = JumpRuleEnum.genJumpEnum(name).backUrl(dto);
总结
- 策略模式定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端
- 策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的
- 策略模式满足开闭原则添加新策略的时候,最小化、集中化代码改动,减少引入 bug 的风险
- 如果 if-else 分支判断不复杂、代码不多就不要过度设计使用策略模式,遵循 KISS 原则
|