0% found this document useful (0 votes)
44 views

SOLID Architectural Pattern With Real Time Example: Single Responsibility Principle (SRP)

The document discusses real-world examples of the SOLID principles of object-oriented design, which are Single Responsibility Principle, Open-Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle. It provides examples for each principle, such as creating separate classes for validation and updating of XML to adhere to the Single Responsibility Principle, and using inheritance and polymorphism correctly to follow the Liskov Substitution Principle. The examples help illustrate how to apply these fundamental software design concepts in practical coding situations.

Uploaded by

Khaleek Ahmad
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
44 views

SOLID Architectural Pattern With Real Time Example: Single Responsibility Principle (SRP)

The document discusses real-world examples of the SOLID principles of object-oriented design, which are Single Responsibility Principle, Open-Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle. It provides examples for each principle, such as creating separate classes for validation and updating of XML to adhere to the Single Responsibility Principle, and using inheritance and polymorphism correctly to follow the Liskov Substitution Principle. The examples help illustrate how to apply these fundamental software design concepts in practical coding situations.

Uploaded by

Khaleek Ahmad
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 16

SOLID Architectural pattern with real time example

In interview interviewer often asked real time example of SOLID design pattern. So I decide to
write some real time example of SOLID design pattern.

What is SOLID?
SOLID is an acronym for five principle of architecture.

S – Single Responsibility Principle


O – Open Close Principle
L – Liskov Substitution Principle
I –Interface Segregation Principle
D – Dependency Injection Principle

Single Responsibility Principle (SRP)–


It says that every class should have single responsibility. A class should not have more than one
reason to change.

Example - Suppose you have created a class XmlValidator for xml validation which has the
responsibility to validate the xml.

If there is a need to update the xml than a separate class should be created for the same.
XmlValidator class should not be use for updating the xml.

public class XmlValidator


