High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
In the example below, the PaymentService
is a high-level module that provides payment processing functionality. It depends on the PaymentGateway
abstraction rather than specific implementations like PayPal or Stripe. This allows different payment gateways to be easily swapped without modifying the PaymentService.
// High-level module (depends on abstraction)
class PaymentService {
private let paymentGateway: PaymentGateway
init(paymentGateway: PaymentGateway) {
self.paymentGateway = paymentGateway
}
func processPayment(amount: Double) {
// Process payment using the payment gateway
paymentGateway.processPayment(amount: amount)
}
}
// Abstraction (does not depend on details)
protocol PaymentGateway {
func processPayment(amount: Double)
}
// Concrete implementation (depends on abstraction)
class PayPalGateway: PaymentGateway {
func processPayment(amount: Double) {
// Process payment using PayPal API
// Implementation details specific to PayPal
}
}
// Concrete implementation (depends on abstraction)
class StripeGateway: PaymentGateway {
func processPayment(amount: Double) {
// Process payment using Stripe API
// Implementation details specific to Stripe
}
}
The PaymentGateway
protocol defines the required behavior for a payment gateway, and the concrete implementations (PayPalGateway
and StripeGateway
) depend on the abstraction. This promotes flexibility and allows for future additions or changes to payment gateway implementations.
By applying the Dependency Inversion Principle, you can design your code in a way that reduces dependencies, increases flexibility, and promotes better code organization and maintainability.
Top comments (0)