SOLID expansion below
S: Single Responsibility Principle (SRP)
O: Open closed Principle (OCP)
L: Liskov substitution Principle (LSP)
I: Interface Segregation Principle (ISP)
D: Dependency Inversion Principle (DIP)
Single Responsibility Principle (SRP):
SRP suggest only one prime functionality exist in single class & functions.
Single class should not include multiple functions and properties which is not related or common reusable.
This means that every class, or similar structure, in your code should have only one job to do.
Everything in that class should be related to a single purpose.
Example :
Phone number validation, Email validation.
Sometimes we will include these validations within same class like User.class itself but SRP suggest we need to split and create separate class like EmailValidation.class & PhoneNoValidation.class
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
///Not In SRP
//public static bool IsValidEmail(string email)
//{
// if (email == null || !email.Contains("@"))
// {
// return false;
// }
// return true;
//}
}
above class IsValidEmail function is not related user it can be use any email validation so need to create separate one.
public class EmailValidationService
{
public static bool IsValidEmail(string email)
{
/// sample validation just for understanding SRP
if (email == null || !email.Contains("@"))
{
return false;
}
return true;
}
}
O: Open/Closed Principle
Best Example ;
1. we need to find interest calculation but each and every bank have their own calculations but outcome result is interest so should not use if ....else conditions for each new banks trying to implement.
2.Find area of the object
Not in open/closed Principle
public class Rectangle{
public double Height {get;set;}
public double Wight {get;set; }
}
public class Circle{
public double Radius {get;set;}
}
public class AreaCalculator
{
public double TotalArea(object[] arrObjects)
{
double area = 0;
Rectangle objRectangle;
Circle objCircle;
foreach(var obj in arrObjects)
{
if(obj is Rectangle)
{
area += obj.Height * obj.Width;
}
else
{
objCircle = (Circle)obj;
area += objCircle.Radius * objCircle.Radius * Math.PI;
}
}
return area;
}
}
Open/ Closed Principle
public abstract class Shape
{
public abstract double Area();
}
public class Rectangle: Shape
{
public double Height {get;set;}
public double Width {get;set;}
public override double Area()
{
Console.WriteLine ("Rectangle");
return Height * Width;
}
}
public class Circle: Shape
{
public double Radius {get;set;}
public override double Area()
{
Console.WriteLine ("Circle");
return Radius * Radus * Math.PI;
}
}
/// Main class based on object it will call corresponding class method.
public class AreaCalculator
{
public double TotalArea(Shape[] arrShapes)
{
double area=0;
foreach(var objShape in arrShapes)
{
var shapearea = objShape.Area();
area+=shapearea;
Console.WriteLine (shapearea);
}
return area;
}
}
L: Liskov Substitution Principle
The Liskov Substitution Principle (LSP) states that "you should be able to use any derived class instead of a parent class and have it behave in the same manner without modification". It ensures that a derived class does not affect the behavior of the parent class, in other words,, that a derived class must be substitutable for its base class. This principle is just an extension of the Open Closed Principle and it means that we must ensure that new derived classes extend the base classes without changing their behavior.
public class SqlFileManager
{
public List<SqlFile? lstSqlFiles {get;set}
public string GetTextFromFiles()
{
StringBuilder objStrBuilder = new StringBuilder();
foreach(var objFile in lstSqlFiles)
{
objStrBuilder.Append(objFile.LoadText());
}
return objStrBuilder.ToString();
}
public void SaveTextIntoFiles()
{
foreach(var objFile in lstSqlFiles)
{
//Check whether the current file object is read-only or not.If yes, skip calling it's
// SaveText() method to skip the exception.
if(! objFile is ReadOnlySqlFile)
objFile.SaveText();
}
}
}
Here we altered the SaveTextIntoFiles() method in the SqlFileManager class to determine whether or not the instance is of ReadOnlySqlFile to avoid the exception. We can't use this ReadOnlySqlFile class as a substitute for its parent without altering SqlFileManager code. So we can say that this design is not following LSP. Let's make this design follow the LSP. Here we will introduce interfaces to make the SqlFileManager class independent from the rest of the blocks.
public interface IReadableSqlFile
{
string LoadText();
}
public interface IWritableSqlFile
{
void SaveText();
}
public class ReadOnlySqlFile: IReadableSqlFile
{
public string FilePath {get;set;}
public string FileText {get;set;}
public string LoadText()
{
/* Code to read text from sql file */
}
}
public class SqlFile: IWritableSqlFile,IReadableSqlFile
{
public string FilePath {get;set;}
public string FileText {get;set;}
public string LoadText()
{
/* Code to read text from sql file */
}
public void SaveText()
{
/* Code to save text into sql file */
}
}
public class SqlFileManager
{
public string GetTextFromFiles(List<IReadableSqlFile> aLstReadableFiles)
{
StringBuilder objStrBuilder = new StringBuilder();
foreach(var objFile in aLstReadableFiles)
{
objStrBuilder.Append(objFile.LoadText());
}
return objStrBuilder.ToString();
}
public void SaveTextIntoFiles(List<IWritableSqlFile> aLstWritableFiles)
{
foreach(var objFile in aLstWritableFiles)
{
objFile.SaveText();
}
}
}
I: Interface Segregation Principle (ISP)
Example:
public Interface IScrum
{
void CreateSubTask();
void AssginTask();
void WorkOnTask();
}
public class TeamLead : IScrum
{
public void AssignTask()
{
//Code to assign a task.
}
public void CreateSubTask()
{
//Code to create a sub task
}
public void WorkOnTask()
{
//Code to implement perform assigned task.
}
}
public class Manager: IScrum
{
public void AssignTask()
{
//Code to assign a task.
}
public void CreateSubTask()
{
//Code to create a sub task.
}
public void WorkOnTask()
{
//// here forced to implement so not in ISP
throw new Exception("Manager can't work on Task");
}
}
Avoid to throw exception we split the interface
public interface IProgrammer
{
void WorkOnTask();
}
public interface ILead
{
void AssignTask();
void CreateSubTask();
}
public class Programmer: IProgrammer
{
public void WorkOnTask()
{
//code to implement to work on the Task.
}
}
public class Manager: ILead
{
public void AssignTask()
{
//Code to assign a Task
}
public void CreateSubTask()
{
//Code to create a sub taks from a task.
}
}
public class TeamLead: IProgrammer, ILead
{
public void AssignTask()
{
//Code to assign a Task
}
public void CreateSubTask()
{
//Code to create a sub task from a task.
}
public void WorkOnTask()
{
//code to implement to work on the Task.
}
}
D: Dependency Inversion Principle
Example: Exception log, error.log(exception ex) should be single function but within that based on cofiguration we alter storage place.
Not in DIP
public class DataExporter
{
public void ExportDataFromFile()
{
try {
//code to export data from files to database.
}
catch(IOException ex)
{
new ExceptionLogger().LogIntoDataBase(ex);
}
catch(Exception ex)
{
new ExceptionLogger().LogIntoFile(ex);
}
}
}
DIP:
public class DataExporter
{
public void ExportDataFromFile()
{
try {
//code to export data from files to database.
}
catch(Exception ex)
{
new ExceptionLogger().LogException(ex);
}
}
}
Public interface ILogger
{
void LogException(Exception exception);
}
public class FileLog : ILogger
{
public void LogException()
{
//defalut logger
///logic for Write exception into files
}
}
public class ExceptionLogger
{
private ILogger _logger;
public ExceptionLogger()
{
//defalut logger
this._logger = new FileLog();
}
public ExceptionLogger(ILogger aLogger)
{
this._logger = aLogger;
}
public void LogException(Exception exception)
{
//based on implemeted logger it will log, Ex: log exception in DB,File,Mail etc
this._logger.LogMessage(exception);
}
}
No comments:
Post a Comment