SOLID Principles
   Declan Whelan
     @dwhelan
SOLID Principles

• Single Responsibility Principle
• Open Closed Principle
• Liskov Substitution Principle
• Interface Segregation Principle
• Dependency Inversion Principle
Single Responsibility
        Principle


There should be one and only one
reason for a class to change.
SRP Violation
public class UserSettingService
{
  public void changeEmail(User user)
  {
    if(checkAccess(user))
    {
       //Grant option to change
    }
  }

  public boolean checkAccess(User user)
  {
    //Verify if the user is valid.
  }
}
SRP Restored
public class UserSettingService
{
  public void changeEmail(User user)
  {
    if(SecurityService.checkAccess(user))
    {
       //Grant option to change
    }
  }
}

public class SecurityService
{
  public static boolean checkAccess(User user)
  {
    //check the access.
  }
}
Open Closed Principle


Software entities should be open for
extension but closed for modification.
A Product Filter


We need a way to filter products by colour.
Class Responsibility
The classes’ responsibility is to filter
products (its job) based off the action of
filtering by colour (its behaviour).
The ProductFilter is responsible for
filtering products by colour.
Filter By Colour
public class ProductFilter
{
  public IEnumerable<Product> ByColor(IList<Product> products, ProductColor productColor)
  {
     foreach (var product in products)
     {
        if (product.Color == productColor)
            yield return product;
     }
  }
}
More Requirements!
User: We also need to filter by size.
Developer: Just size or colour and size?
User: Umm probably both.
Developer: Great!
Filter By Colour

The ProductFilter is responsible
for filtering products by colour,
size, colour and size.
public class ProductFilter
{

  {
                        Filter By Colour
  public IEnumerable<Product> ByColor(IList<Product> products, ProductColor productColor)

     foreach (var product in products)
     {
        if (product.Color == productColor)
            yield return product;
     }
  }

    public IEnumerable<Product> ByColorAndSize(IList<Product> products,
                                   ProductColor productColor,
                                   ProductSize productSize)
    {
      foreach (var product in products)
      {
         if ((product.Color == productColor) &&
             (product.Size == productSize))
             yield return product;
      }
    }

    public IEnumerable<Product> BySize(IList<Product> products,
                             ProductSize productSize)
    {
      foreach (var product in products)
      {
         if ((product.Size == productSize))
             yield return product;
      }
    }
}
OCP Filter
     public abstract class ProductFilterSpecification
{
    public IEnumerable<Product> Filter(IList<Product> products)
    {
      return ApplyFilter(products);
    }

    protected abstract IEnumerable<Product> ApplyFilter(IList<Product> products);
}

public class ProductFilter
{
  public IEnumerable<Product> By(IList<Product> products, ProductFilterSpecification
                                        filterSpecification)
  {
     return filterSpecification.Filter(products);
  }
}
OCP Filter
     public class ColorFilterSpecification : ProductFilterSpecification
{
    private readonly ProductColor productColor;

    public ColorFilterSpecification(ProductColor productColor)
    {
      this.productColor = productColor;
    }

    protected override IEnumerable<Product> ApplyFilter(IList<Product> products)
    {
      foreach (var product in products)
      {
         if (product.Color == productColor)
             yield return product;
      }
    }
}
Liskov Substitution
         Principle

Functions that use references to base
classes must be able to use objects of
derived classes without knowing it.
LSP Violation
    class Rectangle
{
!   protected int m_width;
!   protected int m_height;

!   public void setWidth(int width){
!   ! m_width = width;
!   }

!   public void setHeight(int height){
!   ! m_height = height;
!   }

!   public int getWidth(){
!   ! return m_width;
!   }

!   public int getHeight(){
!   ! return m_height;
!   }

!   public int getArea(){
!   ! return m_width * m_height;
!   }!
}
LSP Violation
    class Square extends Rectangle
{
!   public void setWidth(int width){
!   ! m_width = width;
!   ! m_height = width;
!   }

!   public void setHeight(int height){
!   ! m_width = height;
!   ! m_height = height;
!   }
}
LSP Violation
    class LspTest
{
!   private static Rectangle getNewRectangle()
!   {
!   ! // it can be an object returned by some factory ...
!   ! return new Square();
!   }

!   public static void main (String args[])
!   {
!   ! Rectangle r = LspTest.getNewRectangle();

!   !   r.setWidth(5);
!   !   r.setHeight(10);
!   !   // user knows that r it's a rectangle.
!   !   // It assumes that he's able to set the width and height as for the base class

!   !   System.out.println(r.getArea());
!   !   // now he's surprised to see that the area is 100 instead of 50.
!   }
}
Interface Segregation
         Principle


