0% found this document useful (0 votes)
3 views11 pages

SOLID Principles

The SOLID principles are a set of design principles aimed at creating more maintainable and flexible software by promoting loose coupling among classes. Each principle, including Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Injection, addresses specific design concerns to enhance code quality and reusability. Additionally, the document emphasizes the importance of favoring composition over inheritance to avoid ambiguity and improve code clarity.

Uploaded by

shivammsdsharma
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views11 pages

SOLID Principles

The SOLID principles are a set of design principles aimed at creating more maintainable and flexible software by promoting loose coupling among classes. Each principle, including Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Injection, addresses specific design concerns to enhance code quality and reusability. Additionally, the document emphasizes the importance of favoring composition over inheritance to avoid ambiguity and improve code clarity.

Uploaded by

shivammsdsharma
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 11

SOLID Principles

The SOLID principle helps in reducing tight coupling. Tight coupling


means a group of classes are highly dependent on one another which you
should avoid in your code. Opposite of tight coupling is loose coupling and
your code is considered as a good code when it has loosely-coupled
classes. Loosely coupled classes minimize changes in your code, helps in
making code more reusable, maintainable, flexible and stable.

Single Responsibility Principle (SRP)

It represents “S” on the SOLID acronym. According to it a class should


have one and only one reason to be changed.

Example:
The following example is a TypeScript class that defines a Person, this class
should not include email validation because that is not related with a
person behaviour:

class Person {
public name : string;
public surname : string;
public email : string;
constructor(name : string, surname : string, email : string) {
this.surname = surname;
this.name = name;
If (this.validateEmail(email)) {
this.email = email;
} else {
throw new Error("Invalid email!");
}
}
validateEmail(email : string) {
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]
{2})?)$/i;
return re.test(email);
}
greet() {
alert("Hi!");
}
}
We can improve the class above by removing the responsibility of email
validation from the Person class and creating a new Email class that will
have that responsibility:

