The SOLID principles are a set of five object-oriented design principles that help developers write better code. The principles are:
Single Responsibility Principle (SRP): A class should have one and only one reason to change.
Open/Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Liskov Substitution Principle (LSP): Objects in a program should be replaceable with instances of their subtypes without altering the correctness of the program.
Interface Segregation Principle (ISP): Many client-specific interfaces are better than one general-purpose interface.
Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules. Both should depend on abstractions.
Why use the SOLID principles?
The SOLID principles help developers write code that is more:
Maintainable: Code that follows the SOLID principles is easier to understand and change because each class has a single responsibility and is loosely coupled to other classes.
Reusable: Code that follows the SOLID principles is more reusable because classes are designed to be open for extension and closed for modification.
Testable: Code that follows the SOLID principles is easier to test because classes are loosely coupled and dependencies are inverted.
How to use the SOLID principles
There are many ways to use the SOLID principles in Java. Here are a few examples:
SRP:
Design classes to have a single responsibility.
Avoid putting too much code in a single class.
Break down large classes into smaller, more focused classes.
OCP:
Use interfaces to define the behavior of classes.
Design classes to implement interfaces rather than concrete classes.
Use abstract classes to define common behavior for a set of related classes.
LSP:
Design classes to be substitutable with their subtypes.
Override methods in subclasses to provide additional functionality or to modify the behavior of the inherited method.
Avoid breaking the contract of the overridden method.
ISP:
Define multiple small, specific interfaces rather than a single large, general-purpose interface.
Clients should only be forced to depend on the interfaces that they use.
Avoid defining interfaces that are too large or too complex.
DIP:
Use interfaces to define the dependencies of classes.
Inject dependencies into classes rather than having them create their own dependencies.
Use dependency injection frameworks to manage the injection of dependencies.
Code samples
Here are some code samples that demonstrate the use of the SOLID principles in Java:
Java
// SRP
public class EmailService {
public void sendEmail(String toAddress, String subject, String body) {
// ...
}
}
public
class
UserService
{
private EmailService emailService;
public
UserService(EmailService emailService)
{
this.emailService = emailService;
}
public
void registerUser(String emailAddress) {
// ...
// Send a confirmation email to the user.
this.emailService.sendEmail(emailAddress, "Welcome to our application!", "Thank you for registering for an account.");
}
}
In this example, the EmailService class has the single responsibility of sending emails. The UserService class depends on the EmailService to send confirmation emails to new users. However, the UserService class does not need to know how the EmailService class works. Instead, the EmailService class is injected into the UserService class when it is created.
Java
// OCP
public interface EmailService {
void sendEmail(String toAddress, String subject, String body);
}
public class ProductionEmailService implements EmailService {
// ...
}
public class TestEmailService implements EmailService {
// ...
}
In this example, the EmailService interface defines the behavior of an email service. The ProductionEmailService and TestEmailService classes implement the EmailService interface. The UserService class can depend on the EmailService interface rather than a concrete implementation of the interface. This allows us to swap out different implementations of the EmailService interface without having to modify the UserService class. For example, we could use the TestEmailService class when testing the UserService class and use the ProductionEmailService class in production.
Java
// LSP
public class Animal {
public void makeSound() {
// ...
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
Sources
1. https://github.com/Igor-Mont/Typecscript
4. https://github.com/veryobinna/assessment
5. https://github.com/JetyCZ/inpia-dependency-injection-nospring