无源码场景下多服务多类型对象的多态设计方案咨询
问题
希望用多态实现多服务处理不同类型对象的简洁设计,但无法获取Car、Computer等目标类的源码。具体场景:
PhysicalDetailsService的calculateWeight方法需计算不同对象重量:- Car:重量 = 4 * tierWeight + frameWeight
- Computer:重量 = processorWeight + casingWeight + powerBankWeight
PriceService的calculatePrice方法需以类似逻辑计算Car和Computer的价格
需要解决的核心问题:
- 如何设计服务方法的参数类型及相关类型?
- 无法修改Car、Computer类实现接口的情况下,是否需要定义接口并实现适配器?
- 避免大量
if判断对象类型,是否适合用抽象工厂模式创建服务? - 如何配置Spring Bean服务实现上述设计?
解决方案
一、参数类型与接口定义
先定义业务导向的接口,只暴露核心业务方法,不依赖原有类:
public interface WeightCalculable { double calculateWeight(); } public interface PriceCalculable { BigDecimal calculatePrice(); }
服务方法直接使用这些接口作为参数,实现与具体对象的解耦:
@Service public class PhysicalDetailsService { public double calculateWeight(WeightCalculable calculable) { return calculable.calculateWeight(); } } @Service public class PriceService { public BigDecimal calculatePrice(PriceCalculable calculable) { return calculable.calculatePrice(); } }
二、用适配器模式适配原有类
因为无法修改Car、Computer源码,用适配器模式包装原有对象,让它们实现上述业务接口,完成逻辑适配:
Car的适配器实现
public class CarWeightAdapter implements WeightCalculable { private final Car car; public CarWeightAdapter(Car car) { this.car = car; } @Override public double calculateWeight() { return 4 * car.getTierWeight() + car.getFrameWeight(); } } public class CarPriceAdapter implements PriceCalculable { private final Car car; public CarPriceAdapter(Car car) { this.car = car; } @Override public BigDecimal calculatePrice() { // 此处实现Car的价格计算逻辑 return new BigDecimal(car.getBasePrice()).multiply(new BigDecimal("1.2")); } }
Computer的适配器实现
public class ComputerWeightAdapter implements WeightCalculable { private final Computer computer; public ComputerWeightAdapter(Computer computer) { this.computer = computer; } @Override public double calculateWeight() { return computer.getProcessorWeight() + computer.getCasingWeight() + computer.getPowerBankWeight(); } } public class ComputerPriceAdapter implements PriceCalculable { private final Computer computer; public ComputerPriceAdapter(Computer computer) { this.computer = computer; } @Override public BigDecimal calculatePrice() { // 此处实现Computer的价格计算逻辑 return computer.getComponentPrice().add(computer.getAssemblyFee()); } }
三、用类型映射器替代抽象工厂,消除if判断
抽象工厂在此场景下过于厚重,更轻量的方案是类型映射器:定义一个Bean存储「原有类型 -> 适配器实例」的映射,自动匹配对应适配器:
@Component public class WeightAdapterMapper { private final Map<Class<?>, Function<Object, WeightCalculable>> adapterMap; public WeightAdapterMapper() { adapterMap = new HashMap<>(); adapterMap.put(Car.class, obj -> new CarWeightAdapter((Car) obj)); adapterMap.put(Computer.class, obj -> new ComputerWeightAdapter((Computer) obj)); } public WeightCalculable getAdapter(Object obj) { Function<Object, WeightCalculable> adapterFactory = adapterMap.get(obj.getClass()); if (adapterFactory == null) { throw new IllegalArgumentException("不支持的对象类型: " + obj.getClass().getName()); } return adapterFactory.apply(obj); } }
同理可实现PriceAdapterMapper,调用时只需通过映射器获取适配器,再传入服务方法:
// 调用示例 @Autowired private PhysicalDetailsService physicalDetailsService; @Autowired private WeightAdapterMapper weightAdapterMapper; public double getObjectWeight(Object obj) { WeightCalculable adapter = weightAdapterMapper.getAdapter(obj); return physicalDetailsService.calculateWeight(adapter); }
后续新增类型时,仅需在映射器中添加新的映射关系,完全避免if/else判断。
如果要遵循开闭原则,可通过Spring自动收集适配器工厂:
@Component public class WeightAdapterMapper { private final Map<Class<?>, WeightAdapterFactory> factoryMap; // 构造函数注入所有WeightAdapterFactory类型的Bean public WeightAdapterMapper(List<WeightAdapterFactory> factories) { factoryMap = factories.stream() .collect(Collectors.toMap(WeightAdapterFactory::getSupportedType, Function.identity())); } public WeightCalculable getAdapter(Object obj) { WeightAdapterFactory factory = factoryMap.get(obj.getClass()); if (factory == null) { throw new IllegalArgumentException("不支持的对象类型: " + obj.getClass().getName()); } return factory.createAdapter(obj); } // 适配器工厂接口 public interface WeightAdapterFactory { Class<?> getSupportedType(); WeightCalculable createAdapter(Object obj); } }
每个适配器对应一个工厂Bean:
@Component public class CarWeightAdapterFactory implements WeightAdapterMapper.WeightAdapterFactory { @Override public Class<?> getSupportedType() { return Car.class; } @Override public WeightCalculable createAdapter(Object obj) { return new CarWeightAdapter((Car) obj); } }
这种方式新增类型时仅需添加适配器和对应的工厂Bean,无需修改原有映射器代码。
四、Spring Bean配置
- 服务类
PhysicalDetailsService、PriceService用@Service注解,Spring自动扫描注册 - 映射器类
WeightAdapterMapper、PriceAdapterMapper用@Component注解,自动注册 - 适配器工厂类(如
CarWeightAdapterFactory)用@Component注解,自动被映射器收集
适配器类无需注册为Bean,因为它们是针对具体对象的包装,需要按需创建。
内容的提问来源于stack exchange,提问作者sjakovac
相关产品推荐
相关产品推荐

