Understanding Single Responsibility and Interface Segregation

weixin_33831673發表於2013-05-11

Introduction

In the previous one article,DebugLZQ discussed OCP(Open Closed Principle) and DIP(Dependency InversionPrinciple) of SOLID.

Here I am going to discuss SRP(Single Responsibility Principle) and ISP(Interface Segregation Principle) of SOLID.

Background

If you read the previous one article it will very helpful for you to understand this one because I am going to use the same example here.So read the below artcle first.

 Understanding Open Closed Principle and Dependency Inversion

Using the Code

Before start technical discussion I want to answer the below questions.

What is Single Responsibiliy Principle?

Answer:Every class should have one single responsibility.

What is Interface Segregation?

Answer:No client should be forced to depend on motheds it does not use.

I am going to use the same example which I used in my previous article.Let's consider an example to make it better understand.Suppose I need the price of the computer. I went to a Computer Shop to ask price of desktop.Let's convert my requirements into code. 

using System;

namespace SRIS
{
    public interface IComputer
    {
        string GetComputerDescripution();
        string GetColor();
        double GetPrice();
        double GetPriceAfterTax();
    }

    public class Desktop : IComputer
    {
        private double desktopPrice = 2300;
 
        public string GetComputerDescripution()
        {
            return "You get a desktop";
        }

        public string GetColor()
        {
            return "Color is white";
        }

        public double GetPrice()
        {
            return desktopPrice;
        }

        public double GetPriceAfterTax()
        {
            return desktopPrice + desktopPrice * .20;
        }
    }

    public class Laptop : IComputer
    {
        private double laptopPrice = 5000;//Change Price (one reason)

        public string GetComputerDescripution()
        {
            return "You get a laptop";
        }

        public string GetColor()
        {
            return "Color is black";
        }

        public double GetPrice()
        {
            return laptopPrice;
        }

        public double GetPriceAfterTax()
        {
            return laptopPrice + laptopPrice * .20;// Change calculation Logic (another different reason )---Violation of SRP
        }
    }

    public class GiftItem : IComputer
    { 
        public string GetComputerDescripution()
        {
            return "You get a Pad as gift item enjoy";
        }

        public string GetColor()
        {
            return "Choose any color from black and white";
        }

        public double GetPrice()//No Use here but forcefully implemented ------ Violation of ISP
        {
            return 0;
        }

        public double GetPriceAfterTax()//No Use here but forcefully implemented ------ Violation of ISP
        {
            return 0;
        }
    }

    public class ComputerShop
    {
        public string GetMyComputerDescripution(IComputer computer)
        {
            return computer.GetComputerDescripution();
        }

        public string GetMyComputerColor(IComputer computer)
        {
            return computer.GetColor();
        }

        public double GetMyComputerPriceAfterTax(IComputer computer)
        {
            return computer.GetPriceAfterTax();
        }

        public string WhatIsTheColorOfGiftItem(IComputer gift)
        {
            return gift.GetColor();
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var computerShop = new ComputerShop();
            computerShop.GetMyComputerDescripution(new Desktop());
            computerShop.GetMyComputerColor(new Desktop());
            computerShop.GetMyComputerPriceAfterTax(new Desktop());
            computerShop.WhatIsTheColorOfGiftItem(new GiftItem());
        }
    }
}

 If you run the code it will execute fine.

But our code violates the SRP and ISP rules.How our code violate the SRP and ISP ? Did you notice ? No,still confuse. No problem,I'll explain.

How we violate SRP rule?

 "There should never be more than one reason for a class to change."---Robert Martin, SRP paper linked from The Principles of OOD

 Look at the functions below,thoes are the culprits.It's a calculation logic but it's in Desktop, Laptop class.

So if the price has changed then we need to change the class for that and if the calculation logic has changed we need to change it again. So we need to change the same class for two different reasons. Because class has multiple responsibility(This is the violation). 

        public double GetPriceAfterTax()
        {
            return desktopPrice + desktopPrice * .20;
        }
        public double GetPriceAfterTax()
        {
            return laptopPrice + laptopPrice * .20;
        }

Now let's see how violation take place on Desktop Class for SRP.

public class Desktop : IComputerDescription
{
    double desktopPrice = 130;     // Change Price (one reason)
    public string GetDescription()
    {
        return " You get a Desktop";
    }     
    public string GetColor()
    {
        return " Color is White";
    }
    public double GetPrice()
    {
        return desktopPrice;
    }
    public double CalculatePriceAfterTax()
    // Change calculation Logic (another different reason )
    {
        return desktopPrice + 
           (desktopPrice * .20)+1; // Violation of SRP
    }
}

 How we violate ISP rule?

