Skip to main content

Solid Principles in Object Oriented Programming

SOLID Principles in Object Oriented Programming

Solid Principles in Object Oriented Programming

Introduction

SOLID is an acronym that represents five principles of object-oriented programming and design, aimed at making software systems more maintainable and scalable. The SOLID principles are:

  1. Single Responsibility Principle (SRP) - A class should have only one reason to change, meaning that a class should have only one responsibility.

  2. Open-Closed Principle (OCP) - Software entities should be open for extension but closed for modification, meaning that a class should be easily extendable without modifying its existing code.

  3. Liskov Substitution Principle (LSP) - Subtypes should be substitutable for their base types, meaning that objects of a derived class should be able to replace objects of the base class without affecting the correctness of the program.

  4. Interface Segregation Principle (ISP) - Clients should not be forced to depend on interfaces they do not use, meaning that an interface should only include methods that are relevant to its clients.

  5. Dependency Inversion Principle (DIP) - High-level modules should not depend on low-level modules. Both should depend on abstractions, meaning that high-level components should depend on abstractions, not on concrete implementations.

Single Responsibility Principle (SRP)

Let there be a class Employee that represents an employee in an organization. Employees should have only one responsibility, which is to store and retrieve employee information. Additionally, if we add a responsibility for loggingemployee data changes, it would be in violation of the SRP

The Employee class in this example has only one responsibility, which is to store and retrieve employee data, and it does not violate the SRP. We can create another class that is responsible for logging changes to employee data.

By separating the responsibilities of storing and retrieving employee data from logging changes, we have made the code more flexible and maintainable.

Open-Closed Principle (OCP)

Consider the case where we have a class Shape that represents a geometric shape, and we wish to calculate the area of different geometric shapes. It is possible to create subclasses for each shape and provide the implementation for each shape by overriding the calculateArea method.

Now, consider the following derived classes from Shape:

By creating new subclasses and implementing the calculateArea method, we can add new shapes without modifying the existing code. According to the OCP, the Shape class is closed for modification and open for extension. As a result, we are able to add new shapes without having to change the existing code, which makes the code more flexible and easier to maintain.

Liskov Substitution Principle (LSP)

Let us assume that we have an interface Rectangle that defines a rectangular shape and a method calculateArea that calculates the area of that rectangle. Additionally, we have a class Square, which represents a square, which is a special case of a rectangle.

For the purpose of this example, the Square class implements the Rectangle interface and implements all the methods defined by the Rectangle interface. Due to its conformance to the LSP, the Square class can be used wherever a Rectangle is required, since its subtypes must be substitutable for their base types. As a result, a method that expects a Rectangle can also accept a Square as an argument without affecting the program's correctness.

Interface Segregation Principle (ISP)

Let us suppose that we have an interface Printer that defines methods for printing different types of documents, such as text documents, image files, and PDF files. Additionally, we have two classes that implement the Printer interface, TextPrinter and ImagePrinter defined as follows:

There are too many methods in the Printer interface in this example which are not relevant for all implementations. According to the ISP, a client should not be forced to rely on interfaces that are not used by them. We may be able to resolve this issue by splitting the Printer interface into multiple smaller interfaces, each of which defines a specific type of document as follows:

As a result of this design, the TextPrinter and ImagePrinter classes will be able to implement only the interfaces they need, which makes the code easier to maintain and less prone to errors.

Dependency Inversion Principle (DIP)

Consider two modules: HighLevelModule and LowLevelModule. In order to perform some functions, HighLevelModule relies on LowLevelModule.

Due to the dependency between HighLevelModule and LowLevelModule, this design violates the DIP. As outlined in the DIP, high-level modules should not depend on low-level modules, but both should be dependent on abstractions. As a solution to this problem, we can introduce an interface WorkModule that defines the work that is to be performed, and both HighLevelModule and LowLevelModule classes can implement the following interface as:

By using this design, both HighLevelModule and LowLevelModule classes are dependent on the abstraction WorkModule, which makes the code more maintainable. There is no effect on the LowLevelModule class when changes are made to the HighLevelModule class, and vice versa.

Web References

  • sarcar2022, Sarcar V., Java Design Patterns: A hands-on experience with Real-World Examples, Chapter 1, pp.3-64, APress, 2022.

  • weisfeld2019, Weisfeld M., Object-Oriented Thought Process, 5th Ed, Chapter 12, Addison-Wesley, 2019.

  • joshi2016, Joshi B., Beginning SOLID Principles and Design Patterns for ASP.NET Developers, APress, 2016.

  • martin2008, Martin R., Clean Code: A Handbook of Agile Software Craftsmanship, Pearson, 2008

 

Comments

Popular posts from this blog

Linear Least Squares Methods with R: An Algebraic Approach - Part I

Linear Least-Squares Method with R - Part I Linear Least Squares Methods with R: An Algebraic Approach - Part I Algebraic Approach Principles The least-squares method is one of the most well-known linear optimization methods because of its flexibility. Furthermore, it gives a reasonable approximation of a given function. Among the diverse applications that it can be used for is Regression in statistical learning, Direct Linear Transformation methods in projective geometry, and so on. We will demonstrate the principles of least squares methods and implement examples in R in this article. Consider the above figure of a simple linear system where the x variable is an input variable, A is a measurements matrix, and y is the output variable. That is, the model for the equation is given by (1) y = A x , which in a more explicit notation from (1) can be expressed as (2) [ y 1 y 2 ⋮ y i ] = [ a 1 , 1 a 1 , 2 ⋯ a 1 , i a 2 , 1 a 2 , 2 ⋯ a 2 , i ⋮ ⋮ ⋯ ⋮ a j , 1 a j , 2 ⋯ a j , i ] [...

Variance concepts in the context of parametric programming with Java

Variance concepts in the context of parametric programming with Java By Obed Rios (5/7/2023 ) Revision 1.0 Abstract In Java, the concepts of variance are related to how the type parameters of a class or interface are related to each other when the class or interface is sub-typed or implemented. The key difference between invariant and covariant in the context of Java generics is how they handle sub-typing relationships. Invariant types do not allow assignments between different type parameters, while covariant types can accept a specified type or any of its sub-types. In addition contravariance enables you to use a more general type (super type) in a generic type or method that would normally require a more specific type (sub-type). In this work, we show explicitly the concepts of variance in the context of Java Generics. Introduction Parametric variance refers to the relationship between the type parameters of a class or interface and their subtypes. It defines how subtyping is i...