菜单

利用策略模式优化IF-ELSE

duckflew
发布于 2023-08-11 / 66 阅读
0
0

利用策略模式优化IF-ELSE

今天在写协议解析的代码的时候,遇到某些协议项目比较复杂,需要大量的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;


评论