OOP Principles/Concepts

Inheritance
Inheritance is the procedure in which one class inherits the attributes and methods of another class. The class whose properties and methods are inherited is known as the Parent class. And the class that inherits the properties from the parent class is the Child class.
The key to understanding Inheritance is that it provides code re-usability. In place of writing the same code, again and again, we can simply inherit the properties of one class into the other.

Polomophism
Polymorphism means "many forms", and it occurs when we have many classes that are related to each other by inheritance.

As describe above Inheritance lets us inherit attributes and methods from another class. Polymorphism uses those methods to perform different tasks. This allows us to perform a single action in different ways.
Polymorphism in Java is a concept by which we can perform a single action in different ways. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object.
There are two types of polymorphism in Java: compile-time polymorphism and runtime polymorphism. We can perform polymorphism in java by method overloading and method overriding.

If you overload a static method in Java, it is the example of compile time polymorphism

Runtime polymorphism or Dynamic Method Dispatch is a process in which a call to an overridden method is resolved at runtime rather than compile-time.an overridden method is called through the reference variable of a superclass. The determination of the method to be called is based on the object being referred to by the reference variable.

Encapsulation
is a mechanism of wrapping the data (variables) and code acting on the data (methods) together as a single unit. In encapsulation, the variables of a class will be hidden from other classes, and can be accessed only through the methods of their current class. Therefore, it is also known as data hiding.
To achieve encapsulation in Java −
  • Declare the variables of a class as private.
  • Provide public setter and getter methods to modify and view the variables values.
Advantage of Encapsulation in Java
  • By providing only a setter or getter method, you can make the class read-only or write-only.
  • control over the data. Suppose you want to set the value of id which should be greater than 100 only, you can write the logic inside the setter method. 
  • data hiding in Java because other class will not be able to access the data through the private data members.
Abstraction
Data abstraction is the process of hiding certain details and showing only essential information to the user.Abstraction can be achieved with either abstract classes or interfaces.
The abstract keyword is a non-access modifier, used for classes and methods:
  • Abstract class: is a restricted class that cannot be used to create objects (to access it, it must be inherited from another class).
  • Abstract method: can only be used in an abstract class, and it does not have a body. The body is provided by the subclass (inherited from).

SOLID principle
The SOLID Principles are five principles of Object-Oriented class design. They are a set of rules and best practices to follow while designing a class structure.They are a set of rules and best practices to follow while designing a class structure.
These principles establish practices that lend to developing software with considerations for maintaining and extending as the project grows. Adopting these practices can also contribute to avoiding code smells, refactoring code, and Agile or Adaptive software development.
  • S - Single Responsibility Principle (known as SRP)

A class should have only one reason to change. in other word ; A class should have only one responsibility.
we should take care that one class at the most is responsible for doing one task or functionality among the whole set of responsibilities that it has. And only when there is a change needed in that specific task or functionality should this class be changed.
class patient{
String name;
int code;
Date admitDate;
Date dischargeDate;

}

// Admit and discharge date determine/controll/change by the hospital, specially Visit object own that responsibility ;
// So those 2 attribute are not belong to patient object according to the single responsibility principle;
// once refactor

class patient{
String name;
int code;


}

class Visit{
Date admitDate;
Date dischargeDate;
}
  • O - Open/Closed Principle
A class can be considered to be closed if its runtime or compiled class is available for use as a base class which can be extended by child classes. It should make sure that changes are guaranteed to not happen. In short – The said class is open for extension.
A class can be considered to be open if its functionality can be enhanced by sub-classing it. When a class is a sub-class then by using the Liskov Substitution rule it can be replaced by its sub-class. This sub-class behaves as its parent class but is an enhanced version of it. In short – The said class is open for modification(via extension).
class CalculateGarmentSellingPrice {
double calculateFrockPrice(double unitMaterialPrice, int qty, double serviceCharge, int frockSize) {
return (unitMaterialPrice * qty * frockSize) + serviceCharge;
}

double calculateWinterJacketPrice(double unitMaterialPrice, int qty, double serviceCharge, int size, double seasonalPrice) {
return (unitMaterialPrice * qty * size) + serviceCharge + seasonalPrice;
}
}

// If we have a class like above ;we have to change every time if garment introduce a new garment type
// We can go for below approach which will satisfy open close principle
interface Garment {
double getPrice();

}

class Frock implements Garment {
int itemCode;
int itemTypeCode;
String Material;
int sizeCode;
double serviceCharge;
double unitMaterialPrice;

@Override
public double getPrice() {
return serviceCharge + (unitMaterialPrice * sizeCode);
}
}

class WinterJackets implements Garment {
int itemCode;
int itemTypeCode;
String Material;
int sizeCode;
double serviceCharge;
double unitMaterialPrice;
double seasonalDemandPrice;

@Override
public double getPrice() {
return serviceCharge + seasonalDemandPrice + (unitMaterialPrice * sizeCode);
}
}