Clients should not be forced to depend
on interfaces that they do not use.
ISP Example
    public abstract class Animal
{
    public abstract void Feed();
}

public class Dog : Animal
{
  public override void Feed()
  {
     // do something
  }
}

public class Rattlesnake : Animal
{
  public override void Feed()
  {
     // do something
  }
}
ISP Violation
    public abstract class Animal
{
    public abstract void Feed();
    public abstract void Groom();
}

public class Rattlesnake : Animal
{
  public override void Feed()
  {
     // do something
  }

    public override void Groom()
    {
      // ignore
    }
}
ISP Restored
     public interface IPet
{
    void Groom();
}

public abstract class Animal
{
  public abstract void Feed();
}

public class Dog : Animal, IPet
{
  public override void Feed()
  {
     // do something
  }

    public void Groom()
    {
      // do something
    }
}

public class Rattlesnake : Animal
{
  public override void Feed()
  {
     // do something
  }
Handout
public class CustomMembershipProvider : MembershipProvider
{
  public override string ApplicationName
  {
     get
     {
        throw new Exception("The method or operation is not implemented.");
     }
     set
     {
        throw new Exception("The method or operation is not implemented.");
     }
  }

  public override bool ChangePassword(string username, string oldPassword, string
newPassword)
  {
    throw new Exception("The method or operation is not implemented.");
  }

