多库功能一致接口不同时,客户端解耦使用的设计方案问询
问题场景
- 存在两个功能本质相同但实现方式不同的库;
- 两者的参数类型、返回类型乃至方法名称均不相同;
- 需要在代码运行初期就确定全程使用Library A还是Library B。
当前已基于泛型抽象类定义了通用接口模板:
public abstract class LibraryInterface<A,B,C> { public abstract List<B> methodA(A a,B b,C c); public abstract List<C> methodB(B b); public abstract List<A> methodC(); }
对应两个实现类分别适配Library A和Library B:
public class LibraryA extends LibraryInterface<ObjectA,ObjectB,ObjectC> { @Override public List<ObjectB> methodA(ObjectA a,ObjectB b,ObjectC c) { // 调用Library A的具体逻辑 } @Override public List<ObjectC> methodB(ObjectB b) { // 调用Library A的具体逻辑 } @Override public List<ObjectA> methodC() { // 调用Library A的具体逻辑 } }
public class LibraryB extends LibraryInterface<ObjectD,ObjectE,ObjectF> { @Override public List<ObjectE> methodA(ObjectD d,ObjectE e,ObjectF f) { // 调用Library B的具体逻辑 } @Override public List<ObjectF> methodB(ObjectE e) { // 调用Library B的具体逻辑 } @Override public List<ObjectD> methodC() { // 调用Library B的具体逻辑 } }
核心需求:
- 客户端代码在初始化阶段选定具体实现,全程使用;
- 客户端代码不能与库的具体实现耦合;
- 插件系统不适用,因为客户端需要深度依赖选中的那个库的实现。
解决方案
1. 用泛型工厂封装实例化逻辑
把LibraryA/LibraryB的创建逻辑集中到工厂类里,客户端只通过工厂获取LibraryInterface实例,不直接引用具体实现类:
public class LibraryFactory { @SuppressWarnings("unchecked") public static <A,B,C> LibraryInterface<A,B,C> createLibrary(String libraryType) { switch(libraryType) { case "A": return (LibraryInterface<A,B,C>) new LibraryA(); case "B": return (LibraryInterface<A,B,C>) new LibraryB(); default: throw new IllegalArgumentException("不支持的库类型"); } } }
2. 定义客户端统一业务模型,隔离库自有类型
为客户端定义一套通用的业务DTO,让客户端只处理自己的业务对象,具体的类型转换逻辑放到LibraryA/LibraryB中完成——这是解决"深度依赖"同时解耦的核心:
首先定义客户端业务对象:
// 客户端通用业务对象,与具体库无关 public class BizObjX {} public class BizObjY {} public class BizObjZ {}
修改实现类,增加业务对象与库自有对象的转换:
public class LibraryA extends LibraryInterface<BizObjX,BizObjY,BizObjZ> { @Override public List<BizObjY> methodA(BizObjX x,BizObjY y,BizObjZ z) { // 1. 把客户端业务对象转成Library A的自有类型 ObjectA a = convertToObjectA(x); ObjectB b = convertToObjectB(y); ObjectC c = convertToObjectC(z); // 2. 调用Library A的原生方法 List<ObjectB> nativeResult = libraryANativeMethod(a,b,c); // 3. 把库返回的结果转成客户端业务对象 return nativeResult.stream().map(this::convertToBizObjY).collect(Collectors.toList()); } // 省略其他方法的转换逻辑 private ObjectA convertToObjectA(BizObjX x) { /* 转换实现 */ } private BizObjY convertToBizObjY(ObjectB b) { /* 转换实现 */ } }
3. 初始化阶段绑定实现,客户端全程依赖抽象
在应用启动入口(比如main方法、Spring配置类)读取配置确定要使用的库,通过工厂创建实例后注入到客户端服务中,后续客户端全程只依赖LibraryInterface和业务DTO:
public class ClientService { private final LibraryInterface<BizObjX,BizObjY,BizObjZ> library; // 构造注入实例,初始化时确定具体实现 public ClientService(LibraryInterface<BizObjX,BizObjY,BizObjZ> library) { this.library = library; } public void handleBusiness() { BizObjX x = new BizObjX(); BizObjY y = new BizObjY(); BizObjZ z = new BizObjZ(); // 直接用业务对象调用方法,完全不感知LibraryA/B的存在 List<BizObjY> result = library.methodA(x,y,z); // 后续业务逻辑处理 } }
启动初始化示例:
public class AppBootstrap { public static void main(String[] args) { // 从配置文件/环境变量读取要使用的库类型 String targetLibrary = System.getenv("TARGET_LIBRARY"); // 通过工厂创建实例 LibraryInterface<BizObjX,BizObjY,BizObjZ> library = LibraryFactory.createLibrary(targetLibrary); // 初始化客户端服务,全程使用该实例 ClientService client = new ClientService(library); client.handleBusiness(); } }
方案优势
- 完全解耦客户端与具体库:客户端代码只认识
LibraryInterface和自己的业务对象,新增或替换库实现时无需修改客户端代码; - 初始化阶段锁定实现:启动时就确定要使用的库,全程使用同一个实例,符合需求;
- 满足深度依赖需求:通过实现类内部的类型转换,把客户端的业务逻辑与具体库的实现细节隔离,既保证业务逻辑的深度实现,又不耦合具体库。
内容的提问来源于stack exchange,提问作者Vishal
相关产品推荐
相关产品推荐

