OOP Principles

These principles are guidelines intended for programmers to apply while working on software to remove buggy code. 

1. Single Responsibility Principle (SRP)

The Single Responsibility Principle (SRP) states that there should never be more than one reason for a class to change. This means that every class, or similar structure, in your code should have only one job to do. Simply, a class should have only a single responsibility.

  • The class should only have one and only one reason to be changed. When there is more than one responsibility, there is more than one reason to change that class at the same point. So, there should not be more than one separate functionality in that same class that may be affected.
  • For Example, If a class Sales keeps the information about a sales order, and in addition has a method saveOrder() that saves the Sales in a database and a method exportXML() that exports the Sales in XML format, this design will violate the SRP because there will be different types of users of this class and different reasons for making changes to this class. A change made for one type of user, says the change in the type of database, may require the re-test, recompilation, and re-linking of the class for the other type of user.
  • A better design will be to have the Sales class only keeps the information about a sales order, and have different classes to save the order and export order, respectively. Such a design will confirm with SRP.
  • The SRP principle helps us to deal with bugs and implement changes without confusing co-dependencies.

2. Open-Closed Principle (OCP)

The Open-Closed Principle states that classes should be open for extension but closed for modification. “Closed for modification” means that once you have developed a class you should never modify it, except to correct bugs. “Open to extension” means that you should design your classes in such a way that new functions will be generated as per new requirements are generated. Simply, “software entities like classes, modules, functions, should be open for extension, but closed for modification”.

  • For Example, Suppose an OrderValidate class has a method validate(Order order) that is programmed to validate an order based on a set of hard-coded rules. This design violates the OCP because if the rules change,  the OrderValidate class has to be modified, compiled, and tested.
  • A better design will be to let the OrderValidate class contain a collection of ValidationRule objects each of which has a validate(Order order) method (perhaps defined in a Validation interface) to validate an Order using a specific rule, and the validate(Order order) method of OrderValidate class can simply iterate through those ValidationRule objects to validate the order. The new design will satisfy the OCP because if the rules change, one can just create a new ValidationRule object and add it to an OrderValidation instance at run time (rather than to the class definition itself).

3. Liskov Substitution Principle (LSP)

The principle was introduced by Barbara Liskov in 1987 and according to this principle “Derived or child classes must be substitutable for their base or parent classes“. It applies to inheritance hierarchies, specifying that you should design your classes so that client dependencies can be substituted with subclasses without the client knowing about the change.

  • For Example, Suppose a Rectangle class has two instance variables height and width, and a method setSize(int a, int b), which set the height to a and width to b. Suppose Square is a subclass of Rectangle and it overrides the inherited method by setting both height and width to a. This design will violate LSP. 
  • To see this, consider a client using a reference variable of type Rectangle to call the setSize() method to assign different values of a and b, and then immediately verify if the sizes were set correctly or the area is correctly computed. 
  • The results will be different if the variable references a Rectangle object than a Square object. It turns out that in OO programming, a Square is not a Rectangle at all because it behaves differently from a Rectangle.

Generally, if a subtype of the supertype does something that the client of the supertype does not expect, then this is in violation of LSP.

4. Interface Segregation Principle (ISP)

This principle is the first principle that applies to Interfaces instead of classes in SOLID and it is similar to the single responsibility principle. It states that clients should not be forced to depend upon interface members they do not use. This means that the interface should have the minimal set of methods required to ensure functionality and be limited to only one functionality.

  • For Example, If we create a Burger interface, we don’t need to implement the cheese() method because cheese isn’t available for every Burger type. 

5. Dependency Inversion Principle (DIP)

DIP principle The DIP requires that high-level modules should not depend on low-level modules, both should depend on abstraction. Also, abstraction should not depend on details, details should depend on abstractions. Secondly, abstractions should not depend upon details; details should depend upon abstractions. The idea is that we isolate our class behind a boundary formed by the abstractions it depends on. If all the details behind those abstractions change, then our class is still safe. This helps keep coupling low and makes our design easier to change. DIP also allows us to test things in isolation.

  • For Example, Making a class Button associate with another class Lamp (because a Lamp has a Button) is a violation of DIP. Instead, a better design will be to associate an AbstractBut with an AbstractButClient and define a Button as a subclass of the AbstractBut and a Lamp as a subclass of the AbstractButClient.
  • Another example can be, making an EBookReader class use PDFBook class is a violation of DIP because it requires changing the EBookReader class to read other types of e-books. A better design is to let EBookReader use an interface EBook and let PDFBook and other types of e-book classes implement EBook. Now adding or changing e-book classes will not require any change to EBookReader class.

