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

Software design principles SOLID

  • 1.
  • 2.
  • 3.
    Single Responsibility Principle ● Aclass/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
  • 4.
    Example ● Problem: Iwant to input 2 number and want the sum of those as output. ● Possible method?
  • 5.
    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); }
  • 6.
    GUI button click privatevoid 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()); }
  • 7.
    Quiz ● How manyworks (responsibilities) the methods are working in past two slides? ○ 1 ○ 2 ○ 3 ○ 4
  • 8.
    Before giving theanswer ● 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?
  • 9.
    Answer: 2 ● Themethods 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]
  • 10.
    What it shouldbe? 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; }
  • 11.
    What it shouldbe 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; }
  • 12.
    Open/Closed Principle ● Softwareentities (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
  • 13.
    Example ● Say, wehave 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/=)
  • 14.
    public enum EmployeeType { Manager, Officer, Clerk } publicclass 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; } } }
  • 15.
    Client’s changed requirement ● Wewant 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
  • 16.
    What possibly wecan 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
  • 17.
    But… ● We arefrequently 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
  • 18.
    Lets dig theproblem ● 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
  • 19.
    What should wedo? ● Decouple the responsibility of this class ● Introduce inheritance and interface if needed ● Use dynamic polymorphism etc. ● Use abstraction
  • 20.
    Steps public abstract classEmployee { public abstract double GetSalary(); }
  • 21.
    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; } }
  • 22.
    Steps public class SalaryCalculator { publicdouble GetSalary(Employee type) { return type.GetSalary(); } } SalaryCalculator salaryCalculator=new SalaryCalculator(); Manager manager=new Manager(); double sum = salaryCalculator.GetSalary(manager); DisplayOutput(sum);
  • 23.
    If 1% taxis 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; } }
  • 24.
    If Human ResourceManager 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
  • 25.
    Liskov Substitution Principle ● Functionsthat 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
  • 26.
    Example ● I wantto 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
  • 27.
    Animal class public abstractclass Animal { public abstract void Eat(); public abstract void Drink(); public abstract void Run(); public abstract void Fly(); }
  • 28.
    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?
  • 29.
    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(); } }
  • 30.
    Can we considerlion’s object as animal’s object according to Liskov’s Substitution Principle?
  • 31.
    Good practice ● Accordingto GOF(Gang of four): ○ Favor object composition over class inheritance ● So, what can we do now?
  • 32.
    Steps ● We shouldfind 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
  • 33.
    public abstract classAnimal { public abstract void Eat(); public abstract void Drink(); } public interface Runnable { void Run(); } public interface Flyable { void Fly(); }
  • 34.
    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"); } }
  • 35.
    We can nowintroduce 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"); } }
  • 36.
    Interface Segregation Principle ● Clientsshould not be forced to depend upon interfaces that they do not use ● Keep interfaces small and cohesive
  • 37.
    A traditional Gateway classshould ● Save object ● Get All object ● Get object by id ● Get object by name ● Get object by date ● Udpate object ● Delete object
  • 38.
    Lets create aninterface 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); }
  • 39.
    But… ● Do youthink 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
  • 40.
    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
  • 41.
    Better interfaces public interfaceIProductGateway { 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); }
  • 42.
    Sample implementation public classProductGateway: IGateway, IProductGateway { // implement interfaces }
  • 43.
    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
  • 44.
    Example ● Suppose thereis 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 } }
  • 45.
    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
  • 46.
    Result public class ProductGateway { privateSqlConnection connection; public ProductGateway(SqlConnection sqlConnection) { connection = sqlConnection; } public void Save(Product product) { //save logic } }
  • 47.
    Benefit ● We canchange the connection properties while calling the save method. ● So the dependency is inverted