Why Make Software Engineer Principles SOLID?

AnyOneCanCode
26 min readApr 18, 2021

--

SOLID Principles

Introduction:

Let’s start the blog with a deep breath. This Blog is not about the states of matter like solid, liquid, or gases (what software engineers have to do with chemistry. we don’t need chemicals to burn systems. poorly written code is enough).

This blog is about good programming practices, design principles and to be specific we are discussing SOLID principles.

Note: this blog is not for beginners. beginners should focus on writing a lot of code even it is a very dirty code(inefficient). they should first learn a language fundamental. write a lot of code, do personal projects, or any kind of shit you like. do a lot of mistakes in code and it is completely fine for beginners. (but what about when you are working in the company)

Think of it as a football game when a newbie plays nobody expects a perfect pass or shoot. newbie should not focus on the tactics involved in a team game. you can’t learn football in a single day it needs a lot of practice and that involved lot of mistakes.

If an experienced software professional is writing dirty code uhhhh then you are in the wrong profession. you should try to become a singer, boxer, painter, etc whatever your passion.

What are Principles and why should learn? :

it is important to understand first about principle. why the word principles exist in the coding world?

what is the meaning of principles?

it is a fundamental law that is validated by people over many years. people have executed it for many years and finally when they can’t disproof. it becomes principles. so simply we can say it is a fundamental truth.

why we need principles?

To put it simply, we need principles to not repeat mistakes.
When professionals are developing software for years they realized they are doing a common design mistake(i am not discussing missing semicolon in end dumbo).
While developing software they realized it is hard to maintain a codebase or it is not easy to make it extendable(means feature requirement grows).

how to deal with growing complexity due to growing features? Remember most software start with a basic feature(login, signup, etc) later it becomes set of unlimited feature. take the example of Facebook it is started with basic functionality (signup, login, profile, friends list, etc) but now it consists of unlimited features.

So the goal is to make software easy to maintain, flexible(easy to extend), and understandable. so professionals in history developed numerous software and done a lot of mistakes in design. faced a lot of problems to deal with the growing complexity of the codebase consider the case when thousands of people are working simultaneously(how to deal with the complexity of the project). Then they realized they are not alone in this. Everyone is facing these challenges.

To put it simply principles exist to save your ass. the goal of principles is to save developers from mistakes that numerous developers have already done. principles are not created overnight it is a sum equal to a decade's experience of professionals. it saves the cost of effort, time & money for the organization.

principles help make software look simple, easy to use, easy to read, extendable to unlimited features, and require low maintenance costs. it is only possible when design and code quality is good for the codebase.

Why the hell I need clean code or good design? when I can write a lot of code and make functionality complete

It is important to discuss why we need good design and clean code practices?
How much disastrous it can go? we will discuss those people who focus solely on completing features anyhow and how it significantly impacts business.

Let’s take a simple example to understand real-world case:

There is a new project started in the company like a newborn baby.
Starting they have a limited feature list and need to quickly rock and roll in the market so they hired few developers.

the manager sent the feature list to the developer and told them to complete it quickly with a timeline of a weak(we need to rock and roll in market boy ).

The developers started working they are professional dumb developers. they focused on completing the feature list instead of design and code quality.
as soon as the feature is completed they are in hurry to go home to spend time with a beautiful wife or children(or get drunk at night) without worry about the poor design of the system and code quality(who cares feature is complete).

Work Completed

They know management wants the feature to get completed and they are not technical people who understand the quality of work.
Management is happy that they have hired the right people who complete features quickly. Boss is happy he is appreciating the team for their effort.

This is happy perfect life everyone is happy. the whole room is spread with the light of enthusiasm. people are on top of their motivation and ready to conquer the market from their product.

After 1–2 years things are different from the start. now the project is not a newborn baby. it is running as a mature product in the market.
it has a large user base. starting you can make many mistakes as few people are using, but now even a slight mistake in functionality impacts user experience. competition is tough in the market as opponents also doing the same thing, so the company needs to constantly improve the product.

Now codebase is large it has tons of files, classes, methods, etc. it is not a small repo now. this is the starting of problems now a real test of the project and developers started.