6. KISS – Keep It Simple, Stupid Principle

This principle suggests not involving complexity in the code and trying to avoid it as much as you can. This is because the more complex code is written, the more difficult it becomes to modify at any later point in time. Other acronyms are: Keep it short and simple, Keep it simple and smart, and Keep it simple and straightforward.

  • The basic idea is to achieve goals and solve problems by building solutions as simple as possible and as short as can be. 
  • It helps in solving problems quickly, maintainable code, easy to debug, and high-quality, human-readable code.
  • If someone writes messy code, defines functions with unnecessary lines of code, etc. it leads to a violation of the KISS principle.

7. DRY Principle

The DRY principle stands for the “Don’t Repeat Yourself” principle. Programmers tend to write lots of duplicate code intentionally or unintentionally. This principle forces us to avoid this habit. 

  • To follow the DRY principle, we can – reuse the code and never duplicate it, use functions, and recursions, and write code in appropriate layers, locations, and services.

Below is the Java program to demonstrate the DRY principle:

Java




public class Animal { 
       public void eat() { 
           System.out.println("Eating"); 
       
}
public class Dog extends Animal { 
    public void bark() { 
        System.out.println("Barks"); 
    
public class Cat extends Animal { 
    public void meow() { 
        System.out.println("Meows"); 
    
}


Though both cat and dogs have common functionality eat(), they speak differently. So we put common functionality eating in the parent class Animal and then extend the parent class to child classes Dog and Cat. Repeating the code is a violation of the DRY principle.

8. YAGNI – You Ain’t Gonna Need It Principle

YAGNI stands for You Ain’t Gonna Need It. It simply means, don’t use or write code for something, until you really find value in doing it. The programmer usually implements so many things that they really don’t need. They write so many lines of code that are actually not required or may be useless. This leads to a waste of time and energy for the programmer and can even financial losses in the company product. In short, this principle is against the building of any features which are not required at present. So that developer can save time and focus on other pieces or components of a code.

9. DI – Dependency Inversion or Dependency Injection

This principle revolves around the coupling. Coupling is the degree of connectivity among things, that is how your piece of code is connected to each other. Simply, if your code is talking about another piece of code, there is coupling. A great example could be traditional class-based inheritance. Inheritance actually increases coupling quite a bit. Now as per this principle, either remove or minimize dependency to the extent it could be. If you can’t remove all dependency then at least minimize it. In the context of object-oriented design, depending on a class is called tight coupling, whereas depending on an interface, is called loose coupling.

10. Composition Over Inheritance Principle

This principle helps us to implement flexible and maintainable code. It states that we should have to implement interfaces rather than extending the classes. We implement the inheritance when the class need to implement all the functionalities and the child class can be used as a substitute for our parent class.

In Java, there are different types of Inheritance: Single inheritance, Multilevel inheritance, Hierarchical inheritance, Multiple inheritances, and Hybrid inheritance. Learn more about inheritance.

Java




//Inheritance example
import java.io.*;
 
// Base or Super Class
class Employee{
    int salary = 45000;
      int leaves = 5;
}
 
// Inherited or Sub Class
class employeeOfTheMonth extends Employee {
    int benefits = 10000;
      int leaves = 7;
}
 
// Driver Class
class Gfg {
    public static void main(String args[])
    {
        employeeOfTheMonth e1 = new employeeOfTheMonth();
        System.out.println("Salary is: " + e1.salary);
          System.out.println("Benefits: " + e1.benefits);
          System.out.println("Leaves: " + e1.leaves);
    }
}


Following these principles and OOP principles, any developer can build good and efficient software and products which can help the community.



Design Goals and Principles of Object Oriented Programming

The fundamental goal of dealing with the complexity of building modern software naturally gives rise to several sub-goals. These sub-goals are directed at the production of quality software, including good implementations of data structures and algorithms. The article focuses on discussing design goals and principles of Object Oriented Programming. 

Similar Reads

OOP Goals

The three goals of Object Oriented Programming are Robustness, Adaptability, and Reusability....

OOP Principles

These principles are guidelines intended for programmers to apply while working on software to remove buggy code....