{

public void Validate()


{

public void DoUpdate()


{
}

For updating a new class should be created

public class XmlUpdate


{

public void DoUpdate()


{

Open Close Principle (OCP)–


A class should be open for extension and closed for modification.

Example – Suppose we class name Customer which has a property InvoiceNumber which has
integer type

public class Customer


{
public int InvoiceNumber
{
get;
set;

}
}

And in future requirement has been changed now InvoiceNumber should be alphanumeric
rather than only integer. So in this case you should create a subclass CustomerNew with a same
property but different datatype rather than modifying previous one.

public class CustomerNew : Customer


{

public new String InvoiceNumber


{
get;
set;

}
}
Liskov Substitution Principle (LSP)–
A parent object should be able to replace its child during runtime polymorphism.

Example - Suppose you have two classes Cooler and Fan, both are inherited from a common
interface named ISwitch. Which has three methods On, Off and Regulate.

public interface ISwitch


{
void On();
void Off();
}

public class Cooler : ISwitch


{
public void On()
{

}
public void Off()
{

public void Regulate()


{

public class Fan : ISwitch


{
public void On()
{

public void Off()


{

}
public void Regulate()
{

public class MainClass


{
public void AddObject()
{
List<ISwitch> Switch = new List<ISwitch>();
Switch.Add(new Cooler());
Switch.Add(new Fan());

foreach (var o in Switch)


{
o.Regulate();
}
}
}

Everything was fine until a new class introduced for same interface named Bulb which have
only two methods On and Off. It does not have Regulate method. So Bulb class is

public class Bulb : ISwitch


{
public void On()
{

public void Off()


{

}
public void Regulate()
{
throw new NotImplementedException();
}

Now AddObject method will be updated like this

public void AddObject()


{
List<ISwitch> Switch = new List<ISwitch>();
Switch.Add(new Cooler());
Switch.Add(new Fan());
Switch.Add(new Bulb());

foreach (var o in Switch)


{
o.Regulate();
}
}

In this case Regulate method will throw an error.

One horrible solution to this problem is, put an if condition

foreach (var o in Switch)


{

if(o is Bulb)

continue;

o.Regulate();
}

This is an example of bad design, if above condition used somewhere it clearly means
that there is violation of LSK principle.

Interface Segregation Principle (ISP)–


Client specific interfaces are better than one general purpose interfaces.

Suppose we have one interface for click

public interface IClick{

void onClick(Object obj);


}

As time passes new requirement comes for adding one more function onLongClick. And you
add this method in already created interface

public interface IClick{

void onClick(Object obj);

void onLongClick(Object obj);


}

After some time one new requirement comes for adding function for touch also and you add
the method in same interface

public interface IClick{


void onClick(Object obj);

void onLongClick(Object obj);

void onTouch(Object obj);


}

At this point you decide to change the name of interface too because touch if different than
click.

And this way this interface becoming a problem—generic and polluted. So at this stage ISP
comes in light.

WHY GENERIC INTERFACE CREATE PROBLEM?


Suppose some clients need only onClick function and some need only onTouch function than
one will be useless for both. So ISP gives the solution split the interface into two interfaces

ITouch and IClick. The client which has required onClick, it can implement IClick and which need
onTouch it can implement ITouch and which need both, it can implement both.

public interface IClick{

void onClick(Object obj);

void onLongClick(Object obj);


}

public interface ITouch{

void onClick(Object obj);

void onTouch(Object obj);


}
Dependency Injection Principle (ISP) –
It states two points first point is higher level module should not depend on low level module.
Both should depend on abstraction. And second point is Abstraction should not depend detail.

Detail should depend on abstraction.

In other words no object should be created inside a class they should be passed or injected from
outside. And where it would receive it will be an interface rather class.

Here is a bad design without using dependency injection.

class Student
{
// Handle to EventLog writer to write to the logs
LogWriter writer = null;
// This function will be called when the student has problem
public void Notify(string message)
{
if (writer == null)
{
writer = new LogWriter();
}
writer.Write(message);
}
}

And this one is also a bad design

class Student
{
// Handle to EventLog writer to write to the logs
LogWriter writer = null;
Public Student(LogWriter writer)
{

This.writer = writer;
}
// This function will be called when the student has problem
public void Notify(string message)
{
writer.Write(message);
}
}
Above written classes are bad designs because in case of in change in LogWrite, you have to
disturb Student class. We should use interface inside Student instead of class.

With dependency injection


class Student
{
// Handle to EventLog writer to write to the logs
ILogWriter writer = null;
Public Student(ILogWriter writer)
{

This.writer = writer;
}
// This function will be called when the student has problem
public void Notify(string message)
{
writer.Write(message);
}
}

//////////////////////////////////////////////////////////////////////////////
<h2>SOLID Architectural</h2>

<p>In interview interviewer often asked real time example of SOLID design pattern. So I
decided to write some real time example of SOLID design pattern.</p>

<h2>What is SOLID?</h2>

<p>SOLID is an acronym for five principle of architecture.</p>

<h3>S&ndash; Single Responsibility Principle</h3>

<h3>O&ndash; Open Close Principle</h3>

<h3>L&ndash; Liskov Substitution Principle</h3>

<h3>I&ndash;Interface Segregation Principle</h3>

<h3>D&ndash; Dependency Inversion Principle</h3>

<h3>Single Responsibility Principle (SRP)&ndash;</h3>

<p>It says that every class should have single responsibility. A class should not have more than
one reason to change. Example - Suppose you have created a class XmlValidator for xml
validation which has the responsibility to validate the xml. If there is a need to update the xml
than a separate class should be created for the same. XmlValidator class should not be use for
updating the xml.</p>

<p>Wrong design</p>

<p>Here XML validator class used for both validating and updating which is wrong.</p>

<pre lang="C++">
public class XmlValidator
{

public void Validate()


{
//Code
}

public void DoUpdate()


{
//Code
}
}

</pre>

<p>For updating, a new class should be created. Here is the right design.</p>

<pre lang="C++">
public class XmlValidator
{
public void Validate()
{
//Code
}
}

public class XmlUpdate


{
public void DoUpdate()
{
//Code
}
}

</pre>
<h2>Open Close Principle (OCP)&ndash;</h2>

<p>Example &ndash; Suppose we have class name Customer which has a property
InvoiceNumber which has integer type.</p>

<pre lang="C++">
public class Customer
{
public int InvoiceNumber
{
get;
set;
}
}
</pre>

<p>And in future requirement has been changed now InvoiceNumber should be alphanumeric
rather than only integer. So in this case you should create a subclass CustomerNew with same
property but different datatype rather than modifying previous one.</p>

<pre lang="C++">
public class CustomerNew : Customer
{
public new String InvoiceNumber
{
get;
set;
}
}
</pre>

<h2>Liskov Substitution Principle (LSP)&ndash;</h2>

<p>A parent object should be able to replace its child during runtime polymorphism. Example -
Suppose you have two classes Cooler and Fan, both are inherited from a common interface
named ISwitch. Which has three methods On, Off and Regulate.</p>

<pre lang="C++">
public interface ISwitch
{
void On();
void Off();
void Regulate();
}

public class Cooler : ISwitch


{
public void On()
{

}
public void Off()
{

public void Regulate()


{

}
}

public class Fan : ISwitch


{
public void On()
{

public void Off()


{

}
public void Regulate()
{

public class MainClass


{
public void AddObject()
{
List&lt;ISwitch&gt; Switch = new List&lt;ISwitch&gt;();
Switch.Add(new Cooler());
Switch.Add(new Fan());

foreach (var o in Switch)


{
o.Regulate();
}
}
}

</pre>

<p>Everything was fine until a new class introduced for same interface named Bulb which have
only two methods On and Off. It does not have Regulate method. So Bulb class is</p>

<pre lang="C++">

public class Bulb : ISwitch


{
public void On()
{

public void Off()


{

}
public void Regulate()
{
throw new NotImplementedException();
}

</pre>

<p>Now AddObject method will be updated like this</p>

<pre lang="C++">
public void AddObject()
{
List&lt;ISwitch&gt; Switch = new List&lt;ISwitch&gt;();
Switch.Add(new Cooler());
Switch.Add(new Fan());
Switch.Add(new Bulb());

foreach (var o in Switch)


{
o.Regulate();
}
}
</pre>

<p>In this case Regulate method will throw an error.One horrible solution to this problem is, put
an if condition.</p>

<pre lang="C++">
foreach (var o in Switch)
{

if(o is Bulb)
continue;

o.Regulate();
}
</pre>

<p>This is an example of bad design, if above condition used somewhere it clearly means that
there is violation of LSK principle.</p>

<h2>Interface Segregation Principle (ISP)&ndash;</h2>

<p>Client specific interfaces are better than one general purpose interfaces. Suppose we have
one interface for click</p>

<pre lang="C++">

public interface IClick


{
void onClick(Object obj);
}
</pre>

<p>As time passes new requirement comes for adding one more function onLongClick. And you
add this method in already created interface.</p>

<pre lang="C++">

public interface IClick


{
void onClick(Object obj);
void onLongClick(Object obj);
}
</pre>

<p>After some time one new requirement comes for adding function for touch also and you add
the method in same interface.</p>
<pre lang="C++">

public interface IClick


{
void onClick(Object obj);
void onLongClick(Object obj);
void onTouch(Object obj);
}
</pre>

<p>At this point you decide to change the name of interface too because touch is different than
click. And this way this interface becoming a problem, generic and polluted. So at this stage ISP
comes in light.</p>

<h2>WHY GENERIC INTERFACE CREATE PROBLEM?</h2>

<p>Suppose some clients need only onClick function and some need only onTouch function than
one will be useless for both. So ISP gives the solution, split the interface into two interfaces
ITouch and IClick. The client which has required onClick, it can implement IClick and which
need onTouch it can implement ITouch and which need both, it can implement both.</p>

<pre lang="C++">
public interface IClick
{
void onClick(Object obj);
void onLongClick(Object obj);
}

public interface ITouch


{
void onClick(Object obj);
void onTouch(Object obj);
}

</pre>

<h2>Dependency Inversion Principle (ISP)-</h2>

<p>It states two points, first point is higher level module should not depend on low level
module. Both should depend on abstraction. And second point is, Abstraction should not depend
detail. Detail should depend on abstraction. In other words no object should be created inside a
class, they should be passed or injected from outside. And where it would receive it will be an
interface rather class. Here is a bad design without using dependency injection.</p>

<pre lang="C++">
class Student
{
// Handle to EventLog writer to write to the logs
LogWriter writer = null;
// This function will be called when the student has problem
public void Notify(string message)
{
if (writer == null)
{
writer = new LogWriter();
}
writer.Write(message);
}
}

</pre>

<p>And this one is also a bad design</p>

<pre lang="C++">
class Student
{
// Handle to EventLog writer to write to the logs
LogWriter writer = null;
Public Student(LogWriter writer)
{

This.writer = writer;
}
// This function will be called when the student has problem
public void Notify(string message)
{
writer.Write(message);
}
}
</pre>

<p>Above written classes are bad designs because in case of any change in LogWrite, you have
to disturb Student class. We should use an interface inside Student instead of class. With
dependency injection.</p>

<pre lang="C++">
class Student
{
// Handle to EventLog writer to write to the logs
ILogWriter writer = null;
Public Student(ILogWriter writer)
{
this.writer = writer;
}
// This function will be called when the student has problem
public void Notify(string message)
{
writer.Write(message);
}
}
</pre>

<p>It should be called like this.</p>

<pre lang="C++">
class Main
{
Private void AddStudent
{

ILogWriter writer = null;

write = new LogWrite();


Student obj = new Student(write);
write.Notify(&quot;&quot;message&quot;);
}

}
</pre>

You might also like