Management gives a new set of feature lists for the product.
when developers started implementing features they felt it is tougher to add features in comparison to starting. now projects contain tons of files, components, class, etc and they are interrelated to each other. any change in class affects much connected classes.
it is hard to find what purpose class serves by directly looking into it. naming is not consistent also so hard to read code. bugs are coming more frequently than in starting. the rate of bugs on the server is increasing.

now developers are afraid to make changes as it can affect previous functionality they might lose the job if some functionality gets broken on production. who will test the already tested feature( involves cost time and time is gold)?

So-called software professionals knew that they have done mistakes for not focusing on good design and clean code early. now, projects can’t be stopped or they can not spend months for rework.

Time for another idea to make management fool. the evergreen idea we need more developers to work on the project.

let’s hire more developers

To save the ass more hiring is a good idea. we can win by brute force then intelligence. all people are thinking project is big and complex. they have a more user base now they really need more developers to meet the needs of markets. nobody is ready to accept their design and code sucks that’s why it needs a lot of people to manage.

management can’t do anything (don’t know about it). so it started hiring more people.

think about the scenario when you see the big mess and instead of cleaning it, you add many new people to make a bigger mess. this is the same case.
now the project contains a hell lot of people. they all working on the same code base and foundation design of the project.

Now every changing year management needs to spend many times cost(compare to the previous year) on developers. even a small change in the product takes a lot of time. maintenance cost is extremely high.

Think about the project after 5 years? what will happen when a hell lot of people work in poor design and do dirty code practices.

now after 5 years management realized the product is taking a lot of cost for maintenance, they need to hire a lot of developers every year and the project is not flexible like they can add new feature easily. they can not create a new same project (business is running it don’t get develop overnight) or can rework it(involves cost and time).

now the founder developer of the project is long gone from a company they can't blame anyone. they don’t have an option they can only drag the project more if they have a lot of money still left in a pocket or can decide to close the product(death for the project). if the company is making a lot of money they might drag the project 5 years more but it will still be a big mess.

Moral of the story:

Put simply products get died with poor design and dirty code. now I hope you can understand how disastrous the myopia view can be.

long term problems required long-term solutions. good design and clean code practice is not the fruit that grows overnight or in a weak. it is a proper plant with beautiful flowers and yummy fruits. it is a long-term view of the project.
the principle exists to make software simple irrespective of growing complexity.it is the fundamental concept that every professional must know and should implement(part of the work).

Now we can discuss the main star SOLID principles because we are clear why we need it(at least me).

SOLID PRINCIPLES:

Yes, software development is not a Jenga game. While playing this game you have to be careful. you need to add and remove the block carefully so the whole building doesn’t get collapsed.

Don’t play Jenga with software. suppose block of Jenga is a new feature or component(whatever you want to call) of software. you should not be afraid to add or remove blocks from the software.
it should not be like that if a block removed(or add) the whole software gets collapsed. software should have the flexibility to stand strong(easily maintainable) & extendable(add any feature) to any limit.
the individual block should not be tightly dependent on each other any kind of change in one block should not impact another block.

SOLID principles were first introduced by Robert J. martin also known as Uncle BOB. he is also the author of the famous book clean code, clean architecture(go and read). I encouraged every software developer to read it.

The SOLID principle's purpose is simple it is a set of practices. you follow the practice to develop software simple (break complexity into simplicity), easy to maintain & extendable with time.

Note: it is a common simple definition you can get from anywhere around the web. but don’t think if you read the principle you can develop the software easily like that.

see anyone can read and remember the theory part. do you think just knowing principles theoretically will make you a great software developer(don’t daydream)? it is not a viva test that you can learn one night ago and brag in front of a teacher to prove you are smart af.

Only from constant practice (daily), you can truly implement principles. It is a practice recommended by professionals from their years of experience(it saves your ass).

while working you need to implement it daily to truly understand it. don’t write code just to complete the functionality. completion of a feature is an easy task(they give it because you can complete it) real work start after that. it is the nature of work(practices means) that you need to implement inside.

Think about the rocket scientist. Maybe he has read a lot of theory and believes can make a great rocket. but what is the benefit if hasn’t contributed to the development of the single rocket? is he really a rocket scientist. real work applies to the world not in dreams.

