跨项目场景下如何重构MyClassA实现Data的懒加载实例化?
可行实现方案与适用设计模式
一、核心思路
由于项目A不能直接依赖项目B,必须通过解耦方式让项目A获取到项目B中SpecialData的实例,同时满足移除setter、懒加载的需求。以下是几种落地可行的方案:
二、具体实现方式
1. 服务提供者模式(SPI)
这是Java原生的跨模块解耦方案,适配单向依赖下的接口实现加载场景:
- 步骤1:确保项目A中的
Data是公共接口,在项目B的src/main/resources/META-INF/services/目录下创建文件,文件名是Data接口的全限定名(如com.testa.Data),文件内容写入SpecialData的全限定名(com.testb.SpecialData) - 步骤2:修改项目A的
MyClassA:
package com.testa; import java.util.ServiceLoader; public class MyClassA { private Data data; public Data getData() { if (data != null) { return data; } if (projectB.isEnabled) { ServiceLoader<Data> loader = ServiceLoader.load(Data.class); for (Data dataImpl : loader) { // 通过类名匹配目标实现,避免项目A直接依赖SpecialData if ("com.testb.SpecialData".equals(dataImpl.getClass().getName())) { this.data = dataImpl; return data; } } } throw new IllegalStateException("Data instance not available"); } }
2. 工厂方法模式+依赖注入
通过工厂接口封装实例创建逻辑,实现彻底解耦:
- 步骤1:在项目A中定义
DataFactory接口:
package com.testa; public interface DataFactory { Data createData(); boolean isSupported(); // 对应projectB.isEnabled的判断逻辑 }
- 步骤2:修改
MyClassA,通过SPI或DI框架加载工厂实现:
package com.testa; import java.util.List; import java.util.ServiceLoader; public class MyClassA { private Data data; private static final List<DataFactory> DATA_FACTORIES = loadFactories(); private static List<DataFactory> loadFactories() { return ServiceLoader.load(DataFactory.class).stream() .map(ServiceLoader.Provider::get) .toList(); } public Data getData() { if (data != null) { return data; } for (DataFactory factory : DATA_FACTORIES) { if (factory.isSupported()) { this.data = factory.createData(); return data; } } throw new IllegalStateException("Data instance not available"); } }
- 步骤3:在项目B中实现
DataFactory:
package com.testb; import com.testa.Data; import com.testa.DataFactory; public class SpecialDataFactory implements DataFactory { @Override public Data createData() { return new SpecialData(); } @Override public boolean isSupported() { return projectB.isEnabled; } }
3. 反射实现(应急可用,不推荐)
通过反射动态加载SpecialData,但硬编码类名会降低可维护性,建议配合配置文件存储类名:
package com.testa; public class MyClassA { private Data data; // 可将类名抽离到配置文件中 private static final String SPECIAL_DATA_CLASS = "com.testb.SpecialData"; public Data getData() { if (data != null) { return data; } if (projectB.isEnabled) { try { Class<?> dataClass = Class.forName(SPECIAL_DATA_CLASS); this.data = (Data) dataClass.getDeclaredConstructor().newInstance(); return data; } catch (Exception e) { throw new RuntimeException("Failed to create SpecialData instance", e); } } throw new IllegalStateException("Data instance not available"); } }
三、适用的设计模式
- 服务提供者模式(SPI):Java原生的跨模块接口实现加载方案,完美适配单向依赖场景
- 工厂方法模式:通过工厂接口封装实例创建逻辑,彻底解耦
MyClassA与SpecialData的依赖 - 懒加载(Lazy Initialization):作为实现细节,确保
Data实例在第一次使用时才初始化
内容的提问来源于stack exchange,提问作者AndreasInfo
相关产品推荐
相关产品推荐

