SOLID & IoC Principles
- 2. What is SOLID?
The first five principles of OOP design.
A set of rules on how to build an awesome application that
is flexible and easy to use, understand and maintain.
- 4. SINGLE RESPONSIBILITY PRINCIPLE
// Data access class is only responsible for data base
related operations
class DataAccess
{
public static void InsertData()
{
Console.WriteLine("Data inserted into database
successfully");
}
}
// Data access class is only responsible for data base
related operations
class DataAccess
{
public static void InsertData()
{
Logger.WriteLog("Data inserted into database
successfully");
}
}
// Logger class is only responsible for logging
related operations
class Logger
{
public static void WriteLog(string messsage)
{
Console.WriteLine("Logged Time:" +
DateTime.Now + messsage);
}
}
- 6. OPEN/CLOSED PRINCIPLE
// Here DataProvider is open for extension (extends to
Sql, Oracle, Oledb Providers and so on..) and closed
for manipulation
abstract class DataProvider
{
public abstract int OpenConnection();
public abstract int CloseConnection();
public abstract int ExecuteCommand();
}
class SqlDataProvider : DataProvider
{
public override int OpenConnection()
{
Console.WriteLine("nSql Connection opened");
return 1;
}
public override int CloseConnection()
{
Console.WriteLine("Sql Connection closed");
return 1;
}
public override int ExecuteCommand()
{
Console.WriteLine("Sql Command Executed");
return 1;
}
}
- 7. OPEN/CLOSED PRINCIPLE
class OracleDataProvider : DataProvider
{
public override int OpenConnection()
{
Console.WriteLine("Oracle Connection opened");
return 1;
}
public override int CloseConnection()
{
Console.WriteLine("Oracle Connection closed");
return 1;
}
public override int ExecuteCommand()
{
Console.WriteLine("Oracle Command Executed");
return 1;
}
}
class OpenClosePrincipleDemo
{
public static void OSPDemo()
{
Console.WriteLine("Open Close Principle");
DataProvider DataProviderObject =
new SqlDataProvider();
DataProviderObject.OpenConnection();
DataProviderObject.ExecuteCommand();
DataProviderObject.CloseConnection();
DataProviderObject = new OracleDataProvider();
DataProviderObject.OpenConnection();
DataProviderObject.ExecuteCommand();
DataProviderObject.CloseConnection();
}
}
- 8. LISKOV SUBSTITUTION PRINCIPLE
Let f(x) be a property of objects X of type T.
Then f(y) should be true for objects Y of type S
where S is a subtype of T
Barbara Liskov
- 9. LISKOV SUBSTITUTION PRINCIPLE
// While implementing derived classes, one needs to
ensure that, derived classes just extend the
functionality of base classes without replacing the
functionality of base classes.
class Rectangle
{
protected int mWidth = 0;
protected int mHeight = 0;
public virtual void SetWidth(int width)
{
mWidth = width;
}
public virtual void SetHeight(int height)
{
mHeight = height;
}
public virtual int GetArea()
{
return mWidth * mHeight;
}
}
// While implementing derived class if one replaces
the functionality of base class then,
// it might results into undesired side effects when
such derived classes are used in existing program
modules.
class Square : Rectangle
{
// This class modifies the base class
functionality instead of extending the base class
functionality. Now below methods implementation will
impact base class functionality.
public override void SetWidth(int width)
{
mWidth = width;
mHeight = width;
}
public override void SetHeight(int height)
{
mWidth = height;
mHeight = height;
}
}
- 12. INTERFACE SEGREGATION PRINCIPLE
// Only common generic methods exists for all
derived classes.
interface IDataProvider
{
int OpenConnection();
int CloseConnection();
}
// Implement methods specific to the respective
derived classes
interface ISqlDataProvider : IDataProvider
{
int ExecuteSqlCommand();
}
// Implement methods specific to the respective
derived classes
interface IOracleDataProvider : IDataProvider
{
int ExecuteOracleCommand();
}
// Should not force SqlDataProvider client to
implement ExecuteOracleCommand, as it wont required
that method to be implemented.
class SqlDataClient : ISqlDataProvider
{
public int OpenConnection()
{
Console.WriteLine("Sql Connection opened");
return 1;
}
public int CloseConnection()
{
Console.WriteLine("Sql Connection closed");
return 1;
}
public int ExecuteSqlCommand()
{
Console.WriteLine("Sql Server specific
Command Executed successfully");
return 1;
}
}
- 13. INTERFACE SEGREGATION PRINCIPLE
class OracleDataClient : IOracleDataProvider
{
public int OpenConnection()
{
Console.WriteLine("Oracle Connection
opened");
return 1;
}
public int CloseConnection()
{
Console.WriteLine("Oracle Connection
closed");
return 1;
}
public int ExecuteOracleCommand()
{
Console.WriteLine("Oracle specific Command
Executed successfully");
return 1;
}
}
class InterfaceSegregationPrincipleDemo
{
public static void ISPDemo()
{
Console.WriteLine("Interface Inversion
Principle Demo ");
ISqlDataProvider SqlDataProviderObject =
new SqlDataClient();
SqlDataProviderObject.OpenConnection();
SqlDataProviderObject.ExecuteSqlCommand();
SqlDataProviderObject.CloseConnection();
IOracleDataProvider OracleDataProviderObject
= new OracleDataClient();
OracleDataProviderObject.OpenConnection();
OracleDataProviderObject.ExecuteOracleCommand();
OracleDataProviderObject.CloseConnection();
}
}
- 15. DEPENDENCY INVERSION PRINCIPLE
A. High-level modules should not depend on
low-level modules. Both should depend on
abstractions.
B. Abstractions should not depend on details.
Details should depend on abstractions.
- 16. DEPENDENCY INVERSION PRINCIPLE
public interface ITransferSource
{
long AccountNumber { get; set; }
decimal Balance { get; set; }
void RemoveFunds(decimal value);
}
public interface ITransferDestination
{
long AccountNumber { get; set; }
decimal Balance { get; set; }
void AddFunds(decimal value);
}
public class BOABankAccount :
ITransferSource,
ITransferDestination
{
public long AccountNumber { get; set; }
public decimal Balance { get; set; }
public void AddFunds(decimal value)
{
Balance += value;
}
public void RemoveFunds(decimal value)
{
Balance -= value;
}
}
- 17. DEPENDENCY INVERSION PRINCIPLE
public class TransferAmounts
{
public decimal Amount { get; set; }
public void Transfer(
ITransferSource TransferSource,
ITransferDestination TransferDestination)
{
TransferSource.RemoveFunds(Amount);
TransferDestination.AddFunds(Amount);
}
}
public class BOABankAccount :
ITransferSource,
ITransferDestination
{
public long AccountNumber { get; set; }
public decimal Balance { get; set; }
public void AddFunds(decimal value)
{
Balance += value;
}
public void RemoveFunds(decimal value)
{
Balance -= value;
}
}
- 18. INVERSION OF CONTROL PRINCIPLE
Describes a design in which custom-written
portions of a computer program receive the flow
of control from a generic, reusable library
- 19. What For?
• To decouple the execution of a task from
implementation.
• To focus a module on the task it is designed for.
• To free modules from assumptions about how
other systems do what they do and instead rely
on contracts.
• To prevent side effects when replacing a module.
- 21. DEPENDENCY INJECTION PATTERN
A software design pattern that implements
inversion of control for resolving dependencies
through:
• Constructor Injection
• Method Injection
• Property Injection
- 23. SERVICE LOCATOR PATTERN
A design pattern used in software development
to encapsulate the processes involved in
obtaining a service with a strong abstraction
layer.
- 25. IOC CONTAINERS
What for?
• Is responsible for creating
objects
• Is responsible for dependency
injection
• Manages lifetime of those
objects
• Removes dependencies from
your code
Concrete Implementations
• Microsoft Unity
• Castle Windsor
• Ninject
• Spring.NET
• Light Inject
• Simple Injector
- 27. WHAT MAKES YOUR CODE TESTABLE ?
SRP
Since type is doing
only one job, you can
clearly understand
what should be
tested.
LSP
Since types used
can be replaced with
subtypes, mocks and
stubs can be used
instead of real types.
ISP
Since there are more
granular client-
specific interfaces,
you know exactly
what to mock.
- 28. LET’S REVIEW SOME CONCEPTS
Reusable
Higher-level components can
be reused if lower-lever
components change with time
to meet the requirements.
Extensible
Software is easily extended
with new components and
features because design is
based on abstractions.
Replaceable
Lower-level components can be
replaced to meet new
requirements without modifying
existing code.
Loosely Coupled
Allows components to perform
on their own with as little
knowledge as possible about
other components.
Cohesive
Build you software easily with
different components as
building blocks.
Testable
It is easy to understand what
and how to test in each
component because
components are loosely-
coupled.