Just a meme

SOLID stands for

S: Single Responsibility Principle
O: Open-Closed Principle
L: Liskov Substitution Principle
I: Interface Segregation Principle
D: Dependency Inversion Principle

Single Responsibility Principle

To put simply “Do one thing and do it well” is the single responsibility principle.

Whether you make method, service class or component rule is simple and straightforward, it should do only and only one thing. don’t complicate code by mixing things. actually in the coding world multitasking is bad.

let’s take a very simple example to understand it.

void createBook() {
// logic to create book
Book book = new Book();
book.setName(“bla bla bla”);
book.setprice(“bla bla bla”);
……..

// logic to create author of book
Author author = new Author();
author.setName(“bla bla bla”);
author.setNumberOfPublications(“bla bla bla bla”);
………

// logic to creat publication of book
Publication publi = new Publication();
publi.setName(“bla bla bla”);
…….


book.setAuthor(author);
book.publication(publi);
}

The above method is creating a new book and it involves the creation of the author and publication of the book also.
Now take a minute to think and answer what is wrong in code(not asking for any error exists). is it a good code or my work is completed?

The above method clearly violating the single responsibility principle. it is doing a lot of things creating the book, author, and publication. now imagine you want to add new data for the book later in the future like the number of users, pages, calculate profit, etc will change the same method every time. it will impact the existing functionality and clearly, it is not extendible.
so it must do only one thing let’s rewrite it again.

void createBook() {

// logic to create book
Book book = new Book();
book.setName(“bla bla bla”);
book.setprice(“bla bla bla”);
......

book.setAuthor(createAuthor());
book.publication(createPublication());
}

Author createAuthor() {

// logic to create author of book
Author author = new Author();
author.setName(“bla bla bla”);
author.setNumberOfPublications(“bla bla bla bla”);
………
return author;
}

Publication createPublication() {

// logic to creat publication of book
Publication publi = new Publication();
publi.setName(“bla bla bla”);
…….
return publi;
}

Now it is better every method has a single responsibility. creating the book, author, and publication. later if want to do a change on the author we only need to work on the author method or we need to add a new feature to the book we can add a new method and can associate the feature(extendible).
if you have observed it looks cleaner and readable.

let’s take one more example (i want to explain more even if you are bored).

checkout this service interface.

 public Interface Employee {
Employee createEmployee();
Employee getEmployee();
Manager getEmployeeManager();
String getEmployeeManagerName();
Department getEmployeeDepartment();
void assignEmployeeDepartment();
}

Anything wrong in this huh think think think think ?

Salute to Captain America

Employee Interface is doing a hell lot of things it is managing employees, managers, and departments. Think about the implementation class when it is managing all those unrelated purposes. it creates a lot of dependency within code hence it is not extendible, or manageable.

Simply think Employee, manager, and department all are an independent entity. why do they need to depend on each other? they should not directly access each other without abstraction.

it is clearly violating the single responsibility principle which everyone is meant for doing one and only one thing.

let’s rework it a little bit. let's break the single service into many services. (divide and conquer)

public Interface Employee {
Employee createEmployee();
Employee getEmployee();
}

public Interface Manager {
Employee getEmployeeManager();
String getEmployeeManagerName();
}

public Interface Department {
Department getEmployeeDepartment();
void assignEmployeeDepartment();
}

Now we have a separation of concern each thing meant for doing one thing. Department code change won’t affect the employee or manager (independent of each other). all depend on each other through abstraction and totally unaware of what is happening behind the curtain?

I have given a very simple example that is easy to understand but the principle is relevant from the method, class up to service, component level. Take it as a rule that applied even to smaller blocks as well as bigger blocks (made up of smaller blocks). so every method, class, service interface, component, etc should always try to do only and only one thing. remove dependency as much as you can to make it maintainable and extendible.

it is just a blog with a simple theory and example to truly implement it. you need to think a lot when you are writing code. it needs years of practice to write very good clean code but we can start trying now(there is no code that is perfect but it can be improved). the rule is simple don’t violate any principle and the result code will be clean.

Open-Closed Principle

As the name suggests the software entity(method, class, module anything) should be open for extension but closed for modification.

