Java接口模块化困境:API依赖实现类如何破局?
你当前的核心问题是Builder接口的静态工厂方法直接依赖实现类,导致本应作为依赖顶层的API模块反而依赖了实现模块,打破了“实现依赖API”的分层原则。下面是几种可行的解决方案,包括你问到的依赖注入方案:
方案1:将静态工厂方法迁移到实现模块
把原本放在Builder接口里的create方法移到实现模块的独立工厂类中,让API模块只保留纯接口定义,彻底切断对实现类的依赖。
API模块(Api/Builder.java):
public interface Builder { // 仅定义Builder的业务方法,比如: Object build(); }
实现模块(Impl/BuilderFactory.java):
public class BuilderFactory { private static final int N = 5; // 示例版本阈值 public static Builder create(int version) { if (version < N) { return new SubBuilder1(); } else { return new SubBuilder2(); } } static class SubBuilder1 implements Builder { @Override public Object build() { // 具体实现逻辑 return new Object(); } } static class SubBuilder2 implements Builder { @Override public Object build() { // 具体实现逻辑 return new Object(); } } }
上层代码通过BuilderFactory.create(version)获取Builder实例,此时依赖关系是实现模块 → API模块,完全符合分层要求。
方案2:使用Java SPI机制实现动态加载
如果需要支持后续扩展更多Builder实现,Java自带的SPI(服务提供者接口)是更灵活的选择,它允许实现模块主动注册实现类,API模块通过动态加载获取实例,无需编译期依赖。
步骤1:API模块定义接口并添加版本判断方法
public interface Builder { Object build(); // 新增方法,判断当前实现是否支持指定版本 boolean supports(int version); }
步骤2:实现模块注册服务提供者
在实现模块的META-INF/services目录下创建一个以API接口全类名为文件名的文件(比如com.yourpackage.api.Builder),文件内容写入实现类的全类名:
com.yourpackage.impl.SubBuilder1 com.yourpackage.impl.SubBuilder2
步骤3:实现类实现supports方法
public class SubBuilder1 implements Builder { @Override public Object build() { // 实现逻辑 return new Object(); } @Override public boolean supports(int version) { return version < 5; // 匹配版本阈值 } } public class SubBuilder2 implements Builder { @Override public Object build() { // 实现逻辑 return new Object(); } @Override public boolean supports(int version) { return version >= 5; } }
步骤4:API模块或上层提供加载器
public class BuilderLoader { public static Builder create(int version) { ServiceLoader<Builder> loader = ServiceLoader.load(Builder.class); for (Builder builder : loader) { if (builder.supports(version)) { return builder; } } throw new IllegalArgumentException("No Builder supports version: " + version); } }
这种方式下,API模块完全不依赖实现类,实现模块只需依赖API接口,同时支持动态扩展新的Builder实现。
方案3:依赖注入(DI)完全解耦
依赖注入非常适合这种场景,通过容器管理实现类的实例化,API模块无需关心具体实现的创建逻辑。以Spring为例:
API模块:保留纯Builder接口不变。
实现模块:将实现类注册为Spring Bean,并添加版本判断逻辑:
@Component public class SubBuilder1 implements Builder { @Override public Object build() { return new Object(); } public boolean supports(int version) { return version < 5; } } @Component public class SubBuilder2 implements Builder { @Override public Object build() { return new Object(); } public boolean supports(int version) { return version >= 5; } }
实现模块提供获取实例的Bean:
@Component public class BuilderProvider { @Autowired private List<Builder> builderList; public Builder getBuilder(int version) { for (Builder builder : builderList) { if (((VersionSupport) builder).supports(version)) { return builder; } } throw new IllegalArgumentException("Unsupported version"); } } // 定义通用的版本支持接口让Builder实现 public interface VersionSupport { boolean supports(int version); }
上层代码通过注入BuilderProvider来获取对应版本的Builder实例,依赖关系完全符合“实现依赖API”的原则,同时容器负责实例化和管理实现类,进一步降低耦合。
内容的提问来源于stack exchange,提问作者LGDGODV