class CalculateGarmentSellingPrice{

double calculateGarmentPrice(Garment garment){
return garment.getPrice();
}
}

  • L - Liskov’s Substitution Principle
Liskov Substitution Principle prescribes substitutability of a class by its subclass. Broadly put, a class can be replaced by its subclass in all practical usage scenarios. A superclass reference can hold a subclass object i.e. superclass can be replaced by subclass in a superclass reference at any time. So, Java inheritance mechanism follows Liskov Substitution Principle.


class Oval {
int width;
int height;

void setWidth(int w) {
width = w;
}

void setHeight(int h) {
height = h;
}
}

class Circle extends Oval{

}

class testOval{

public static void main(String args[]){
Circle circle = new Circle();
circle.setHeight(5);
circle.setWidth(8);
}


}

// Above example is not correct as we can set the differen width and height for a circle
//So we need to refactor above class list to below to get Liskov substitution principle
class Shapes {
int width;
int height;

void setWidth(int w) {
width = w;
System.out.println(" set shape width ");
}

void setHeight(int h) {
height = h;
System.out.println(" set shape height ");

}
}

class Circles extends Shapes{
@Override
void setHeight(int h) {
super.setWidth(h);
super.setHeight(h);
System.out.println(" set circle diameter ");
}

@Override
void setWidth(int w) {
super.setHeight(w);
super.setWidth(w);
System.out.println(" set circle diameter ");
}
}
class Ovals extends Shapes{

}

class testShapes{

public static void main(String args[]){
Shapes circle = new Circles();
Shapes ovel = new Ovals();
circle.setHeight(5);
ovel.setWidth(8);
}


}
Relation of Liskov Substitution Principle with Open Closed Principle
Open Closed Principle says that a class should be open for extension and closed for modification. I.e. to modify what a class does, we should not change the original class. Rather, we should override the original class and implement the functionality to be changed in the overriding class. This way when the derived class’s(or the sub-type’s) object is used in place of the parent/super-class, then the overridden functionality is executed on executing the same functionality. This is exactly in line with the Liskov Principle we just saw above.
  • I - Interface Segregation Principle
 Many client specific interfaces are better than one general purpose interface
 Instead of having a single interface catering to all the clients, i.e. holding all the methods for all the clients, it is better to have multiple interfaces with each interface containing methods for a client-specific functionality or to have functionally cohesive interfaces.

interface RestuarentOrder{
void acceptOnlineOrder();
void takeTelephoneOrder();
void payOnline();
void payWalkIn()
}

class onlineorder implements RestuarentOrder{
public void acceptOnlineOrder() {
//logic for placing online order
}
@Override
public void takeTelephoneOrder() {
//Not Applicable for Online Order
throw new UnsupportedOperationException();
}

@Override
public void payOnline() {

}

@Override
public void payWalkIn() {

}


}

//In the above logic online order client has to implemet telephone order related method even though it is not need for that client.
// And any changes done to resturantorder interface will be propergate to all the class which implement it.
// and also resturantorder interface brak the single responsibility principle.

interface orderInterface{
void placeOrder();
}

interface payInterface{
void payOrder();
}

class onlineClient implements payInterface,orderInterface{

@Override
public void placeOrder() {

}

@Override
public void payOrder() {

}
}
  • D - Dependency Inversion Principle
Depend upon Abstractions. Do not depend upon concretions.
In onother way
Abstractions should not depend upon details. Details should depend upon abstractions.
In other words
High-level modules should not depend on low-level modules. Both should depend on abstractions.

Dependency Inversion Principle is away with this tight-coupling between layers by introducing a layer of abstraction between them. DIP is about inverting the classic dependency between high-level and low-level components by abstracting away the interaction between them.

Service layer in the Java Spring Framework uses Dependency Inversion Principle.

class operationItems {
OpProfile profile = new OpProfile();
OpSurgoen surgoen = new OpSurgoen();

void makeOpProfileCombination() {
profile.addProfile();
surgoen.addSurgeon();
}
}

class OpProfile {
public void addProfile() {

}
}

class OpSurgoen {
public void addSurgeon() {

}
}
// Here operationItems class is directly depend on low level modules(OpSurgoen,OpProfile) which violates DIP
//By using interface OpItems we can follow with the DIP principle

interface OpItemes {
void add();
}

class operationProfile1 {
void makeOpProfileCombination(OpItemes itemes) {
itemes.add();
}
}

class OpProfile1 implements OpItemes {
public void addProfile() {
//todo method
}

@Override
public void add() {
this.addProfile();
}
}

class OpSurgoen1 implements OpItemes {
public void addSurgeon() {
//todo method
}

@Override
public void add() {
this.addSurgeon();
}
}

public class DipendencyInversionPrinciple {

public static void main(String[] args) {
OpItemes profile = new OpProfile1();
OpItemes surgoen = new OpSurgoen1();
operationProfile1 operationProfile1 = new operationProfile1();

operationProfile1.makeOpProfileCombination(profile);
operationProfile1.makeOpProfileCombination(surgoen);

}
}


Comments