The SOLID principles are a set of five object-oriented design principles that help in creating more maintainable, understandable, and scalable software systems. These principles were introduced by Robert C. Martin (Uncle Bob), and they serve as guidelines for writing clean, efficient, and robust code. The SOLID acronym stands for:
Let’s delve deeper into each of these principles with Java code examples to illustrate how they help in building better software systems.
1. Single Responsibility Principle (SRP)
A class should have only one responsibility. It should do one thing and do it well.
Example:
// Violation of SRPclass User { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } public void save() { // Code to save user to database } public void sendEmail() { // Code to send email }}
Refactored:
class User { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } public void save() { // Save user to database }}class EmailService { public void sendEmail(User user) { // Code to send email to the user }}
In the refactored code, User
handles user-related data, while EmailService
is responsible for sending emails. Each class has a single responsibility.
2. Open/Closed Principle (OCP)
Software entities should be open for extension but closed for modification.
Example:
// Violation of OCPclass AreaCalculator { public double calculateArea(Object shape) { if (shape instanceof Circle) { Circle circle = (Circle) shape; return Math.PI * Math.pow(circle.getRadius(), 2); } else if (shape instanceof Rectangle) { Rectangle rectangle = (Rectangle) shape; return rectangle.getWidth() * rectangle.getHeight(); } return 0; }}class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; }}class Rectangle { private double width; private double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } public double getWidth() { return width; } public double getHeight() { return height; }}
Refactored:
interface Shape { double calculateArea();}class Circle implements Shape { private double radius; public Circle(double radius) { this.radius = radius; } @Override public double calculateArea() { return Math.PI * Math.pow(radius, 2); }}class Rectangle implements Shape { private double width; private double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } @Override public double calculateArea() { return width * height; }}class AreaCalculator { public double calculateArea(Shape shape) { return shape.calculateArea(); }}
New shapes can now be added without modifying the AreaCalculator
class, adhering to the OCP.
3. Liskov Substitution Principle (LSP)
Derived classes should be substitutable for their base classes without altering the program's behavior.
Example:
// Violation of LSPclass Bird { public void fly() { System.out.println("Bird is flying"); }}class Penguin extends Bird { @Override public void fly() { throw new UnsupportedOperationException("Penguins can't fly"); }}
Refactored:
interface Flyable { void fly();}class Bird { // General bird behavior}class Sparrow extends Bird implements Flyable { @Override public void fly() { System.out.println("Sparrow is flying"); }}class Penguin extends Bird { // Penguins don't implement Flyable}
Now, Penguin
does not override fly()
, and the code adheres to LSP.
4. Interface Segregation Principle (ISP)
Interfaces should be specific to client needs, avoiding monolithic interfaces.
Example:
// Violation of ISPinterface Worker { void work(); void eat();}class Manager implements Worker { public void work() { System.out.println("Managing people"); } public void eat() { System.out.println("Eating lunch"); }}class Robot implements Worker { public void work() { System.out.println("Performing tasks"); } public void eat() { throw new UnsupportedOperationException("Robots don't eat"); }}
Refactored:
interface Workable { void work();}interface Eatable { void eat();}class Manager implements Workable, Eatable { public void work() { System.out.println("Managing people"); } public void eat() { System.out.println("Eating lunch"); }}class Robot implements Workable { public void work() { System.out.println("Performing tasks"); }}
Smaller, more specific interfaces prevent unnecessary implementations, following ISP.
5. Dependency Inversion Principle (DIP)
High-level modules should depend on abstractions, not low-level modules.
Example:
// Violation of DIPclass Database { public void saveUser(User user) { // Save user to database }}class UserService { private Database database = new Database(); public void registerUser(User user) { database.saveUser(user); }}
Refactored:
interface Database { void saveUser(User user);}class MySQLDatabase implements Database { public void saveUser(User user) { // Save user to MySQL database }}class UserService { private Database database; public UserService(Database database) { this.database = database; } public void registerUser(User user) { database.saveUser(user); }}
The UserService
depends on the Database
interface, allowing the underlying implementation (e.g., MySQLDatabase
, PostgreSQLDatabase
) to vary without impacting the service. This adheres to DIP.
By following these principles, developers can build better-structured, robust, and flexible software systems.
Physics, Chemistry, Biology and Geography.
Computer Programming, languages & their frameworks.
Economics, Accounts and Management.
Reviewing old and new books.
Ancient, Medieval, Modern, World History.
Indian Constitution, Politics, Policies, etc.
Everything related to International Affairs.
For all humanities topics, except History & Polity.
Anything related to entertainment industry.
Mainly Cricket but other sports too.
CS, IT, Services & Corporate Sector.
Comments
No comments yet. Be the first to comment!
Leave a Comment