Open closed is an important principle as it makes code extendible without changing existing code. the purpose is simply to add a lot of new features(code) without changing the existing code. Do you like to rewrite the code? once written the code we should not worry about changing later in the future when new code was written on top of that.

Let’s understand a simple example. you have the requirement to make a rectangle, circle, and calculation feature for the area.

public class Rectangle {
Double length;
Double breadth;
}

public class Circle {
Double radious;
}

public class Area {

Double calculateArea(Object shape) {

Double result = 0.0;

//check class of object
if(shape.getClass() == Rectangle.class) {
Rectangle rect = (Rectangle)shape;
result = rect.length * rect.breadth;
} else if(shape.getClass() == Circle.class) {
Circle circle = (Circle)shape;
result = Math.pi * Math.pow(circle.radious, 2);
}

return result;
}
}

We have a Rectangle class with property length & breadth and a circle with radius Then we have an area class that calculates the area of the shape by checking its class.

Many questions are popping in mind like What is wrong with the implementation? do I not need to change again in the future if a new requirement came? is it violates the Open-Closed principle? let’s check it.

Later if we want to add more shapes like triangle, square, hexagon, etc. what I need to do then? first I will create a new class with properties(triangle, square ……) then will add the calculation logic to the existing method of calculateArea it means the modification of existing code. so it violates the Open-Closed Principle (means we fail :( ). we should extend the code without touching it.

Let’s rewrite the code to make it extendible(think how you can improve it ? ).
if we have observed it we know rectangle, square are all shapes (let’s use inheritance), and later also in the future, we will add more shapes( still that will be a shape only). also, we need to calculate the area of shape so the area is common property in all of them. let’s redesign it.

public Interface Shape {
void area();
}

public class Rectangle implements Shape {
Double length;
Double breadth;

Double area() {
return length * breadth;
}
}

public class Circle implements Shape {
Double radious;

Double area() {
return result = Math.pi * Math.pow(circle.radious, 2); // pi * r2
}
}

public class Area {

// passing parent class polymorphism
Double calculateArea(Shape shape) {
return shape.area();
}
}

Now we created an interface and defined the common property of all shape areas. then we are creating a separate class of different shapes and implementing the method to calculate area. Then we are using polymorphism to pass shape object and calling area method.

Read it 10 times and think is it extendible? can I add new shapes without changing the existing code?

if we have to add a triangle we just need to make a new class, implement parent interface Shape, and bingo it is done. no need to touch the existing code so means it becomes open for extension and closed for modification.

Suppose you are creating a house you created ground floor so the first floor only extends the ground floor it doesn’t affect it right ? all floor is extendible you can add more without affecting other. think how much costly it can be when the top floor affects the ground floor? (fixing cost of that) software is also the same don’t be a bad architecture.

Liskov Substitution Principle

Liskov Principle (Source Wikipedia)
if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, a task performed, etc.).

it is hard to understand this statement let’s put it in a simple definition.
All Parent class objects should be replaceable from any Subclass Objects without affecting the program.

Holi Moli did you understood the statement? it’s simple every parent class object in the program should be replaceable with any of its own subclass object without affecting the program(didn’t break anything working same).

why the hell this weird rule exist? it simply enforced the same behavior in a child as of parent. means you have the exact same behavior as your father. if someone replaces you with your father nobody will notice it.

Remember it is not the same object (inheritance hope you know about it). the subclass has its feature also. so basically subclass has all the features of the parent class(that’s why it is replaceable) and also its own feature.
now if you think about it you might argue. hey dude! I am using inheritance means automatically subclass is extending the whole object so why need principle? (one minute pause)
When subclass extending parent class it automatically adds all the method and properties. but it can also override(public method) so the principle is to enforce that the behavior of a child should same as a parent.

let’s try to understand with the example we can say every square is a rectangle?

