介绍
在工作中大家肯定会用到策略模式,比如对接不同种类的下单渠道,不同种类的消息通知(短信,mq等)今天就给大家带来一种自认为与Spring结合比较好的实现模式,这种模式的实现得益于同事的指导,今天就分享给大家,期待与大家的共同进步。
策略模式的定义
一组可以相互替换的算法簇,可以供大家根据一定的规则来选择相对应的策略来使用。
策略模式的组成
策略模式一般由以下三个部分组成
- 策略(Strategy):一般都会有个Strategy接口,然后具体的策略都实现Strategy的接口都是策略类;
- 策略容器(Context):存放具体的策略的地方(可以类比古时候装妙计的锦囊);
- 决策策略引擎(策略使用方client) :通过一定的规则来选择具体执行的策略。
策略模式实战编码
-
首先给大家看一下项目的结构示意图
-
其次,再给大家看一下主要类之间的关系图
-
StrategyService类是是策略接口类,KafkaStrategyServiceImpl和SmsStrategyServiceImpl是具体的策略类
-
StrategyServiceFactory相当于策略容器,存放策略规则与策略的对应关系
具体的代码实现如下
/**
* 策略接口类
**/
public interface StrategyService {
void sendMessage(String context);
}
具体的策略实现类SmsStrategyServiceImpl
import com.example.demo.strategy.ForStrategyType;
import com.example.demo.strategy.StrategyService;
import com.example.demo.strategy.StrategyType;
import org.springframework.stereotype.Service;
@Service
@ForStrategyType(
StrategyType.SMS
)
public class SmsStrategyServiceImpl implements StrategyService {
@Override
public void sendMessage(String context) {
System.out.println("sms:"+context);
}
}
具体实现策略类KafkaStrategyServiceImpl
import com.example.demo.strategy.ForStrategyType;
import com.example.demo.strategy.StrategyService;
import com.example.demo.strategy.StrategyType;
import org.springframework.stereotype.Service;
@Service
@ForStrategyType(
StrategyType.MQ
)
public class KafkaStrategyServiceImpl implements StrategyService {
@Override
public void sendMessage(String context) {
System.out.println("mq:"+context);
}
}
策略注册注解类
package com.example.demo.strategy;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface ForStrategyType {
StrategyType[] value();
}
策略注册枚举类
package com.example.demo.strategy;
public enum StrategyType {
SMS(1,"短信"),
MQ(2,"mq消息")
;
StrategyType(int code, String desc) {
this.code = code;
this.desc = desc;
}
private Integer code;
private String desc;
}
策略注册抽象工厂类
package com.example.demo.strategy;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
public abstract class AbstractEnumRegistryBuilder<E extends Enum> {
public <T> Map<E, T> build(T[] beans) {
ImmutableMap.Builder<E, T> mapBuilder = ImmutableMap.builder();
for (T bean : beans) {
Class<?> builderClass = bean.getClass();
E[] keys = extractKeys(builderClass);
for (E key : keys) {
mapBuilder.put(key, bean);
}
}
return mapBuilder.build();
}
public abstract E[] extractKeys(Class<?> builderClass);
}
策略具体注册工厂类
import com.google.common.base.Preconditions;
import org.springframework.stereotype.Component;
@Component
public class StrategyServiceRegistryBuilder extends AbstractEnumRegistryBuilder<StrategyType> {
@Override
public StrategyType[] extractKeys(Class<?> builderClass) {
ForStrategyType forStrategyType = builderClass.getAnnotation(ForStrategyType.class);
Preconditions.checkNotNull(forStrategyType, "Cannot find @StrategyType annotation on %s",
builderClass.getName());
return forStrategyType.value();
}
}
策略容器类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class StrategyServiceFactory {
private final Map<StrategyType, StrategyService> messageServiceMap;
@Autowired
public StrategyServiceFactory(
StrategyServiceRegistryBuilder strategyServiceRegistryBuilder,
StrategyService[] strategyServices) {
messageServiceMap = strategyServiceRegistryBuilder.build(strategyServices);
}
public StrategyService getMessageService(StrategyType strategyType){
StrategyService strategyService = messageServiceMap.get(strategyType);
if(strategyService == null){
throw new RuntimeException("未能找到对应的处理类");
}
return strategyService;
}
}
策略的client
package com.example.demo;
import com.example.demo.strategy.StrategyType;
import com.example.demo.strategy.StrategyServiceFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private StrategyServiceFactory strategyServiceFactory;
@Test
public void contextLoads() {
}
@Test
public void testStrategy(){
strategyServiceFactory.getMessageService(StrategyType.SMS).sendMessage("Hello world");
}
}