"Clients should not be forced to depend on the interface that they do not use"---Robert Martin, SRP paper linked from The Principles of OOD

 Yes we did it by implementing the interface for GiftItem.Because gift items are free,no calculation logic needed,but we forcefully implement it(violation of ISP).

    public class GiftItem : IComputer
    { 
        public string GetComputerDescripution()
        {
            return "You get a Pad as gift item enjoy";
        }

        public string GetColor()
        {
            return "Choose any color from black and white";
        }

        public double GetPrice()//No Use here but forcefully implemented ------ Violation of ISP
        {
            return 0;
        }

        public double GetPriceAfterTax()//No Use here but forcefully implemented ------ Violation of ISP
        {
            return 0;
        }
    }

Did you notice the violation? Yes,we modify the class for two different reasons and unnecessarily implement the interface motheds GetPrice and GetPriceAfterTax on GiftItem class.
Now let's follow the SRP and ISP rules and implement a new structure.

If we can give responsibility of calculation logic to another class and remove the unuse implementation methods of interface on GiftItem class then we can achieve our goal.

Look at the code below how we give responsibility of calculation logic to another class.(this helps to implement SRP rules easily)

    //New Class introduce to take the responsibility of calculation price
    public class CalculateComputerPrice:ICalculatePriceAfterTax 
    {
        public double GetPriceAfterTax(IComputerPrice computerPrice)
        {
            return computerPrice.GetPrice() + computerPrice.GetPrice() * 0.20;
        }
    }

Look at the code below how we break down the interface.(this helps to implement ISP rules easily)

    public interface IComputerDescription
    {
        string GetDescription();
        string GetColor();
    }
    public interface IComputerPrice
    {
        double GetPrice();
    }
    public interface ICalculatePriceAfterTax
    {
        double GetPriceAfterTax(IComputerPrice computerPrice);
    }

 Now the implementation also going to be changed right.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SRISObey
{
    public interface IComputerDescription
    {
        string GetDescription();
        string GetColor();
    }
    public interface IComputerPrice
    {
        double GetPrice();
    }
    public interface ICalculatePriceAfterTax
    {
        double GetPriceAfterTax(IComputerPrice computerPrice);
    }
    //----
    public class Desktop:IComputerDescription,IComputerPrice
    {
        private double desktopPrice = 2300;

        public string GetDescription()
        {
            return "You get a desktop";
        }

        public string GetColor()
        {
            return "Color is white";
        }

        public double GetPrice()
        {
            return desktopPrice;
        }
    }

    public class Laptop : IComputerDescription, IComputerPrice
    {
        private double laptopPrice = 5000;

        public string GetDescription()
        {
            return "You get a laptop";
        }

        public string GetColor()
        {
            return "Color is black";
        }

        public double GetPrice()
        {
            return laptopPrice;
        }
    }

    public class GiftItem : IComputerDescription
    {
        public string GetDescription()
        {
            return "You get a Pad as gift item enjoy";
        }

        public string GetColor()
        {
            return "Choose any color from black and white";
        }
        //----IS
    }
    
    //New Class introduce to take the responsibility of calculation price
    public class CalculateComputerPrice:ICalculatePriceAfterTax 
    {
        public double GetPriceAfterTax(IComputerPrice computerPrice)
        {
            return computerPrice.GetPrice() + computerPrice.GetPrice() * 0.20;
        }
    }

    public class ComputerShop
    {
        public string GetMyComputerDescripution(IComputerDescription computerDescritption)
        {
            return computerDescritption.GetDescription();
        }
        public string GetMyComputerColor(IComputerDescription computerDescription)
        {
            return computerDescription.GetColor();
        }
        public double GetMyComputerPrice(IComputerPrice computerPrice)
        {
            return computerPrice.GetPrice();
        }
        public double GetMyComputerPriceAfterTax(IComputerPrice computerPrice, ICalculatePriceAfterTax calculatePriceAfterTax)
        {
            return calculatePriceAfterTax.GetPriceAfterTax(computerPrice);   
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var computerShop = new ComputerShop();
            computerShop.GetMyComputerDescripution(new Desktop());
            computerShop.GetMyComputerColor(new Laptop());
            computerShop.GetMyComputerPrice(new Desktop());
            computerShop.GetMyComputerPriceAfterTax(new Desktop(), new CalculateComputerPrice());
        }
    }
}

So now our code satisfy the both SRP and ISP rules. 

 

相關文章