  public override bool ChangePasswordQuestionAndAnswer(string username, string
Dependency Inversion
      Principle
High level modules should not depend
upon low level modules. Both should
depend upon abstractions.
Abstractions should not depend upon
details. Details should depend upon
abstractions.
DIP Violation
    public class OrderProcessor
{
    public decimal CalculateTotal(Order order)
    {
        decimal itemTotal = order.GetItemTotal();
        decimal discountAmount = DiscountCalculator.CalculateDiscount(order);

        decimal taxAmount = 0.0M;

        if (order.Country == "US")
            taxAmount = FindTaxAmount(order);
        else if (order.Country == "UK")
            taxAmount = FindVatAmount(order);

        decimal total = itemTotal - discountAmount + taxAmount;

        return total;
    }

    private decimal FindVatAmount(Order order)
    {
        // find the UK value added tax somehow
        return 10.0M;
    }

    private decimal FindTaxAmount(Order order)
    {
        // find the US tax somehow
        return 10.0M;
    }
}
DIP Restored
    public interface IDiscountCalculator
{
    decimal CalculateDiscount(Order order);
}

public interface ITaxStrategy
{
    decimal FindTaxAmount(Order order);
}

public class OrderProcessor
{
    private readonly IDiscountCalculator _discountCalculator;
    private readonly ITaxStrategy _taxStrategy;

    public OrderProcessor(IDiscountCalculator discountCalculator,
                          ITaxStrategy taxStrategy)
    {
        _taxStrategy = taxStrategy;
        _discountCalculator = discountCalculator;
    }

    public decimal CalculateTotal(Order order)
    {
        decimal itemTotal = order.GetItemTotal();
        decimal discountAmount = _discountCalculator.CalculateDiscount(order);
        decimal taxAmount = _taxStrategy.FindTaxAmount(order);
        decimal total = itemTotal - discountAmount + taxAmount;

        return total;
    }
}
DIP Restored
    public class DiscountCalculatorAdapter : IDiscountCalculator
{
    public decimal CalculateDiscount(Order order)
    {
        return DiscountCalculator.CalculateDiscount(order);
    }
}

public class USTaxStrategy : ITaxStrategy
{
    public decimal FindTaxAmount(Order order)
    {
    }
}

public class UKTaxStrategy : ITaxStrategy
{
    public decimal FindTaxAmount(Order order)
    {
    }
}
Reading
Clean Code: A Handbook of Agile Software
Craftsmanship Robert C. Martin

Design Patterns: Elements of Reusable Object-Oriented
Software Erich Gamma, Richard Helm, Ralph Johnson,
and John Vlissides

Agile Principles, Patterns and Practices in C#
Robert C. Martin

Lean Architecture: for Agile Software Development
James O. Coplien and Gertrud Bjørnvig
Photo Credits
SOLID Motivational Posters, by Derick Bailey, is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.



                        http://lostechies.com/derickbailey/files/2011/03/SOLID_6EC97F9C.jpg




                        http://lostechies.com/derickbailey/files/2011/03/SingleResponsibilityPrinciple2_71060858.jpg




                        http://lostechies.com/derickbailey/files/2011/03/OpenClosedPrinciple2_2C596E17.jpg




                        http://lostechies.com/derickbailey/files/2011/03/LiskovSubtitutionPrinciple_52BB5162.jpg




                        http://lostechies.com/derickbailey/files/2011/03/InterfaceSegregationPrinciple_60216468.jpg




                        http://lostechies.com/derickbailey/files/2011/03/DependencyInversionPrinciple_0278F9E2.jpg

Solid principles

  • 1.
    SOLID Principles Declan Whelan @dwhelan
  • 3.
    SOLID Principles • SingleResponsibility Principle • Open Closed Principle • Liskov Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle
  • 5.
    Single Responsibility Principle There should be one and only one reason for a class to change.
  • 6.
    SRP Violation public classUserSettingService {   public void changeEmail(User user)   {     if(checkAccess(user))     {        //Grant option to change     }   }   public boolean checkAccess(User user)   {     //Verify if the user is valid.   } }
  • 7.
    SRP Restored public classUserSettingService {   public void changeEmail(User user)   {     if(SecurityService.checkAccess(user))     {        //Grant option to change     }   } } public class SecurityService {   public static boolean checkAccess(User user)   {     //check the access.   } }
  • 9.
    Open Closed Principle Softwareentities should be open for extension but closed for modification.
  • 10.
    A Product Filter Weneed a way to filter products by colour.
  • 11.
    Class Responsibility The classes’responsibility is to filter products (its job) based off the action of filtering by colour (its behaviour). The ProductFilter is responsible for filtering products by colour.
  • 12.
    Filter By Colour publicclass ProductFilter { public IEnumerable<Product> ByColor(IList<Product> products, ProductColor productColor) { foreach (var product in products) { if (product.Color == productColor) yield return product; } } }
  • 13.
    More Requirements! User: Wealso need to filter by size. Developer: Just size or colour and size? User: Umm probably both. Developer: Great!
  • 14.
    Filter By Colour TheProductFilter is responsible for filtering products by colour, size, colour and size.
  • 15.
    public class ProductFilter { { Filter By Colour public IEnumerable<Product> ByColor(IList<Product> products, ProductColor productColor) foreach (var product in products) { if (product.Color == productColor) yield return product; } } public IEnumerable<Product> ByColorAndSize(IList<Product> products, ProductColor productColor, ProductSize productSize) { foreach (var product in products) { if ((product.Color == productColor) && (product.Size == productSize)) yield return product; } } public IEnumerable<Product> BySize(IList<Product> products, ProductSize productSize) { foreach (var product in products) { if ((product.Size == productSize)) yield return product; } } }
  • 16.
    OCP Filter public abstract class ProductFilterSpecification { public IEnumerable<Product> Filter(IList<Product> products) { return ApplyFilter(products); } protected abstract IEnumerable<Product> ApplyFilter(IList<Product> products); } public class ProductFilter { public IEnumerable<Product> By(IList<Product> products, ProductFilterSpecification filterSpecification) { return filterSpecification.Filter(products); } }
  • 17.
    OCP Filter public class ColorFilterSpecification : ProductFilterSpecification { private readonly ProductColor productColor; public ColorFilterSpecification(ProductColor productColor) { this.productColor = productColor; } protected override IEnumerable<Product> ApplyFilter(IList<Product> products) { foreach (var product in products) { if (product.Color == productColor) yield return product; } } }
  • 19.
    Liskov Substitution Principle Functions that use references to base classes must be able to use objects of derived classes without knowing it.
  • 20.
    LSP Violation class Rectangle { ! protected int m_width; ! protected int m_height; ! public void setWidth(int width){ ! ! m_width = width; ! } ! public void setHeight(int height){ ! ! m_height = height; ! } ! public int getWidth(){ ! ! return m_width; ! } ! public int getHeight(){ ! ! return m_height; ! } ! public int getArea(){ ! ! return m_width * m_height; ! }! }
  • 21.
    LSP Violation class Square extends Rectangle { ! public void setWidth(int width){ ! ! m_width = width; ! ! m_height = width; ! } ! public void setHeight(int height){ ! ! m_width = height; ! ! m_height = height; ! } }
  • 22.
    LSP Violation class LspTest { ! private static Rectangle getNewRectangle() ! { ! ! // it can be an object returned by some factory ... ! ! return new Square(); ! } ! public static void main (String args[]) ! { ! ! Rectangle r = LspTest.getNewRectangle(); ! ! r.setWidth(5); ! ! r.setHeight(10); ! ! // user knows that r it's a rectangle. ! ! // It assumes that he's able to set the width and height as for the base class ! ! System.out.println(r.getArea()); ! ! // now he's surprised to see that the area is 100 instead of 50. ! } }
  • 24.
    Interface Segregation Principle Clients should not be forced to depend on interfaces that they do not use.
  • 25.
    ISP Example public abstract class Animal { public abstract void Feed(); } public class Dog : Animal { public override void Feed() { // do something } } public class Rattlesnake : Animal { public override void Feed() { // do something } }
  • 26.
    ISP Violation public abstract class Animal { public abstract void Feed(); public abstract void Groom(); } public class Rattlesnake : Animal { public override void Feed() { // do something } public override void Groom() { // ignore } }
  • 27.
    ISP Restored public interface IPet { void Groom(); } public abstract class Animal { public abstract void Feed(); } public class Dog : Animal, IPet { public override void Feed() { // do something } public void Groom() { // do something } } public class Rattlesnake : Animal { public override void Feed() { // do something }
  • 28.
    Handout public class CustomMembershipProvider: MembershipProvider { public override string ApplicationName { get { throw new Exception("The method or operation is not implemented."); } set { throw new Exception("The method or operation is not implemented."); } } public override bool ChangePassword(string username, string oldPassword, string newPassword) { throw new Exception("The method or operation is not implemented."); } public override bool ChangePasswordQuestionAndAnswer(string username, string
  • 30.
    Dependency Inversion Principle High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
  • 31.
    DIP Violation public class OrderProcessor { public decimal CalculateTotal(Order order) { decimal itemTotal = order.GetItemTotal(); decimal discountAmount = DiscountCalculator.CalculateDiscount(order); decimal taxAmount = 0.0M; if (order.Country == "US") taxAmount = FindTaxAmount(order); else if (order.Country == "UK") taxAmount = FindVatAmount(order); decimal total = itemTotal - discountAmount + taxAmount; return total; } private decimal FindVatAmount(Order order) { // find the UK value added tax somehow return 10.0M; } private decimal FindTaxAmount(Order order) { // find the US tax somehow return 10.0M; } }
  • 32.
    DIP Restored public interface IDiscountCalculator { decimal CalculateDiscount(Order order); } public interface ITaxStrategy { decimal FindTaxAmount(Order order); } public class OrderProcessor { private readonly IDiscountCalculator _discountCalculator; private readonly ITaxStrategy _taxStrategy; public OrderProcessor(IDiscountCalculator discountCalculator, ITaxStrategy taxStrategy) { _taxStrategy = taxStrategy; _discountCalculator = discountCalculator; } public decimal CalculateTotal(Order order) { decimal itemTotal = order.GetItemTotal(); decimal discountAmount = _discountCalculator.CalculateDiscount(order); decimal taxAmount = _taxStrategy.FindTaxAmount(order); decimal total = itemTotal - discountAmount + taxAmount; return total; } }
  • 33.
    DIP Restored public class DiscountCalculatorAdapter : IDiscountCalculator { public decimal CalculateDiscount(Order order) { return DiscountCalculator.CalculateDiscount(order); } } public class USTaxStrategy : ITaxStrategy { public decimal FindTaxAmount(Order order) { } } public class UKTaxStrategy : ITaxStrategy { public decimal FindTaxAmount(Order order) { } }
  • 34.
    Reading Clean Code: AHandbook of Agile Software Craftsmanship Robert C. Martin Design Patterns: Elements of Reusable Object-Oriented Software Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides Agile Principles, Patterns and Practices in C# Robert C. Martin Lean Architecture: for Agile Software Development James O. Coplien and Gertrud Bjørnvig
  • 35.
    Photo Credits SOLID MotivationalPosters, by Derick Bailey, is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License. http://lostechies.com/derickbailey/files/2011/03/SOLID_6EC97F9C.jpg http://lostechies.com/derickbailey/files/2011/03/SingleResponsibilityPrinciple2_71060858.jpg http://lostechies.com/derickbailey/files/2011/03/OpenClosedPrinciple2_2C596E17.jpg http://lostechies.com/derickbailey/files/2011/03/LiskovSubtitutionPrinciple_52BB5162.jpg http://lostechies.com/derickbailey/files/2011/03/InterfaceSegregationPrinciple_60216468.jpg http://lostechies.com/derickbailey/files/2011/03/DependencyInversionPrinciple_0278F9E2.jpg