Interface Segregation Principle

Die Kernaussage des Interface Segregation Principles – ISP (dt. Schnittstellenaufteilungsprinzip ) ist, dass Schnittstellen keine Methoden enthalten sollen, die von implementierenden Klassen nicht verwendet werden (können).

Eine einsame Katze

Ausgangspunkt ist die Klasse Katze. Sie enthält Methoden zum Essen, Miauen und zerstören von teuren Dingen, das was Katzen nun mal so anstellen.

UML


Cat Class

C#

    public class Cat
    {
        public void Eat()
        {
            //eat some nice fish
        }

        public void MakeSound()
        {
            //MEOW!
        }

        public void ThrowDown(object expensiveObject)
        {
            //Destroy object by accelerating it towards the ground
        }
    }

JAVA

public class Cat {
    public void Eat() {
        //eat some nice fish
    }

    public void MakeSound() {
        //MEOW!
    }

    public void ThrowDown(Object expensiveObject) {
        //Destroy object by accelerating it towards the ground
    }
}

Die Katze wird zum Tier

Sieht zunächst ganz vernünftig aus. Würde nun jedoch ein Hund hinzugefügt werden, der dieselben Dinge wie eine Katze tut, ist anzuraten, das Verhalten in ein Interface zu kapseln. So können die Tiere ganz unabhängig von Ihrer Art angesprochen werden und z.B. mit einer Simple Factory erstellt werden. Da Hunde seltener etwas herunter werfen sondern mehr damit beschäftigt sind, Dinge zu zerkauen, wird die entsprechende Methode in Destroy umbenannt.

UML


Animal Interface

C#

    public interface IAnimal 
    {
        void Destroy(object expensiveObject);
        void Eat();
        void MakeSound();        
    }

    public class Cat : IAnimal
    {
        public void Destroy(object expensiveObject)
        {
            Console.WriteLine($"*Kitty kicks {expensiveObject.ToString()} off the table*");
        }

        public void Eat()
        {
            Console.WriteLine("*Fluffy kitty is eating a fish*");
        }

        public void MakeSound()
        {
            Console.WriteLine("Meow!");
        }
    }

    public class Dog : IAnimal
    {
        public void Destroy(object expensiveObject)
        {
            Console.WriteLine($"*Doggy munshes on {expensiveObject.ToString()}*");
        }

        public void Eat()
        {
            Console.WriteLine("*The dog is chomping on a bone*");
        }

        public void MakeSound()
        {
            Console.WriteLine("Woof Woof!");
        }
    }

JAVA

interface IAnimal {
	public void Destroy(Object expensiveObject);
	public void Eat();
	public void MakeSound();		
}

public class Cat implements IAnimal {
	public void Destroy(Object expensiveObject) {
		System.out.println("*Kitty kicks " + expensiveObject.toString()+ " off the table*");
	}

	public void Eat() {
		System.out.println("*Fluffy kitty is eating a fish*");
	}

	public void MakeSound() {
		System.out.println("Meow!");	
	}
}

public class Dog implements IAnimal {

	public void Destroy(Object expensiveObject) {
		System.out.println("*Doggy munshes on " + expensiveObject.toString() + "*");
	}

	public void Eat() {
		System.out.println("*The dog is chomping on a bone*");
	}

	public void MakeSound() {
		System.out.println("Woof Woof!");
	}
}

Rise of the Robocat

Soweit so gut. Da der technologische Fortschritt jedoch auch vor Haustieren keinen Halt macht, schaffen sich immer mehr Menschen eine Roboterkatze an die nun auch abgebildet werden soll. Wir leiten uns also eine Roboterkatze von IAnimal ab und implementieren die Methoden:

C#

    pbulic class Robocat : IAnimal
    {
        public void Destroy(object expensiveObject)
        {
            Console.WriteLine($"*fires laz0r at {expensiveObject.ToString()}*");
        }

        public void Eat()
        {
            throw new NotImplementedException();
        }

        public void MakeSound()
        {
            Console.WriteLine("I will be meow!");
        }
    }

JAVA

public class Robocat implements IAnimal {

	public void Destroy(Object expensiveObject) {
		System.out.println("*fires laz0r at " + expensiveObject.toString() + "*");		
	}