class Email {
public email : string;
constructor(email : string){
if(this.validateEmail(email)) {
this.email = email;
} else {
throw new Error("Invalid email!");
}
}
validateEmail(email : string) {
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]
{2})?)$/i;
return re.test(email);
}
}
class Person {

public name : string;

public surname : string;

public email : Email;

constructor(name : string, surname : string, email : Email) {

this.email = email;

this.name = name;

this.surname = surname;

greet() {

alert("Hi!");

Making sure that a class has a single responsibility makes it per default
also easier to see what it does and how you can extend/improve it.
Open Closed Design Principle

According to this OOP design principle, “Classes, methods or functions


should be Open for extension (new functionality) and Closed for
modification”. The key benefit of this design principle is that already tried
and tested code is not touched which means they won’t break. The Open-
Closed principle is “O” from the SOLID acronym.
An example can be found here:
http://joelabrahamsson.com/a-simple-example-of-the-openclosed-
principle/

Liskov Substitution Principle (LSP)

According to the Liskov Substitution Principle, Subtypes must be


substitutable for supertype I mean methods or functions which use
superclass type must be able to work with the object of subclass without
any issue”. If a class has more functionality than subclass might not
support some of the functionality and does violate LSP. In order to follow
LSP SOLID design principle, derived class or subclass must enhance
functionality, but not reduce them. LSP represents “L” on the SOLID
acronym.
You need to implement your own checks to ensure that your code follows
the Liskov Substitution Principle. In the best case, you do this via code
reviews and test cases. In your test cases, you can execute a specific part of
your application with objects of all subclasses to make sure that none of
them causes an error or significantly changes its performance.

Example:
Let’s consider an Animal parent class.

public class Animal {


public void makeNoise() {
System.out.println("I am making noise");
}
}

Now let’s consider the Cat and Dog classes which extend Animal.

public class Dog extends Animal {


@Override
public void makeNoise() {
System.out.println("bow wow");
}
}

public class Cat extends Animal {


@Override
public void makeNoise() {
System.out.println("meow meow");
}
}

Now, wherever in our code we were using Animal class object we must be
able to replace it with the Dog or Cat without exploding our code. What do
we mean here is the child class should not implement code such that if it
is replaced by the parent class then the application will stop running. For
ex. if the following class is replaced by Animal then our app will crash.

class DumbDog extends Animal {


@Override
public void makeNoise() {
throw new RuntimeException("I can't make noise");
}
}

Read More Example here:


https://springframework.guru/principles-of-object-oriented-design/liskov-
substitution-principle/

Interface Segregation Principle (ISP)

It represents “I” on SOLID acronym. This principle suggests that “many


client specific interfaces are better than one general interface”. This is the
first principle which is applied on interface
E.g. In android we have multiple click listeners like OnClickListener as well
as OnLongClickListener.

public interface OnClickListener {


void onClick(View v);
}

public interface OnLongClickListener {


boolean onLongClick(View v);
}
Why do we need two interfaces just for the same action with one single
tap and another with long press. Why can’t we have the following
interface?

public interface MyOnClickListener {


void onClick(View v);
boolean onLongClick(View v);
}

If we have this interface then it will force clients to implement onLongClick


even if they don’t even care about long press. Which will lead to overhead
of unused methods.

Dependency Injection

Before we can use methods of other classes, we first need to create the
object of that class (i.e. class A needs to create an instance of class B). So,
transferring the task of creating the object to someone else and directly
using the dependency is called dependency injection. It also represents
“D” on the SOLID acronym.

Example 1:
Any application is composed of many objects that collaborate with each
other to perform some useful stuff. Traditionally each object is responsible
for obtaining its own references to the dependent objects (dependencies)
it collaborate with. This leads to highly coupled classes and hard-to-test
code.

For example, consider a Car object.

A Car depends on wheels, engine, fuel, battery, etc. to run. Traditionally we


define the brand of such dependent objects along with the definition of
the Car object.

Without Dependency Injection (DI):

class Car {

private Wheel wh = new NepaliRubberWheel();

private Battery bt = new ExcideBattery();

//The rest
}

Here, the Car object is responsible for creating the dependent objects.

What if we want to change the type of its dependent object - say Wheel -
after the initial NepaliRubberWheel() punctures? We need to recreate the
Car object with its new dependency say ChineseRubberWheel(), but only
the Car manufacturer can do that.

Then what does the Dependency Injection do for us...?

When using dependency injection, objects are given their dependencies at


run time rather than compile time (car manufacturing time). So that we
can now change the Wheel whenever we want. Here, the dependency
(wheel) can be injected into Car at run time.

After using dependency injection:

Here, we are injecting the dependencies (Wheel and Battery) at runtime.


Hence the term : Dependency Injection.

class Car {

private Wheel wh; // Inject an Instance of Wheel (dependency of car)


at runtime

private Battery bt; // Inject an Instance of Battery (dependency of car)


at runtime

Car(Wheel wh,Battery bt) {

this.wh = wh;

this.bt = bt;

//Or we can have setters

void setWheel(Wheel wh) {

this.wh = wh;

Example 2:
Let's try a simple example with Car and Engine classes, any car needs an
engine to go anywhere, at least for now. So below how code will look
without dependency injection.

public class Car {

public Car() {

GasEngine engine = new GasEngine();

engine.Start();

public class GasEngine {

public void Start() {

Console.WriteLine("I use gas as my fuel!");

And to instantiate the Car class we will use next code:

Car car = new Car();

The issue with this code is that we tightly coupled to GasEngine and if we
decide to change it to ElectricityEngine then we will need to rewrite Car
class. And the bigger the application the more issues and headaches we
will have to add and use new types of engine.

In other words with this approach is that our high level Car class is
dependent on the lower level GasEngine class which violates Dependency
Inversion Principle(DIP) from SOLID. DIP suggests that we should depend
on abstractions, not concrete classes. So to satisfy this we introduce
IEngine interface and rewrite code like below:

public interface IEngine {

void Start();

public class GasEngine : IEngine {


public void Start() {

Console.WriteLine("I use gas as my fuel!");

public class ElectricityEngine : IEngine {

public void Start() {

Console.WriteLine("I am electrocar");

public class Car {

private readonly IEngine _engine;

public Car(IEngine engine) {

_engine = engine;

public void Run() {

_engine.Start();

Now our Car class is dependent on only the IEngine interface, not a
specific implementation of engine. Now, the only trick is how do we create
an instance of the Car and give it an actual concrete Engine class like
GasEngine or ElectricityEngine. That's where Dependency Injection
comes in.

Car gasCar = new Car(new GasEngine());

gasCar.Run();

Car electroCar = new Car(new ElectricityEngine());

electroCar.Run();
Here we basically inject(pass) our dependency(Engine instance) to the Car
constructor. So now our classes have loose coupling between objects and
their dependencies, and we can easily add new types of engines without
changing the Car class.

The main benefit of the Dependency Injection is that classes are more
loosely coupled, because they do not have hard-coded dependencies. This
follows the Dependency Inversion Principle, which was mentioned above.
Instead of referencing specific implementations, classes request
abstractions (usually interfaces) which are provided to them when the
class is constructed.

So in the end Dependency injection is just a technique for achieving loose


coupling between objects and their dependencies. Rather than directly
instantiating dependencies that class needs in order to perform its actions,
dependencies are provided to the class (most often) via constructor
injection.

Composition over Inheritance

There are two general ways to reuse the code you have already written,
Inheritance and Composition, both have their own advantages and
disadvantages, but, in general, you should always favor composition over
inheritance, if possible.

Inheritance

This encourages the use of classes. Inheritance is one of the three tenets of
OO design (inheritance, polymorphism, encapsulation).

class Person {

String Title;

String Name;

Int Age;

class Employee : Person {

Int Salary; String Title;

This is inheritance at work. The Employee "is a" Person or inherits from
Person. All inheritance relationships are "is-a" relationships. Employee also
shadows the Title property from Person, meaning Employee.Title will
return the Title for the Employee not the Person.

Composition

Composition is favoured over inheritance. To put it very simply you would


have:

class Person {

String Title;

String Name;

Int Age;

public Person(String title, String name, String age) {

this.Title = title;

this.Name = name;

this.Age = age;

class Employee {

Int Salary;

private Person person;

public Employee(Person p, Int salary) {

this.person = p;

this.Salary = salary;

Person johnny = new Person ("Mr.", "John", 25);

Employee john = new Employee (johnny, 50000);

Composition is typically "has a" or "uses a" relationship. Here the Employee
class has a Person. It does not inherit from Person but instead gets the
Person object passed to it, which is why it "has a" Person.
Composition over Inheritance

Now say you want to create a Manager type so you end up with:

class Manager : Person, Employee { ... }

This example will work fine, however, what if Person and Employee both
declared Title? Should Manager.Title return "Manager of Operations" or
"Mr."? Under composition this ambiguity is better handled:

Class Manager {

public string Title;

public Manager(Person p, Employee e) {

this.Title = e.Title;

The Manager object is composed as an Employee and a Person. The Title


behaviour is taken from the employee. This explicit composition removes
ambiguity among other things and you'll encounter fewer bugs.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy