学习SOLID原则:依赖倒置原则(DI)为何是开闭原则(OCP)的延伸?
Great question! Let’s break this down clearly—since mixing up DIP and DI is such a common pitfall when starting with SOLID, I’m glad you called out that distinction upfront: Dependency Inversion Principle (DIP) is a design principle, while Dependency Injection (DI) is just one implementation technique to follow that principle. They’re related but not interchangeable.
First, let’s recap the core of OCP: it demands that classes/modules should be open for extension, but closed for modification. In plain terms, you should be able to add new features or behavior without hacking into existing, working code. The problem is, how do you structure your code to actually make that possible? That’s where DIP comes in—it’s the principle that enables OCP to work in practice.
DIP’s Core Rules (And How They Enable OCP)
DIP has two non-negotiable tenets:
- High-level modules (the ones holding your core business logic, like an
OrderProcessor) shouldn’t depend on low-level modules (detail-focused code like database handlers, e.g.,SqlOrderRepository). Both should depend on abstractions (like anIOrderRepositoryinterface). - Abstractions shouldn’t rely on specifics—instead, specifics rely on abstractions. Your interface doesn’t care if you’re using SQL, MongoDB, or a flat file; concrete implementations adapt to the interface, not the other way around.
Example: Violating DIP (And OCP)
Let’s start with a bad example to see why this matters. Suppose you have an order processing system written like this:
// Low-level module (detail-focused) public class SqlOrderRepository { public void SaveOrder(Order order) { // SQL-specific save logic here } } // High-level module (core business logic) public class OrderProcessor { private readonly SqlOrderRepository _repo; public OrderProcessor() { _repo = new SqlOrderRepository(); // Direct dependency on concrete class } public void ProcessOrder(Order order) { // Business logic: validate order, apply discounts, etc. _repo.SaveOrder(order); } }
Here, OrderProcessor (high-level) directly depends on SqlOrderRepository (low-level). If you later need to add support for MongoDB, you have to modify OrderProcessor—either add a new constructor, or rewrite the existing one to accept a MongoOrderRepository. That’s a direct violation of OCP: you’re modifying existing code to add new functionality.
Example: Following DIP (And Achieving OCP)
Now let’s refactor this to follow DIP, which automatically makes it OCP-compliant:
// Abstraction (interface) public interface IOrderRepository { void SaveOrder(Order order); } // Low-level module 1: SQL implementation public class SqlOrderRepository : IOrderRepository { public void SaveOrder(Order order) { // SQL-specific logic } } // Low-level module 2: MongoDB implementation (added later, no changes to existing code) public class MongoOrderRepository : IOrderRepository { public void SaveOrder(Order order) { // MongoDB-specific logic } } // High-level module: depends on abstraction, not concrete classes public class OrderProcessor { private readonly IOrderRepository _repo; // Dependency Injection (implementation technique for DIP) public OrderProcessor(IOrderRepository repo) { _repo = repo; } public void ProcessOrder(Order order) { // Business logic remains 100% unchanged _repo.SaveOrder(order); } }
Now, if you want to add a new storage option (like Redis), you just create a new RedisOrderRepository that implements IOrderRepository—you don’t touch the OrderProcessor at all. That’s OCP in action: extending functionality without modifying existing code.
Why DIP Is an Extension of OCP
OCP sets the goal: extend without modifying. DIP provides the structural blueprint to reach that goal. By decoupling high-level logic from low-level details via abstractions, you create a buffer between the two. Changes to details (like switching databases) don’t require changes to core business logic, because both layers depend on the same abstract contract.
To put it simply: OCP tells you what to aim for, DIP tells you how to structure your code to get there. DI is just one tool to inject those concrete implementations into the high-level modules that depend on the abstractions.
内容的提问来源于stack exchange,提问作者Ezoela Vacca