	public void Eat() {
		// TODO Auto-generated method stub		
	}

	public void MakeSound() {
		System.out.println("I will be meow!");
	}
}

Nun taucht jedoch ein Problem auf: Eine Roboterkatze muss, aufgrund ihrer hochentwickelten Natur, nichts essen. Die Schnittstelle kann also nicht korrekt implementiert werden ohne Kompromisse einzugehen. Im C# Beispiel wird beim Aufruf eine Ausnahme ausgelöst, das JAVA Äquivalent tut einfach nichts. Die Methode enthält lediglich ein Kommentar, dass es sich um eine automatisch generierte Methode handelt. An der Funktionsweise ändert sich dadurch nichts. Jedoch wird das Interface Segregation Principle verletzt, welches ja besagt, dass eine Schnittstelle keine Methoden enthalten soll, die nicht implementiert werden. Die Methode Eat() wird von der Roboterkatze jedoch nicht implementiert.

Die Aufteilung (Segregation) der Schnittstelle IAnimal ist folglich der nächste Schritt. Dafür wird eine neue Schnittstelle definiert die nur die Methode Eat() enthält. Als passenden Name wird ihr INeedFood verpasst. Hund, Katz und Robokatz werden entsprechend angepasst.

UML


Interface Segregation

C#

    interface IAnimal
    {
        void Destroy(object expensiveObject);
        void MakeSound();
    }

    interface INeedsFood
    {
        void Eat();
    }

    class Cat : IAnimal, INeedsFood
    {
        public void Destroy(object expensiveObject)
        {
            Console.WriteLine($"*The cat kicks {expensiveObject.ToString()} from the table");
        }

        public void Eat()
        {
            Console.WriteLine("*Fluffy kitty is eating a fish*");
        }

        public void MakeSound()
        {
            Console.WriteLine("Meow!");
        }
    }

    class Dog : IAnimal, INeedsFood
    {
        public void Destroy(object expensiveObject)
        {
            Console.WriteLine($"*Doggy munshes on {expensiveObject.ToString()}*");
        }

        public void Eat()
        {
            Console.WriteLine("*The dog is chomping on a bone*");
        }

        public void MakeSound()
        {
            Console.WriteLine("Woof Woof!");
        }
    }

    class Robocat : IAnimal
    {
        public void Destroy(object expensiveObject)
        {
            Console.WriteLine($"*fires laz0r at {expensiveObject.ToString()}*");
        }

        public void MakeSound()
        {
            Console.WriteLine("I will be meow!");
        }
    }

JAVA

public interface IAnimal {
	public void Destroy(Object expensiveObject);
	public void MakeSound();		
}

public interface INeedFood {
	public void Eat();
}

public class Cat implements IAnimal, INeedFood {

	public void Destroy(Object expensiveObject) {
		System.out.println("The cat kicks " + expensiveObject.toString()+ " from the table");
	}

	public void Eat() {
		System.out.println("*Fluffy kitty is eating a fish*");
	}

	public void MakeSound() {
		System.out.println("Meow!");	
	}
}

public class Dog implements IAnimal, INeedFood {

	public void Destroy(Object expensiveObject) {
		System.out.println("*Doggy munshes on " + expensiveObject.toString() + "*");
	}

	public void Eat() {
		System.out.println("*The dog is chomping on a bone*");
	}

	public void MakeSound() {
		System.out.println("Woof Woof!");
	}
}

public class Robocat implements IAnimal {

	public void Destroy(Object expensiveObject) {
		System.out.println("*fires laz0r at " + expensiveObject.toString() + "*");		
	}
	
	public void MakeSound() {
		System.out.println("I will be meow!");
	}
}

Damit ist dem ISP genüge getan und alle Tierchen sind glücklich mit Methoden versorgt. Abschließend könnte noch ein Interface hinzugefügt werden um der Robokatze zu erlauben, ihre Energiespeicher aufzufüllen oder man wechselt einfach die Batterien

Als Grundgedanken wäre zu behalten: Sobald eine oder mehrere Methoden eines Interfaces nicht implementiert werden (können), liegt ein Verstoß gegen das ISP vor und das Interface sollte entsprechend geteilt werden.

Leave a Reply

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>