如何重写Java代码避免switch分支与强制类型转换,适配Quarkus CDI场景
解决方案
方案1:访问者模式(零类型转换,无需依赖框架特性)
该方案为纯面向对象设计实现,不依赖CDI能力,所有Service可保持无状态单例:
首先定义访问者接口:
interface ConfigVisitor { String visit(ConfigA configA); String visit(ConfigB configB); }
修改Config基类和子类:
abstract class Config { public abstract String accept(ConfigVisitor visitor); } class ConfigA extends Config { @Override public String accept(ConfigVisitor visitor) { return visitor.visit(this); } } class ConfigB extends Config { @Override public String accept(ConfigVisitor visitor) { return visitor.visit(this); } }
让业务Service实现Visitor接口,保持无状态:
@ApplicationScoped class ServiceA implements ConfigVisitor { @Override public String visit(ConfigA configA) { // 原ServiceA的业务逻辑,直接使用configA无需转换 return ...; } @Override public String visit(ConfigB configB) { throw new UnsupportedOperationException(); } } // 同理实现ServiceB @ApplicationScoped class ServiceB implements ConfigVisitor { @Override public String visit(ConfigA configA) { throw new UnsupportedOperationException(); } @Override public String visit(ConfigB configB) { // 原ServiceB的业务逻辑 return ...; } }
最后实现ServiceRunner:
@ApplicationScoped class ServiceRunner { private final Map<Class<? extends Config>, ConfigVisitor> serviceMap; // Quarkus CDI支持构造器注入所有匹配类型的bean,提前绑定Config类型和对应Service public ServiceRunner(Instance<ConfigVisitor> visitors) { Map<Class<? extends Config>, ConfigVisitor> map = new HashMap<>(); map.put(ConfigA.class, visitors.select(ServiceA.class).get()); map.put(ConfigB.class, visitors.select(ServiceB.class).get()); this.serviceMap = Collections.unmodifiableMap(map); } public String run(Config config) { // 完全不需要类型转换和instanceof判断 return config.accept(serviceMap.get(config.getClass())); } }
方案2:CDI自动注册策略(无需修改现有Config类)
如果不想改动原有Config的继承结构,可利用Quarkus CDI的能力自动注册策略,仅在框架内部封装类型转换,对外完全无感知:
首先定义通用Service接口:
interface ConfigService<T extends Config> { String run(T config); Class<T> getSupportedConfigType(); }
实现对应的业务Service:
@ApplicationScoped class ServiceA implements ConfigService<ConfigA> { @Override public String run(ConfigA configA) { // 业务逻辑 return ...; } @Override public Class<ConfigA> getSupportedConfigType() { return ConfigA.class; } } @ApplicationScoped class ServiceB implements ConfigService<ConfigB> { @Override public String run(ConfigB configB) { // 业务逻辑 return ...; } @Override public Class<ConfigB> getSupportedConfigType() { return ConfigB.class; } }
实现ServiceRunner:
@ApplicationScoped class ServiceRunner { private final Map<Class<? extends Config>, ConfigService<?>> serviceMap; public ServiceRunner(Instance<ConfigService<?>> services) { Map<Class<? extends Config>, ConfigService<?>> map = new HashMap<>(); for (ConfigService<?> service : services) { map.put(service.getSupportedConfigType(), service); } this.serviceMap = Collections.unmodifiableMap(map); } @SuppressWarnings("unchecked") public String run(Config config) { // 此处强转已通过预绑定保证类型安全,对外调用完全无感知 ConfigService<Config> service = (ConfigService<Config>) serviceMap.get(config.getClass()); return service.run(config); } }
两种方案都完全避免了业务代码中的instanceof判断和硬编码分支,新增Config类型时仅需要新增对应Service实现即可,无需修改ServiceRunner代码,符合开闭原则,且所有Service都为无状态单例,适配Quarkus CDI的运行模型。
内容的提问来源于stack exchange,提问作者ra74
相关产品推荐
相关产品推荐

