SOLID原则:单一职责原则与开闭原则是否互斥?
很多开发者会觉得这两个原则看起来有点矛盾——SRP要求类只专注一件事,OCP要求不能修改原有代码却要能扩展功能。但其实它们是互补的:SRP帮你把系统拆成小而清晰的单一职责模块,OCP则帮你在这些模块之间搭建灵活的扩展机制,避免硬编码带来的耦合问题。
核心思路:把「变更理由」和「扩展点」解耦
SRP说的「一个变更理由」,不是指类永远不能有任何变动,而是它只对一种类型的变化负责。比如一个订单处理类,它的职责是执行订单的业务逻辑,那么「订单业务规则调整」是它唯一的变更理由;如果要新增支付方式,这绝对不该成为修改订单类的理由——这时候就该用OCP,把支付方式设计成可扩展的独立组件,让订单类依赖支付抽象而非具体实现。
用工厂模式的例子解决冲突
你提到工厂模式可能违反OCP,比如传统的简单工厂:
public class ProductFactory { public Product createProduct(String type) { if (type.equals("A")) { return new ProductA(); } else if (type.equals("B")) { return new ProductB(); } // 新增ProductC时,必须修改这里的代码! throw new IllegalArgumentException("Unknown product type"); } }
这个工厂类确实违反了OCP(新增产品必须改代码),同时也违反了SRP——它的职责是「创建所有产品」,变更理由会有N种(新增A、修改B的创建逻辑、删除C…)。那怎么改造?
1. 先按SRP拆分:每个产品对应一个专属工厂
把原来的单一工厂拆成多个具体工厂类,每个只负责创建一种产品:
// 抽象工厂接口,定义创建产品的标准 public interface ProductFactory { Product createProduct(); } // 只负责创建ProductA的工厂,职责单一 public class ProductAFactory implements ProductFactory { @Override public Product createProduct() { return new ProductA(); } } // 只负责创建ProductB的工厂,职责单一 public class ProductBFactory implements ProductFactory { @Override public Product createProduct() { return new ProductB(); } }
现在每个具体工厂都符合SRP:它们只有一个变更理由——对应产品的创建逻辑变化。比如修改ProductA的构造参数,只需要改ProductAFactory,其他工厂完全不受影响。
2. 用注册机制实现OCP
现在问题来了:怎么根据类型快速找到对应的工厂?这时候可以加一个工厂注册器,它的职责只有一个:管理所有具体工厂的注册与查找,符合SRP:
public class FactoryRegistry { private static final Map<String, ProductFactory> factories = new HashMap<>(); // 初始化已知工厂(也可以用配置文件、注解扫描等方式) static { factories.put("A", new ProductAFactory()); factories.put("B", new ProductBFactory()); } // 提供外部注册入口,允许新增工厂 public static void registerFactory(String type, ProductFactory factory) { factories.put(type, factory); } // 根据类型获取对应工厂 public static ProductFactory getFactory(String type) { ProductFactory factory = factories.get(type); if (factory == null) { throw new IllegalArgumentException("Unknown product type"); } return factory; } }
现在新增ProductC的时候,只需要做三件事:
- 编写ProductC类
- 编写ProductCFactory实现ProductFactory接口
- 调用
FactoryRegistry.registerFactory("C", new ProductCFactory())
完全不需要修改原有任何代码——完美符合OCP!
通用实践方法总结
- 先做SRP拆分:把大职责拆成小的、单一的模块,每个模块只对一种变化负责。
- 依赖抽象而非具体实现:这是OCP的核心,让模块之间通过接口/抽象类交互,避免直接绑定具体类。
- 用扩展点代替修改:把可能变化的部分做成可插拔组件,比如注册器、策略模式、装饰器模式等,新增功能只需要添加新组件,不用动原有代码。
记住:SRP帮你避免「牵一发而动全身」,OCP帮你在不破坏原有代码的前提下扩展功能——两者结合,就能打造出既清晰又灵活的系统。
内容的提问来源于stack exchange,提问作者Matthew Layton

