关于OCP:客户端还是抽象类层级遵循开闭原则?
Hey there, great question—this confusion is totally normal when you’re first wrapping your head around OCP! Those two authors aren’t contradicting each other; they’re actually describing two interconnected parts of the same core principle. Let me break it down for you:
First: "Base classes are closed, extend via derived classes"
This is the extension-focused side of OCP, rooted in its core rule: "open for extension, closed for modification". Here’s what it means:
- Your abstract base class (like
abstract class A {}) represents a fixed, stable contract—it’s "closed" because you shouldn’t modify its code to add new features or behaviors. Changing the base class risks breaking every piece of code that relies on it. - Instead, you add new functionality by creating derived classes (like
class B : A {}orclass C : A {}). These derivatives implement or override the base class’s contract to introduce new logic, without touching the original base code at all.
For example, if A defines a method ProcessData(), you don’t rewrite A to add a new processing logic—you make B and C override ProcessData() with their own specialized behavior.
Second: "Code dependent on these classes doesn’t need modification because it relies on abstraction"
This is the dependency-focused side of OCP, and it’s why the first part matters so much. If your application code depends on the abstract base class (A) rather than concrete implementations (B or C), you never have to change that code when you add new derived classes.
Let’s use a concrete code example to make this tangible:
// Closed abstract base class (defines the contract) abstract class PaymentProcessor { public abstract void ProcessPayment(decimal amount); } // Extended via derived classes class CreditCardProcessor : PaymentProcessor { public override void ProcessPayment(decimal amount) { // Logic for credit card payments Console.WriteLine($"Processing credit card payment of ${amount}"); } } class PayPalProcessor : PaymentProcessor { public override void ProcessPayment(decimal amount) { // Logic for PayPal payments Console.WriteLine($"Processing PayPal payment of ${amount}"); } } // Code that depends on abstraction (no need to modify this!) class OrderService { public void CompleteOrder(PaymentProcessor processor, decimal total) { // Do order validation logic... processor.ProcessPayment(total); // Finalize order... } }
If later you want to add a ApplePayProcessor, you just create a new ApplePayProcessor : PaymentProcessor class with its own ProcessPayment() method. The OrderService code stays exactly the same—it only cares that the processor follows the abstract PaymentProcessor contract, not what specific type it is.
Putting it all together
Those two statements are two halves of a single OCP workflow:
- You extend your system’s capabilities by adding new derived classes (never modifying the abstract base).
- Because your application code relies on the abstract base (not concrete implementations), it doesn’t need to change when you add those new derivatives.
One can’t work without the other—if you didn’t have the abstract base to depend on, adding a new derived class would force you to rewrite every piece of code that uses it. And if you modified the base class instead of extending via derivatives, you’d risk breaking all existing code that relies on its original contract.
内容的提问来源于stack exchange,提问作者Ezoela Vacca