class Rectangle {
Double length;
Double breadth;

void setSide(Double length, Double breadth) {
this.length = length;
this.breadth = breadth;
}
Double getArea() {
return this.length * this.breadth;
}
}
//Every Square is Rectangle
class Square extends Rectangle{

@Override
void getArea(Double length, Double breadth) {

if(length != breadth) {
throw new Exception(“Not a Square”);
}

this.length = length;
this.breadth = length;
}
}
class RectangleTest {

void calculateAreaTest(Rectangle r) {

r.setSide(2.0, 3.0); // exception will throw "Not a Square"
assert(r.getArea(), 6.0); // here also will fail for square
}
}
public class Main {
public static void main(String[] args) {
Square square = new Square();
RectangleTest test = new RectangleTest();
test.calculateAreaTest(square);
}
}

Check the above code will it fail or run? We have created a rectangle class with length, breadth, and the method to calculate area. we know every square is a rectangle so override the existing method of setSide to enforce the side must be equal.
Did it violate the Liskov principle (is Liskov happy )? Liskov said all parents must be substitutable from the child without affecting the program. we have a RectangleTest class that tests the area of the rectangle. But when we replace/pass the square it fails. means it affects the code when we substitute child so clearly it violates the Liskov principle.
We can say since it violates the Liskov principle so we should not do inheritance between rectangle and square because the behavior of rectangle is not replaceable with square.

Let’s take another example.

Interface Animal {
void eat();
void sleep();
void bark();
void meow();
void play();
}
Interface Dog extends Animal{
void bringNewspaper();
}
Interface Cat extends Animal {
void doAllUselessThing(); //cat is stupid i don’t like
}

Check the code it violates the Liskov principle? can we replace all animal object with dog or cat? do it enforce the same behavior? simply no, because dogs don’t meowwww and cats don’t bark (I haven’t seen). surely it will fail when the dog tries to become a cat.

now let’s rework it.

Interface Animal {
void eat();
void sleep();
void play();
}
Interface Dog extends Animal{
void bark();
void bringNewspaper();
}
Interface Cat extends Animal {
void meow();
void doAllUselessThing(); //cat is stupid i don’t like
}

We can say eat, sleep & play is a common behavior and all animal on the planet follow it. so it makes sense to enforce these behaviors on children. Dogs and cats also have their own feature. so when we replace the animal objects with dog or cat in code it doesn’t violate the principle since basic behavior is the same in all animals (eat, sleep or play) means it will not affect the code.

Interface Segregation Principle

The client should not depend on the feature of the interface that they don’t use.

Think when there is summer season and you go to an ice cream shop. you ordered your favorite vanilla ice cream (yummy) then the shopkeeper asked your name, mother name, father name then he told you to do 10 push-ups. you asked why the hell required these detail and 10 push-ups? I need damn simple ice cream.
He replied sir it is part of our service and mandatory for the client to do it.

So the simple idea is the client should only depend on the feature that it uses. Customers only want to order ice cream not interested in doing push-ups or flip.

What is the interface? it is a 100 percent abstraction of implementation detail so outside nobody knew what is behind the curtain (do you know)?

if you remember the single responsibility principle which states that everything is meant to do only one thing and it should do it well. interface segregation principle is also similar it enforced that interface should not be big means doing many things.

What is the problem with the big interface(big is better see hulk)? think when many classes are implementing the same interface. so classes should not be forced to implement the method that they don’t need. classes should only implement the method that they want to use. when checking a class you should not think about why this method is here?

let’s take a simple example.

Interface BankService {
void openAccount();
void checkAccountBalance();
void createLoan();
void payEmi();
}
class AccountService implements BankService {

@Override
void openAccount() {
// logic to open account
}

@Override
void checkAccountBalance() {
// logic to check account balance
}

@Override
void createLoan() {
throw new Exception(“Unsupported Operation”);
}

@Override
void payEmi() {
throw new Exception(“Unsupported Operation”);
}
}
class LoanService implements BankService {

@Override
void openAccount() {
throw new Exception(“Unsupported Operation”);
}

@Override
void checkAccountBalance() {
throw new Exception(“Unsupported Operation”);
}

@Override
void createLoan() {
// logic to create loan
}

@Override
void payEmi() {
// logic to pay emi
}

}

we have a BankService interface that provides a feature for account & loan management. we have two implementation classes for loan and account.
so when they are implementing an interface they are forced to define the definition of the method. Loan Service doesn’t provide a loan, vice versa. so clearly we need to do that we don’t want to do. it is a violation of the principle that simply states implementation classes should not be forced to define methods they don't need.
To fix the above code we need two separate interfaces for account & loan because the implementation class serves a different purpose.

