Skip to main content
Software Design
Principles
Foyzul Karim
foyzulkarim@gmail.com
SOLID
SRP
OCP
LSPISP
DIP
Single Responsibility
Principle
● A class/method should have one, and only one, reason
to change
● What do we mean by reason?
○ It is purpose or responsibility
● Focus on SOC
○ Separation of Concern
○ Make your method/class do one thing at a time
● Make your code highly cohesive and loosely coupled
Example
● Problem: I want to input 2 number and want the sum of
those as output.
● Possible method?
Console
static void Main(string[] args)
{
string first = Console.ReadLine();
string second = Console.ReadLine();
double firstNumber = Convert.ToDouble(first);
double secondNumber = Convert.ToDouble(second);
double sum = firstNumber + secondNumber;
Console.WriteLine(sum);
}
GUI button click
private void addButton_Click(object sender, EventArgs e)
{
string first = textBox1.Text;
string second = textBox2.Text;
double firstNumber = Convert.ToDouble(first);
double secondNumber = Convert.ToDouble(second);
double sum = firstNumber + secondNumber;
MessageBox.Show(sum.ToString());
}
Quiz
● How many works (responsibilities) the methods are
working in past two slides?
○ 1
○ 2
○ 3
○ 4
Before giving the answer
● If the user wants to get the output in file, what will you
do?
● If the user wants to take the input from file, what will you
do?
Answer: 2
● The methods are doing two different types of works in a
single scope
○ Process the input
○ Display output
[Of course we can split the input processing into chunks. But we can skip that for
now]
What it should be?
static void Main(string[] args)
{
double sum = GetSum();
DisplayOutput(sum);
}
private static void DisplayOutput(double sum)
{
Console.WriteLine(sum);
}
private static double GetSum()
{
string first = Console.ReadLine();
string second = Console.ReadLine();
double firstNumber = Convert.ToDouble(first);
double secondNumber = Convert.ToDouble(second);
return firstNumber + secondNumber;
}
What it should be in GUI?
private void addButton_Click(object sender, EventArgs e)
{
double sum = GetSum();
DisplayOutput(sum);
}
private static void DisplayOutput(double sum)
{
MessageBox.Show(sum.ToString());
}
private double GetSum()
{
string first = textBox1.Text;
string second = textBox2.Text;
double firstNumber = Convert.ToDouble(first);
double secondNumber = Convert.ToDouble(second);
double sum = firstNumber + secondNumber;
return sum;
}
Open/Closed Principle
● Software entities (classes, modules, functions, etc.)
should be open for extension, but closed for modification
● Change a class’ behaviour using inheritance and
composition
● What does we mean by modification?
○ Responds to the client’s changed requirement
Example
● Say, we have a class which calculates the salary of a
office.
● That office has three types of employees
○ Manager (salary: 100000/=)
○ Officer (salary: 50000/=)
○ Clerk (salary: 20000/=)
public enum EmployeeType
{
Manager,
Officer,
Clerk
}
public class SalaryCalculator
{
public double GetSalary(EmployeeType type)
{
switch (type)
{
case EmployeeType.Manager:
return 100000;
case EmployeeType.Officer:
return 50000;
case EmployeeType.Clerk:
return 20000;
default:
return 0;
}
}
}
Client’s changed
requirement
● We want to put taxes for the employees who withdraw
salary more than 50000/=
● We want to introduce Human Resource Manager into
our office who will get 35000/= per month
What possibly we can do?
● Add another enum type
● Add another check in the switch
● Add logic to check whether the salary is more than
50000/= or not etc
But…
● We are frequently changing our class
● This may costs in functionality malfunctioning
● Class is getting bigger and various type of code smell
arises
● Maintaining the code is getting more harder and harder
● Unit test cases may fail for this new code modification
Lets dig the problem
● Lets apply SRP here
○ Salary calculator is calculating salary for each type of employee which is not
good
○ Salary calculator is going to be changed for multiple reason like
● tax rule
● introduction of new employee
● Change of salary amount
● Change of salary calculation logic
What should we do?
● Decouple the responsibility of this class
● Introduce inheritance and interface if needed
● Use dynamic polymorphism etc.
● Use abstraction
Steps
public abstract class Employee
{
public abstract double GetSalary();
}
public class Manager : Employee
{
public override double GetSalary()
{
return 100000;
}
}
public class Officer: Employee
{
public override double GetSalary()
{
return 50000;
}
}
public class Clerk : Employee
{
public override double GetSalary()
{
return 20000;
}
}
Steps
public class SalaryCalculator
{
public double GetSalary(Employee type)
{
return type.GetSalary();
}
}
SalaryCalculator salaryCalculator=new SalaryCalculator();
Manager manager=new Manager();
double sum = salaryCalculator.GetSalary(manager);
DisplayOutput(sum);
If 1% tax is applied for the
salary more than 50000/=
public class SalaryCalculator
{
public double GetSalary(Employee type)
{
double salary = type.GetSalary();
if (salary>50000)
{
double tax = salary*.01;
return salary - tax;
}
return salary;
}
}
If Human Resource Manager
is introduced with salary
35000/=
public class HRM : Employee
{
public override double GetSalary()
{
return 35000;
}
}
I didn’t have to change my existing code for this new requirement
Liskov Substitution
Principle
● Functions that use pointers or references to base
classes must be able to use objects of derived classes
without knowing it
● Subclasses should behave nicely when used in place of
their base class
Example
● I want to output the behavior of the animals
● The specific animal should apply the behavior according
to them
● So my Animal class might have the following methods
○ Eat
○ Drink
○ Run
○ Fly
Animal class
public abstract class Animal
{
public abstract void Eat();
public abstract void Drink();
public abstract void Run();
public abstract void Fly();
}
Let's introduce Lion’s
behavior
● Lion is an animal. So it will inherit Animal class
● So it should
○ Eat
○ Drink
○ Run
● But does it fly?
public class Lion : Animal
{
public override void Eat()
{
// System.Console.Out.WriteLine("Eats flesh");
}
public override void Drink()
{
// System.Console.Out.WriteLine("Drinks water");
}
public override void Run()
{
// System.Console.Out.WriteLine("Runs fast");
}
public override void Fly()
{
throw new NotImplementedException();
}
}
Can we consider lion’s object
as animal’s object according
to Liskov’s Substitution
Principle?
Good practice
● According to GOF(Gang of four):
○ Favor object composition over class inheritance
● So, what can we do now?
Steps
● We should find out the common behavior for all of the
animals and put those in Animal class
● For specific functionalities, we should use Interface
instead of putting those in base class
public abstract class Animal
{
public abstract void Eat();
public abstract void Drink();
}
public interface Runnable
{
void Run();
}
public interface Flyable
{
void Fly();
}
public class Lion : Animal, Runnable
{
public override void Eat()
{
// System.Console.Out.WriteLine("Eats flesh");
}
public override void Drink()
{
// System.Console.Out.WriteLine("Drinks water");
}
public void Run()
{
//System.Console.Out.WriteLine("Runs fast");
}
}
We can now introduce Eagle
class easily
public class Eagle: Animal, Flyable
{
public override void Eat()
{
// System.Console.Out.WriteLine("Eats flesh");
}
public override void Drink()
{
// System.Console.Out.WriteLine("Drinks water");
}
public void Fly()
{
// System.Console.Out.WriteLine("Fly high");
}
}
Interface Segregation
Principle
● Clients should not be forced to depend upon interfaces
that they do not use
● Keep interfaces small and cohesive
A traditional Gateway
class should
● Save object
● Get All object
● Get object by id
● Get object by name
● Get object by date
● Udpate object
● Delete object
Lets create an interface
public interface IGateway
{
void Save(object o);
IList GetAll();
object GetById(string id);
IList GetByName(string name);
IList GetByDate(DateTime dateTime);
void Update(object o);
void Delete(object o);
}
But…
● Do you think all of the gateway classes needs those
methods?
● For example it is common that some objects don’t have
any DateTime property but they are also forced to
implement GetByDate method which is not good
Lets break it
● Put the minimum block of methods in a specific interface
● Create multiple interface instead of putting all of the
methods in a single one
Better interfaces
public interface IProductGateway
{
IList<Product> GetByName(string name);
IList<Product> GetByDate(DateTime dateTime);
}
public interface IGateway
{
void Save(object o);
IList GetAll();
object GetById(string id);
void Update(object o);
void Delete(object o);
}
Sample implementation
public class ProductGateway: IGateway, IProductGateway
{
// implement interfaces
}
Dependency Inversion
Principle
● A. High level modules should not depend upon low level
modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details
should depend upon abstractions
● Use lots of interfaces and abstractions
Example
● Suppose there is a product gateway class which
saves a product object
public class ProductGateway
{
private SqlConnection connection;
public ProductGateway()
{
string connectionString = “connection value goes here";
connection = new SqlConnection(connectionString);
}
public void Save(Product product)
{
//save logic
}
}
Whats the problem?
● Manager class is dependent on the gateway class in
terms of connection.
● What to do?
● Rather than hard code the connection into the gateway
class, we can parameterized the connection to be used
in gateway class to perform operations
Result
public class ProductGateway
{
private SqlConnection connection;
public ProductGateway(SqlConnection sqlConnection)
{
connection = sqlConnection;
}
public void Save(Product product)
{
//save logic
}
}
Benefit
● We can change the connection properties while calling
the save method.
● So the dependency is inverted