继承还是组合?基于JTOpen的IBMi项目重构困惑求解
嘿,我太懂这种踩坑的感觉了!跟着QSYS的层级用继承搭了架构,一开始觉得顺理成章,结果后期想优化的时候,改个基类所有子类都炸,连“菱形问题”都冒出来了——这绝对是组合优于继承的典型场景!
咱们先理清楚问题根源:QSYS里的库、物理文件、成员这些是结构上的层级关系,但用继承把它们绑在一起,等于把“连接管理”“路径解析”“对象操作”“元数据获取”这些不同的职责全塞在了基类里,最后基类变成了一个大杂烩,牵一发而动全身。
重构思路:用组合拆分职责,替代继承层级
核心就是把原来基类里的不同职责拆成独立的「组件」,然后让每个QSYS对象(库、文件、成员)去组合这些组件,而不是继承它们的行为。咱们结合JTOpen的实际场景来落地,绝对不用水果动物的例子:
第一步:拆分核心职责为独立组件
先把原来基类里的功能按单一职责拆成几个组件:
- JT400连接管理器:专门负责和IBM i的会话管理(创建连接、断开、获取AS400实例),所有需要通信的操作都委托给它。
- QSYS路径服务:专门处理QSYS格式的路径拼接、解析(比如把库名转成
/QSYS.LIB/MYLIB.LIB)。 - 对象操作组件:不同类型的QSYS对象(库、文件)有不同的创建/删除逻辑,拆成接口+实现类。
- 元数据组件:专门负责获取不同对象的元数据(比如库的描述、文件的字段信息),同样用接口+实现类。
第二步:用组合重构业务类
原来的Library、PhysicalFile不再继承基类,而是持有这些组件的实例,自己只专注于特有的业务逻辑。
先看组件的代码示例(Java,因为JTOpen是Java库):
// 1. 连接管理组件 public class JT400Connection { private final AS400 as400; public JT400Connection(String host, String user, String password) { this.as400 = new AS400(host, user, password); } public AS400 getAS400() { return as400; } public void disconnect() throws Exception { as400.disconnectAllServices(); } } // 2. 路径服务组件 public class QSYSPathService { public String buildLibraryPath(String libName) { return String.format("/QSYS.LIB/%s.LIB", libName); } public String buildPhysicalFilePath(String libName, String fileName) { return String.format("/QSYS.LIB/%s.LIB/%s.FILE", libName, fileName); } } // 3. 对象操作接口+实现 public interface QSYSObjectOperations { void create(JT400Connection connection, String path) throws Exception; void delete(JT400Connection connection, String path) throws Exception; } public class LibraryOperations implements QSYSObjectOperations { @Override public void create(JT400Connection connection, String path) throws Exception { // 调用JTOpen API创建库的逻辑 AS400 as400 = connection.getAS400(); // ... } @Override public void delete(JT400Connection connection, String path) throws Exception { // 删除库的JTOpen逻辑 } } public class PhysicalFileOperations implements QSYSObjectOperations { @Override public void create(JT400Connection connection, String path) throws Exception { // 创建物理文件的JTOpen逻辑 } @Override public void delete(JT400Connection connection, String path) throws Exception { // 删除物理文件的JTOpen逻辑 } } // 4. 元数据接口+实现 public interface QSYSObjectMetadataProvider { Map<String, Object> getMetadata(JT400Connection connection, String path) throws Exception; } public class LibraryMetadataProvider implements QSYSObjectMetadataProvider { @Override public Map<String, Object> getMetadata(JT400Connection connection, String path) throws Exception { // 获取库元数据的JTOpen逻辑(创建时间、描述等) return new HashMap<>(); } }
再看重构后的Library类:
public class Library { private final JT400Connection connection; private final QSYSPathService pathService; private final QSYSObjectOperations operations; private final QSYSObjectMetadataProvider metadataProvider; private final String libName; private final String path; // 通过构造器注入所有依赖的组件 public Library(JT400Connection connection, QSYSPathService pathService, QSYSObjectOperations operations, QSYSObjectMetadataProvider metadataProvider, String libName) { this.connection = connection; this.pathService = pathService; this.operations = operations; this.metadataProvider = metadataProvider; this.libName = libName; this.path = pathService.buildLibraryPath(libName); } // 委托组件完成通用操作 public void create() throws Exception { operations.create(connection, path); } public void delete() throws Exception { operations.delete(connection, path); } public Map<String, Object> getMetadata() throws Exception { return metadataProvider.getMetadata(connection, path); } // 库特有的业务逻辑 public List<String> listObjects() throws Exception { AS400 as400 = connection.getAS400(); // 调用JTOpen API列举库内对象的逻辑 return new ArrayList<>(); } }
第三步:重构的好处
- 彻底解耦:再也不用担心改基类影响所有子类了,每个组件只负责一件事,修改只会影响用到它的类。
- 避免层级陷阱:QSYS的结构是「包含关系」(库包含文件),不是「继承关系」(文件是库的子类),用组合更贴合实际语义。
- 灵活性拉满:如果需要给某类对象加新功能(比如权限管理),不用改基类,只需要加一个新组件,然后在需要的业务类里组合它就行;甚至可以给同一个对象换不同的操作实现(比如测试用Mock组件)。
- 符合单一职责:每个类和组件的职责都清晰,代码更易维护和测试。
重构的实操步骤
- 先拆职责:把原来基类的方法按功能分类,比如哪些是连接相关、哪些是路径相关、哪些是操作逻辑,每一类抽成组件。
- 逐步替换:不要一次性重构所有代码,先从一个子类(比如
Library)开始,替换成组合模式,测试没问题后再推进其他类。 - 提取接口:给每个组件加接口,这样后续可以灵活替换实现,也方便单元测试。
- 依赖注入(可选):如果项目规模大,用Spring之类的框架管理组件实例,减少业务类里的实例化代码。
说白了,你之前的问题就是把「结构上的层级」当成了「继承关系」,而组合模式刚好能把这种强耦合的继承换成灵活的协作关系——这正是组合优于继承的核心场景!
内容的提问来源于stack exchange,提问作者LppEdd
相关产品推荐
相关产品推荐

