SOLID - Dependency Inversion Principle
September 4, 2022 • ☕️ 4 min read • 🏷 computer, software
Translated by author into: English
The Dependency Inversion Principle (DIP) is one of the principles of object-oriented programming (OOP). DIP recommends that, in the software development process, when interdependent classes are formed and these classes are tightly related, these dependencies should be reversed and the dependencies made less stringent. This principle allows classes to be made less dependent and thus more flexible and reusable.
DIP also stands for the letter “D” of SOLID principles. In principle, a higher-level module should not be directly dependent on a lower-level module. Instead, the top-level module should reverse the dependency on the lower-level module and provide an interface between those modules. In this way, even if the implementation of the lower level modules changes, the higher level module does not need to be changed.
DIP is also important for code maintainability and testability. Reversing dependencies ensures that when one class needs to be changed, other classes are unaffected. This increases code reusability and reduces maintenance costs.
An example to implement the Dependency Inversion principle in PHP is as follows:
interface EmailSenderInterface {
public function sendEmail($to, $subject, $message);
}
class EmailSender implements EmailSenderInterface {
public function sendEmail($to, $subject, $message) {
// ... e-mail sending process ...
}
}
class Customer {
private $emailSender;
public function __construct(EmailSenderInterface $emailSender) {
$this->emailSender = $emailSender;
}
public function placeOrder() {
// ... customer order processing code ...
$this->emailSender->sendEmail($this->email, 'Order Confirmation', 'Your order has been successfully received.');
}
}
$emailSender = new EmailSender();
$customer = new Customer($emailSender);
$customer->placeOrder();
In the above example, the Customer class is made dependent on the EmailSenderInterface interface. Since the EmailSender class also implements the EmailSenderInterface interface, the EmailSender class becomes available to the Customer class.
This way we ensure that our client class is dependent on an interface rather than being directly dependent on a class. Thus, even if we want to use a different email sending service, it will be sufficient to simply create a new class and implement the EmailSenderInterface interface. This way we can use a new email sending service without changing the client class.
The PHP code below is an example of a class that violates the Dependency Inversion principle.
class Customer {
private $emailSender;
public function __construct() {
$this->emailSender = new EmailSender();
}
public function placeOrder() {
// ... customer order processing code ...
$this->emailSender->sendEmail($this->email, 'Order Confirmation', 'Your order has been successfully received.');
}
}
class EmailSender {
public function sendEmail($to, $subject, $message) {
// ... e-mail sending process ...
}
}
$customer = new Customer();
$customer->placeOrder();
In the above example, the Customer class is directly dependent on the EmailSender class. This reduces the flexibility of the Customer class because the customer class only has to send email using the EmailSender class.
Instead, the Customer class should be made dependent on the EmailSenderInterface interface and the EmailSender class should implement this interface. Thus, the client class becomes more flexible and more easily interchangeable as different email sending services can be used.
In GoLang, an example can be given as follows to implement the Dependency Inversion principle:
type EmailSenderInterface interface {
SendEmail(to string, subject string, message string)
}
type EmailSender struct {}
func (e EmailSender) SendEmail(to string, subject string, message string) {
// ... e-mail sending process ...
}
type Customer struct {
EmailSender EmailSenderInterface
Email string
}
func (c Customer) PlaceOrder() {
// ... customer order processing code ...
c.EmailSender.SendEmail(c.Email, "Order Confirmation", "Your order has been successfully received.");
}
func main() {
emailSender := EmailSender{}
customer := Customer{EmailSender: emailSender, Email: "example@example.com"}
customer.PlaceOrder()
}
In the above example, the Customer class is made dependent on the EmailSenderInterface interface. Since the EmailSender class also implements the EmailSenderInterface interface, the EmailSender class becomes available to the Customer class.
This way we ensure that our client class is dependent on an interface rather than being directly dependent on a class. Thus, even if we want to use a different email sending service, it will be sufficient to simply create a new class and implement the EmailSenderInterface interface. This way we can use a new email sending service without changing the client class.
Similar to the PHP example, the GoLang code below is an example that violates the Dependency Inversion principle.
type Customer struct {
EmailSender EmailSender
Email string
}
func (c Customer) PlaceOrder() {
// ... customer order processing code ...
c.EmailSender.SendEmail(c.Email, "Order Confirmation", "Your order has been successfully received.");
}
type EmailSender struct {}
func (e EmailSender) SendEmail(to string, subject string, message string) {
// ... e-mail sending process ...
}
func main() {
emailSender := EmailSender{}
customer := Customer{EmailSender: emailSender, Email: "example@example.com"}
customer.PlaceOrder()
}
In the above example, the Customer class is directly dependent on the EmailSender class. This reduces the flexibility of the Customer class because the customer class only has to send email using the EmailSender class.
Instead, the Customer class should be made dependent on the EmailSenderInterface interface and the EmailSender class should implement this interface. Thus, the client class becomes more flexible and more easily interchangeable as different email sending services can be used.
Resources
- https://en.wikipedia.org/wiki/Dependency_inversion_principle
- https://blog.logrocket.com/dependency-inversion-principle-typescript/#:~:text=The%20dependency%20inversion%20principle%20is,affecting%20the%20high%2Dlevel%20ones.
- https://dev.to/tamerlang/understanding-solid-principles-dependency-inversion-1b0f