Let’s take another example

Inteface ServiceABCD {

void A();
void B();
void C();
void D();
}
class OperationA implements ServiceABCD {

@Override
void A() {
// perform operation A
}

@Override
void B() {
throw new Exception(“Unsupported Operation”);
}

@Override
void C() {
throw new Exception(“Unsupported Operation”);
}

@Override
void D() {
throw new Exception(“Unsupported Operation”);
}
}
class OperationCD implements ServiceABCD {

@Override
void A() {
throw new Exception(“Unsupported Operation”);
}

@Override
void B() {
throw new Exception(“Unsupported Operation”);
}

@Override
void C() {
// perform operation C
}

@Override
void D() {
// perform operation D
}

}
class OperationABCD implements ServiceABCD {

@Override
void A() {
// perform operation A
}

@Override
void B() {
// perform operation B
}

@Override
void C() {
// perform operation C
}

@Override
void D() {
// perform operation D
}
}

code is simple there is Interface ServiceABCD. OperationA class performs only operation A. OperationCD class perform operation C & D. OperationABCD class perform A, B, C & D.

clearly, it is a violation of principle. we are throwing Exception(“Unsupported Operation”) for the operation that is not supported. but the question is can you fix the code?

Let’s rewrite it again

Interface ServiceA {
void A();
}
Interface ServiceB {
void B();
}
Interface ServiceCD {
void C();
void D();
}
class OperationA implements ServiceA{

@Override
void A() {
// perform operation A
}

}
class OperationCD implements ServiceCD {

@Override
void C() {
// perform operation C
}

@Override
void D() {
// perform operation D
}

}
class OperationABCD implements ServiceA, ServiceB, ServiceCD {

@Override
void A() {
// perform operation A
}

@Override
void B() {
// perform operation B
}

@Override
void C() {
// perform operation C
}

@Override
void D() {
// perform operation D
}
}

now we broke the bigger interface into the smaller interface. so implementation classes only need to define what they meant for.

it helps to remove unwanted dependency that is not needed hence low maintenance. it improved readability a lot. by the only first look, you can know what purpose the interface is serving. we should avoid providing a big picture on a single screen. we should divide it into more small pictures that will easy to manage.

Divide and Conquer: this trick is classic, works everywhere. you already know how the British came to India and ruled us by divide and conquer(know from childhood story). they made our elders fool hahaha(don’t be serious).
let’s apply it to software development. we have a lot of feature sets. so instead of making single services doing many tasks (multitasking). we divide the feature into many small features. we can do it in the team without affecting each other work since all services are independent. it makes code manageable as well as readable. if something fails we know where to look since we already broke the whole picture into a much smaller picture. that makes code very easy to maintain.

You can rule the kingdom of software by dividing them into a small manageable independent component.

Dependency Inversion Principle

The principle states that all code dependency in the project should only be based on abstraction then concrete implementation.

Let’s take a simple example electric socket in the house. the device is always independent of the electric socket means it can charge the mobile, laptop, connect tv, washing machine any electric device. we don’t have a socket in our house that only meant for one specific device. charging socket and devices are dependent on abstraction. As long as any device provides the implementation detail it will work. how simple is that to understand by real-world example?

we know abstraction means hiding implementation detail, concrete means when we show implementation detail.

The principle states that we should only use abstract classes or interfaces for dependency, not a concrete implementation. the reason is simple if the class has a dependency on another concrete implementation of the class. then code change in concreate class will affect the class that is referring it aren’t you agree?

another reason is when we are referring to one module in another. if we change the module code we don’t want other modules to recompiled dependency again. if one module is changed why the hell other dependent modules will recompile? it happens when you depend on the concrete implementation. using interfaces smartly will give the advantage to add code in class without affecting other modules. means you can add a lot of code without other modules even knowing about it.

Note:

achieving 100% abstraction dependency not possible ex. String class is the concrete implementation so we depend on many concrete implementations. but these classes are very stable they don’t get change easily. depending on concrete implementation is a problem when things are changing frequently.
if the concrete stable implementation that not going to change for a long time. obviously, we can use them since the whole purpose is that changing code should not affect.

