Home > Java > javaTutorial > body text

SOLID Principles

Linda Hamilton
Release: 2024-10-21 06:12:02
Original
974 people have browsed it

SOLID Principles

SOLID is a collection of fundamental principles designed to enhance the manageability and scalability of code in Object-Oriented Programming (OOP). It consists of five key principles:

  1. S ingle Responsibility Principle — SRP
  2. O pen-Closed Principle — OCP
  3. L iskov’s Substitution Principle — LSP
  4. I nterface Segregation Principle — ISP
  5. D ependency Inversion Principle — DIP

These principles were introduced by Robert C. Martin (also known as Uncle Bob) in the early 2000s and have since become widely adopted in the software development community. By following SOLID principles, developers can create code that is easier to understand, modify, and extend, leading to more robust and maintainable software systems.

Single Responsibility Principle (SRP)

Single Responsibility Principle is the first and most fundamental principle in OOP and SOLID. As the name sounds, this principle means “One class should have only one specific responsibility to take care of”.

Suppose we have a class called Invoice , which contains 2 methods generateInvoice() and saveToFiles() .

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

This is not a good practice because the Invoice class has two responsibilities. A better approach would be to separate these functionalities into dedicated classes.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

Here, we can see we have 2 classes for the use case:

  • Generating the invoice
  • Save it to files

Benefits of following SRP

  • Improved Code Organization : By separating concerns into different classes, the codebase becomes more organized and easier to navigate.
  • Better Maintainability : When a class has a single responsibility, it is easier to understand its purpose and make changes without unintended side effects.
  • Increased Reusability : Classes with a single responsibility are more likely to be reusable in different parts of the application or even in other projects.
  • Easier Testing : Classes with a single responsibility are typically smaller and more focused, making them easier to test in isolation.

Open-Closed Principle (OCP)

Open-Closed principle is another core principle in SOLID. This principle was introduced by Bertrand Meyer in 1997. The idea behind this principle is “Software artifacts (classes, modules, and functions) should open for extensions, but closed for modifications.”

For example;

Let’s say, we have a class called Shape , we can use this class to calculate the area of the shape.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

In the code above, adding a new shape requires modifying the existing Shape class, which is not considered a good practice.

Below is a code example that demonstrates how to apply the Open-Closed Principle to this scenario.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

With the application of OCP, we can add many shapes as we want without modifying the current implementation.

NOTE: Using interfaces is not the only way to achieve OCP.

Benefits of following OCP

  • Reduced Risk of Bugs : By not modifying existing code, the risk of introducing new bugs or breaking existing functionality is minimized.
  • Improved Maintainability : Code that follows the OCP is easier to maintain and extend, as new features can be added without altering the existing codebase.
  • Enhanced Flexibility : The use of abstractions and polymorphism allows for more flexible and adaptable designs, making it easier to accommodate changing requirements.

Liskov’s Substitution Principle (LSP)

Liskov’s Substitution Principle is another important principle in OOP. It was introduced by Barbara Liskov in 1987 during a conference talk on data abstraction.

The principle states, “Objects of a superclass should be replaceable with objects of its subclasses without altering the correctness of the program”.

For example, if Circle and Rectangle are sub types of Shape, then we should be able to replace Shape object with a Circle or Rectangle object without any issues.

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}
Copy after login
Copy after login

As demonstrated in this example, adhering to the Liskov Substitution Principle means we should be able to substitute a superclass instance with a subclass instance seamlessly.

Benefits of following LSP

  • Improved Code Reusability : By ensuring that subtypes can be substituted for their base types, code that uses the base type can also work with any of its subtypes, promoting code reuse.
  • Enhanced Maintainability : Code that follows LSP is easier to maintain because it reduces the risk of introducing bugs when modifying or extending the codebase.
  • Better Testability : LSP makes it easier to write unit tests for classes and their subtypes, as the tests can be written against the base type and should work for all subtypes.

Interface Segregation Principle (ISP)

