面向对象编程(OOP)中适配器模式(Adapter pattern)与依赖注入(dependency injection)的区别及框架实现疑问
Great question—this is such a common point of confusion because both patterns involve connecting components, but they’re solving entirely different problems at their core. Let’s break this down clearly:
1. Core Goals Are Night and Day
Adapter Pattern
The adapter pattern exists solely to fix interface incompatibility. Think of it as a "translator" between two components that speak different "languages" (i.e., have mismatched method names, signatures, or return types). It lets you reuse existing code or third-party libraries without rewriting them to fit your app’s expected interfaces.
For example, say your app expects a PaymentGatewayInterface with a processPayment() method, but you have a legacy LegacyBillingService that only has a chargeCustomer() method:
// Your app's expected interface interface PaymentGatewayInterface { public function processPayment(float $amount): bool; } // Legacy component with incompatible interface class LegacyBillingService { public function chargeCustomer(float $amount): string { return $amount > 0 ? "CHARGED" : "FAILED"; } } // Adapter bridges the gap class BillingAdapter implements PaymentGatewayInterface { private $legacyService; public function __construct(LegacyBillingService $legacyService) { $this->legacyService = $legacyService; } public function processPayment(float $amount): bool { return $this->legacyService->chargeCustomer($amount) === "CHARGED"; } }
Dependency Injection (DI)
DI is all about reducing tight coupling between components. Instead of a component creating its own dependencies (e.g., new LegacyBillingService() inside OrderProcessor), you pass those dependencies in from the outside—usually via the constructor. This makes your code easier to test (you can inject mock dependencies) and easier to maintain (you can swap out implementations without changing the component itself).
Using the same example, here’s how DI works:
class OrderProcessor { private $paymentGateway; // Dependency is injected, not created internally public function __construct(PaymentGatewayInterface $paymentGateway) { $this->paymentGateway = $paymentGateway; } public function completeOrder(float $total): bool { return $this->paymentGateway->processPayment($total); } } // Outside the component, we set up the dependency $legacyService = new LegacyBillingService(); $adapter = new BillingAdapter($legacyService); $orderProcessor = new OrderProcessor($adapter);
2. Roles & Relationships Are Distinct
- Adapter Pattern: Has three fixed roles: the target interface (what your system expects), the adaptee (the incompatible component), and the adapter (the translator between them). The adapter wraps the adaptee to make it fit the target.
- Dependency Injection: It’s not a "pattern" with rigid roles—it’s a design principle (a specific implementation of Inversion of Control, IoC). Its only job is to decouple components from their concrete dependencies, letting them depend on abstractions instead.
3. Use Cases Don’t Overlap (Mostly)
- Use the adapter pattern when you need to:
- Integrate legacy code with a new system
- Use a third-party library that doesn’t match your app’s interfaces
- Bridge two incompatible systems
- Use DI when you want to:
- Make your code testable (inject mocks/stubs for unit tests)
- Reduce tight coupling between components
- Follow the Dependency Inversion Principle (depend on abstractions, not concretions)
Are DI Implementations in Symfony/Angular/Vue/React Adapter Patterns?
Short answer: No
While DI often uses interfaces (like the constructor type hints you mentioned), that’s just following the Dependency Inversion Principle—not implementing the adapter pattern. DI is about how you provide dependencies to a component; the adapter pattern is about making incompatible components work together.
That said, you can use them together: For example, in Symfony, you might register the BillingAdapter as the implementation for PaymentGatewayInterface, then inject PaymentGatewayInterface into OrderProcessor. Here, DI handles injecting the dependency, and the adapter handles interface compatibility—they’re complementary, but not the same thing.
内容的提问来源于stack exchange,提问作者mike