abstraction should not depend on details. details should depend on abstraction.

it is good if you are already understood the statement. just referring to abstract implementation is not enough. our abstraction should not depend on details means whenever implementation details change should not affect abstraction. then the whole point of principle is gone if it affects.
Abstraction detail should never change. first, we define abstraction then we do the implementation class. obviously, it makes sense abstraction should not depend on the concrete implementation. other modules using the abstraction should assume that it is not going to change(will not going to affect with change).
we need to design code smartly so that we can use abstraction for referring and also the underlying change of concreate class is independent of abstraction. (how cool is that? ) if you do the change without affecting other nobody will get mad at you.

Now let’s learn from the electric socket of your house. electric socket is independent of devices means you can use charging mobile, refrigerator, washing machine, etc.

think electric socket as another module and devices are concrete implementations. How can we do that in software? the ability to inject different concrete implementations. this is only possible with abstraction means we can inject different classes implementing the same abstraction.

Suppose there is an animal interface and we have many classes Dog, Cat, Lion, etc implementing the animal interface. now we can inject any class from abstraction. you want a dog then inject it or like a cat take it or want a lion why not? depending on abstraction gives us so many advantages.

Enough theory let’s try the example.

class LightBlub {

void on {
System.out.println(“Blub is on”);
}

void off {
System.out.println(“Blub is off”);
}
}
class ElectricSocket {

Blub blub = new Blub(); // depend on blub

void switchOn() {
blub.on();
}

void switchoff() {
blub.off();
}

}

what is the problem with the above code? the first problem is ElectricSocket depends on the concrete implementation of LightBlub. so LightBlub method change will affect the ElectricSocket(if add the parameter in method).
the second problem is ElectricSocket can only perform the operation on LightBlub what if I need the fan, refrigerator, or washing machine? then I need to again reference the object. in technical terms, it is tightly coupled we need to make it loosely coupled.

Now let’s refactor the code.

Interface ElectricDevice {
void on();
void off();
}
class ElectricBlub implements ElectricDevice{

void on {
System.out.println(“Blub is on”);
}

void off {
System.out.println(“Blub is off”);
}
}
class Fan implements ElectricDevice{

void on {
System.out.println(“Fan is on”);
}

void off {
System.out.println(“Fan is off”);
}
}
class ElectricSocket {

ElectricDevice device;

ElectricSocket(ElectricDevice device) {
this.device = device; // lossely coupled pass any subclass
}

void switchOn() {
device.on();
}

void switchoff() {
device.off();
}
}

Now check loosely coupled code. now we depend on the abstract interface ElectricDevice that is implemented by any device like fan, electric blub, washing machine, etc. we can inject any device class through constructor during run time that makes it loosely coupled. so it is independent of concrete implementation by using abstraction and also loosely coupled to make it extendible. (wow)

Final Note:

Time to end the blog(felt I have written enough). the beauty of the SOLID principle is it applies from small class to the whole project level. you can be a newbie software developer that manages class level code or senior developer or architecture of the system. doesn’t matter what position you are in? as long as you are designing code on any level SOLID principles still apply.

I am not saying that senior-level position design is also simple as junior level(no dumbo). my point is it follows the same fundamental. law of gravity is applied everywhere. somebody can build a space rocket or firecracker rocket. but still, gravity applied to both cases.
at the junior level, since you are only looking at few classes (small code base) it is good to practice these principles. did my written code follow best practice? what I am doing wrong? it is ok to make mistake in that position. it doesn't matter if you experiment with something. you already have the senior people to review it.
This principle is not learned overnight nobody can know football by reading theory about it. it needs daily practice. at the higher position (senior developer or something higher not talking about god) nobody guides you(who is the reviewer of god work?). so if you haven’t followed the best coding practices from starting then that senior guy is devil.
The newbie can do a lot of mistakes in designing code as it not involve much cost or effort (time is money). but if some seniors follow poor design OH MY GOD! who is going to tell they are doing wrong? imagine the cost of doing a mistake on a higher level?

Thank You,
Naman Sisodia (namansisodia619@gmail.com)

--

--

No responses yet