Tuesday, December 1, 2009

Interface Segregation Principle

Interface Segregation Principle (DIP) is one of the five software design principles. What programming languages you know, you should know these five principles written below :
In this column i will explain Interface Segregation Principle.
Purpose / Reason
The reason to use ISP is to prevent getting interface "fat". Fat in this subject means that unused methods and properties are still exist and it makes developers to force implement them. For instance, when you need to change obese interface, you will likely have to modify all codes which are unrelated with the obese interface. Another term of fat interface is "polluted interface". Having an interface pollution may occur uninvited exception in runtime.  To prevent this disadvantage, we should separate fat interfaces.

Key Principle
Clients shouldn't be forced to depend upon interfaces that they don't use. *

Implementation
For this case we have two examples, one of them is bad, another is a good example. For these example, i use animal world. As you know each species has different abilities, such as flying, swimming. Some abilities are common, such as eating, sleeping. 
In order to clarify ISP, i starts with bad example.

Bad Example :

public interface IAnimal
{
    void Fly();
    void Eat();
    void See();
    void Swim();
}

public class Dog : IAnimal
{
// Fly couldn't be implemented
    public void Fly()
    {
        throw new NotImplementedException();
    }

    public void Eat()
    {
        Console.WriteLine("Dog is eating");
    }

    public void See()
    {
        Console.WriteLine("Dog is seeing");
    }
// Swim couldn't be implemented
    public void Swim()
    {
        throw new NotImplementedException();
    }
}

public class Bird : IAnimal
{
    public void Fly()
    {
        Console.WriteLine("Bird is flying");
    }

    public void Eat()
    {
        Console.WriteLine("Bird is eating");
    }

    public void See()
    {
        Console.WriteLine("Bird is seeing");
    }
// Swim couldn't be implemented
    public void Swim()
    {
        throw new NotImplementedException();
    }
}

public class Fish : IAnimal
{
// Fly couldn't be implemented
    public void Fly()
    {
        throw new NotImplementedException();
    }

    public void Eat()
    {
        Console.WriteLine("Fish is eating");
    }

    public void See()
    {
        Console.WriteLine("Fish is seeing");
    }

    public void Swim()
    {
        Console.WriteLine("Fish is swiming");
    }
}
For this example, IAnimal is an interface and Fish, Bird and Dog classes are derived from IAnimal interface. Although some methods are common in these classes, such as See(), Eat(); however, there are some methods that shouldn't be implemented. For Bird class Swim() method shouldn't be implemented by Bird. Because Bird cannot swim.


Suppose, for example, you need to add Walk() functionality, your interface IAnimal is getting fat. Also classes which implement IAnimal interface have method which is never used.

It seems work and can be built if we use this bad solution. But in runtime, exception can be occurred. 


Good Example : 
Here is the solution. We need to separate common used method and create new interface for these. Look at following code example :

public interface IAnimal
{
    void Eat();
    void See();
}
// interface for living in air
public interface IAnimalAir
{
    void Fly();
}
// interface for living in sea
public interface IAnimalSea
{
    void Swim();
}

public class Fish : IAnimal, IAnimalSea
{
    public void Eat()
    {
        Console.WriteLine("Fish is eating");
    }

    public void See()
    {
        Console.WriteLine("Fish is seeing");
    }

    public void Swim()
    {
        Console.WriteLine("Fish is swiming");
    }
}

public class Dog : IAnimal
{
    public void Eat()
    {
        Console.WriteLine("Dog is eating");
    }

    public void See()
    {
        Console.WriteLine("Dog is seeing");
    }
}

public class Bird : IAnimal, IAnimalAir
{
    public void Eat()
    {
        Console.WriteLine("Bird is eating");
    }

    public void See()
    {
        Console.WriteLine("Bird is seeing");
    }

    public void Fly()
    {
        Console.WriteLine("Bird is  flying");
    }
}
Summary I know, it's really difficult to forecast the interface which was written whether can be expanded in future. Don't concern about next requirements. Just create your interface layout, after that, if you need to add new functions into the interface, you should think about whether you will separate the interface into different interfaces.