Interface segregation principle (ISP)

In software development, the Interface Segregation Principle (ISP) is a principle that states that clients should not be forced to implement interfaces they do not use. In other words, it suggests that interfaces should be split up into smaller, more specific interfaces so that clients only need to implement the methods they are actually using. In this article, we will explore the ISP principle by using a coffee machine as an example, and we will see how it can be applied to create a coffee with cream and without cream.

Some cream with your coffee

Imagine we have a coffee machine that has several features, such as grinding coffee beans, boiling water, and pouring coffee. To control the coffee machine, we would create an interface called ICoffee, which would have methods like GrindCoffeeBeans(), BoilWater(), PourCoffee() and AddCream().

interface ICoffee {
    void GrindCoffeeBeans();
    void BoilWater();
    void PourCoffee();
    void AddCream();
}

However, imagine that we have a client that only wants to use the coffee machine to make coffee without cream. This client would be forced to implement the entire ICoffee interface, even though it only needs the GrindCoffeeBeans(), BoilWater(), and PourCoffee() methods. This is not ideal, as it would introduce unnecessary complexity and make the client’s code harder to understand and maintain.

To address this problem, we can use the ISP principle and split the ICoffee interface into smaller, more specific interfaces. For example, we could create an interface called ICoffeeMaker that has only the methods GrindCoffeeBeans(), BoilWater(), and PourCoffee() and another interface called ICreamAdder that has only the AddCream() method. This way, the client that only wants to make coffee without cream can implement the ICoffeeMaker interface, and the client that wants to make coffee with cream can implement the ICreamAdder interface.

Benefits of ISP

By following the ISP principle, we can create interfaces that are more focused and specific. This makes it easier for clients to understand what methods they need to implement, and it reduces the chance of introducing errors into their code. Additionally, it makes the code more maintainable, as changes to one interface will not affect clients that do not use that interface.

interface ICoffeeMaker {
    void GrindCoffeeBeans();
    void BoilWater();
    void PourCoffee();
}

interface ICreamAdder {
    void AddCream();
}

class CoffeeMaker : ICoffeeMaker {
    public void GrindCoffeeBeans() { /* implementation */ }
    public void BoilWater() { /* implementation */ }
    public void PourCoffee() { /* implementation */ }
}

class CreamAdder : ICreamAdder {
    public void AddCream() { /* implementation */ }
}

class CoffeeClient {
    ICoffeeMaker coffeeMaker;
    ICreamAdder creamAdder;

    public CoffeeClient(ICoffeeMaker maker, ICreamAdder adder) {
        coffeeMaker = maker;
        creamAdder = adder;
    }

    public void MakeCoffee() {
        if(creamAdder != null)
        {
          coffeeMaker.GrindCoffeeBeans();
          coffeeMaker.BoilWater();
          coffeeMaker.PourCoffee();
        }
        
        if(creamAdder != null)
        {
          creamAdder.AddCream();
        }
    }
}

In this example, the CoffeeClient can make a coffee with cream or without it by instantiating the ICoffeeMaker or ICreamAdder interface or both.

So, here is how the code could be used:

class Program {
    static void Main(string[] args) {
        
        //Making a coffee without cream
        CoffeeMaker coffeeMaker = new CoffeeMaker();
        CoffeeClient client = new CoffeeClient(coffeeMaker,null);
        client.MakeCoffee();
        
        //Making a coffee with cream
        CreamAdder creamAdder = new CreamAdder();
        client = new CoffeeClient(coffeeMaker, creamAdder);
        client.MakeCoffee();
    }
}

The client can make a coffee without cream by passing null to the ICreamAdder interface and make a coffee with cream by passing the CreamAdder class to the ICreamAdder interface.

By following the ISP principle, we have created interfaces that are more specific and focused, making it easier for clients to understand what methods they need to implement, and reducing the chance of introducing errors into their code. Additionally, it makes the code more maintainable, as changes to one interface will not affect clients that do not use that interface.

Conclusion

Thanks to the Interface Segregation Principle (ISP), we can improve the design of our application by making interfaces more specific and focused. This makes the code more understandable, maintainable, and error-free, and it allows for more flexibility in creating different variations of the same product, in our case the coffee with cream or without cream.

Une réponse à “Interface segregation principle (ISP)”

  1. Avatar de Dario
    Dario

    Hi Romain, I tried to run the code from this example and I think there’s a little mistake in it:

    If you try to make a coffee without cream using cour code
    //Making a coffee without cream
    CoffeeMaker coffeeMaker = new CoffeeMaker();
    CoffeeClient client = new CoffeeClient(coffeeMaker,null);
    client.MakeCoffee();

    Then the MakeCoffee() method will throw a NullReferenceException because its creamAdder is null.

    Can you check if I’m right?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *