今天在写协议解析的代码的时候,遇到某些协议项目比较复杂,需要大量的if判断,所以考虑用策略模式简化代码
枚举策略
其实枚举类中是可以定义方法的,基于这个特性,可以实现策略的枚举。
每一个if 分支可以考虑是一种具体的策略,例如我需要根据不同的CAN报文的can-id进行一个筛选 ,那么可以定义一个CAN-ID策略定义如下
package com.tkelevator.processor;
import com.google.common.io.BaseEncoding;
import com.tkelevator.processor.ioe.CategoryStrategy;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Slf4j
public enum CanIdStrategy {
ID_7D0((byte)0x7d0){
@Override
void process(byte[] data,int elevatorId) {
if (data==null||data.length==0)return ;
String category = BaseEncoding.base16().encode(Arrays.copyOfRange(data, 0, 1)).replaceFirst("^0+", "");
try {
CategoryStrategy.valueOf("CATEGORY_"+category).process(data,elevatorId);
} catch (IllegalArgumentException e) {
log.error("无效category={}",category);
}
}
};
abstract void process(byte[] data,int elevatorId);
@Getter
private byte canId;
CanIdStrategy(byte canId)
{
this.canId=canId;
}
}
在枚举类中注入SpringBean
枚举类是天然的单例模式,其构造方法是私有的交给jvm管理 无法被Spring扫描,所以如果需要在枚举类中使用SpringBean的话,则需要手动注入。
此时可以考虑在枚举类中定义一个静态内部类:
@Component
@DependsOn("redisTemplate")
public static class RedisProvider{
private RedisTemplate<String,Object> redisTemplate;
@Autowired
public RedisProvider(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@PostConstruct
private void setRedis()
{
for (SubCategoryStrategy categoryStrategy : SubCategoryStrategy.values()) {
categoryStrategy.setRedisTemplate(redisTemplate);
}
}
}
然后利用PostConstruct方法来设置Bean的值
SpringBean的注册顺序问题
一般而言,我们不太会遇到这个问题,Spring会自动帮我们解决依赖关系问题,但是在少数场景下,假如我们手动配置类注入,或者存在异步的情况下可能就会遇到这个问题。
举例
@Slf4j
@Component
public class ClientConfig {
public static ConcurrentHashMap<Integer,AioSession> elevatorSessionMap;
static {
elevatorSessionMap=new ConcurrentHashMap<>();
}
@Autowired
EBasicInfoService elevatorService;
@Autowired
private ElevatorProcessor elevatorProcessor;
@PostConstruct
public void init() throws InterruptedException {
log.info("客户端开始初始化");
List<EBasicInfo> list = elevatorService.list();
log.info("查询到{}座电梯",list.size());
while (elevatorSessionMap.size()<list.size())
{
for (EBasicInfo elevator : list) {
if (elevatorSessionMap.get(elevator.getId())!=null)continue;
AioQuickClient client = new AioQuickClient(elevator.getIpAddress(), elevator.getCanPort(), new BasicProtocol(), elevatorProcessor);
client.setReadBufferSize(1000);
try {
AioSession session = client.start();
session.setAttachment(elevator.getId());
elevatorSessionMap.put(elevator.getId(),session);
} catch (IOException e) {
log.error("elevator 连接失败 elevatorId={}",elevator.getId());
}
}
log.info("发起新一轮连接请求");
Thread.sleep(10000);
}
}
}
比如在这个类里面,我其实没有显式的引用到`SubCategoryStrategy.RedisProvider redisProvider;`, 但在后续的流程中其实会使用到,那么这里就需要手动的注入一下 SubCategoryStrategy.RedisProvider redisProvider;,确保这个类在ClientConfig之前被注入,这样的话我后续的枚举类才能够正确的注入RedisTemplate.
所以应该加入
@Autowried
SubCategoryStrategy.RedisProvider redisProvider;