The Interface Segregation Principle is one of the five SOLID principles introduced by Robert C. Martin. It states: “Clients should not be forced to depend on interfaces they do not use”.

In other words, Using many task specific interfaces is better than using one general purpose interface.

Below example shows the usage of general purpose interface.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

Using a general-purpose interface like MultifunctionPrinter forces us to implement unnecessary methods, which is considered bad practice. Let’s explore how we can apply the Interface Segregation Principle to this scenario.

Interfaces

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

Implementations

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}
Copy after login
Copy after login

By applying the ISP , we split it into smaller, role-specific interfaces — like Printer, Scanner, and Fax. This allows each class (e.g. BasicPrinter, AdvancedPrinter, or FaxMachine) to implement only the relevant functionality, promoting modularity and reducing unnecessary dependencies.

Benefits of following ISP

  • Modular and Reusable Code : By breaking down large interfaces into smaller, more specific ones, the code becomes more modular and reusable. Classes or modules can implement only the interfaces they need, reducing unnecessary dependencies and making it easier to reuse code across different parts of the system.
  • Reduced Code Complexity : When classes or modules depend only on the interfaces they need, the code becomes less complex and easier to understand. This is because developers do not have to deal with unnecessary methods or dependencies. These are not relevant to their specific use case.
  • Improved Maintainability : With smaller and more focused interfaces, it becomes easier to maintain the code. Changes to one interface are less likely to affect other parts of the system, reducing the risk of introducing bugs or breaking existing functionality.
  • Better Testability : Smaller and more focused interfaces make it easier to write unit tests for individual components. This is because the tests can focus on specific behaviors without being affected by irrelevant methods or dependencies.
  • Increased Flexibility : By adhering to the ISP, the system becomes more flexible and easier to extend or modify. New features or requirements can be added by creating new interfaces or modifying existing ones without affecting the entire system.

Dependency Inversion Principle (DIP)

Dependency Inversion Principle is the final principle of SOLID. Which was also introduced by Robert C. Martin. This promotes loosely-coupled code.

DIP states few points:

  • High-level modules should not depend on low-level modules.
  • Both should depend on abstraction.
  • Abstraction should not depend on details.
  • Details should depend on abstraction.

In simple terms, instead of a class directly depending on other specific classes (concrete implementations), it should depend on interfaces or abstract classes. This makes the code more flexible and easier to maintain, as you can swap out implementations without changing the dependent class.

Tightly coupled code (without DIP)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

As shown in the example above, the Computer class directly depends on the Keyboard class.

Loosely coupled code (with DIP)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}
Copy after login
Copy after login
Copy after login
Copy after login

Now, Computer depends on the InputDevice interface, not a specific Keyboard. This makes it easy to switch to another input device, like a WirelessKeyboard, without modifying the Computer class.

Benefits of following DIP

  • Loose Coupling : By depending on abstractions rather than concrete implementations, the code becomes less tightly coupled, making it easier to change one part of the system without affecting others.
  • Improved Maintainability : Changes in low-level modules do not impact high-level modules, making the system easier to maintain and extend.
  • Enhanced Testability : High-level modules can be tested using mock implementations of the low-level modules, making testing faster and more reliable.
  • Increased Reusability : High-level modules can be reused in different contexts without needing to change the low-level modules they depend on.

Conclusion

In conclusion, the SOLID principles: Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion provide essential guidelines for writing clean, maintainable, and scalable code in object-oriented programming.

By adhering to these principles, developers can create systems that are easier to understand, modify, and extend, ultimately leading to higher quality software and more efficient development processes.

Summary

Thank you for reading this article! I hope you now have a solid understanding of the SOLID principles and how you can apply them to enhance your projects.

Follow me on:
  • LinkedIn — @nsadisha
  • GitHub — @nsadisha
  • Medium — @nsadisha
  • Dev.to — @nsadisha

— Sadisha Nimsara

The above is the detailed content of SOLID Principles. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template