Montag, 5. Mai 2014

Das Single Choice Prinzip



Name, Kurzform
Single Choice Prinzip
Synonyme
SCP
Beschreibung
Immer wenn ein Software-System eine Anzahl von Alternativen anbietet, sollte genau ein Modul die vollständige Menge der Wahlmöglichkeiten kennen.“ [Mayer1998]
Erläuterung
Das Single Choice Prinzip ist eines von Bertrand Meyers fünf Modulprinzipien. Es setzt das Konstrukt einer Fallunterscheidung in Zusammenhang mit der Modulbildung. Als Fallunterscheidung kommen dabei ganz unterschiedliche syntaktische Konstrukte in Betracht:
  • if / else if-Kaskaken
  • switch-Statements
  • Entscheidungstabellen
  • usw.
Je nach Programmiersprache stehen unterschiedliche Konstrukte in der Sprachsyntax zur Verfügung. Weitergehende Möglichkeiten wie z. B. Hash-Tabellen, die Selektion über Pattern-Matching o. Ä. können zudem über Standardbibliotheken bereitstehen. All diesen Möglichkeiten ist gemeinsam, dass es eine Anzahl von Kriterien gibt, und dass jedem Kriterium ein bestimmter Ausführungszweig entspricht.

Wiederholt sich eine solche Fallunterscheidung in verschiedenen Modulen, so entsteht eine unnötige Verteilung dieses Wissens (Meyer nennt dies distribution of knowledge), die als Verstoß gegen das Geheimnisprinzip (information hiding) interpretiert werden kann. Zudem bedeutet die entstehende Redundanz einen Verstoß gegen „Don't Repeat Yourself“ / „Once and only once“.

Aus alldem ergibt sich für Meyer ein starkes Kriterium, die Fallunterscheidung in genau einem Modul zu realisieren. Es kann nicht verschwiegen werden, dass er dabei insbesondere einen bestimmten Mechanismus der Objektorientierung im Blick hat, nämlich den Einsatz von Polymorphie und dynamischer Bindung. Als Musterbeispiel für die Anwendung des Single Choice Prinzips wird diese Möglichkeit im Abschnitt „Beispiele“ näher beschrieben. Es muss jedoch ergänzt werden, dass das Prinzip auch andere Möglichkeiten zulässt.
Beispiel(e)
1) Polymorphie und dynamische Bindung:
Das Single Choice Prinzip wird von verschiedenen Autoren auf den Einsatz von Polymorphie und dynamischer Bindung reduziert. Diese stellen einen Basismechanismus zur Verfügung, der die bedingte Fallunterscheidung elegant verlagert. Die folgende Abbildung veranschaulicht dies:

Der Klient ruft lediglich die gewünschte Methode der Instanz auf. Polymorphie und dynamische Bindung sind die konzeptionellen Grundlagen dieser Möglichkeit.
 Supertyp obj;       // Deklaration der Instanz
 ...
 obj.doSomething();  // Implizite Verzweigung
Die Fallunterscheidung ist hier verschwunden. Sie wurde implizit an diejenige Stelle verlagert, welche entschieden hat, welchen Typ die Objektinstanz erhalten soll. Um diese Entscheidung zu realisieren, stehen nun verschiedene Möglichkeiten zur Verfügung. Solche Möglichkeiten sind z. B.:
  • Abstrakte Fabrik: Das Entwurfsmuster Abstrakte Fabrik bietet für bestimmte Konstellationen eine elegante Möglichkeit, die Fallunterscheidung an die Stelle der Objekterzeugung zu verlagern.
  • Objekt-Repository: Bei der Nutzung eines Objekt-Repositories werden alle Alternativen in Form von Objekten in einem Repository gespeichert. Alle Objekte leiten von einem gemeinsamen Supertyp ab. Der Zugriff erfolgt dann vom Klienten aus über einen Selektor (im einfachsten Fall eine Zeichenkette). Die Fallunterscheidung wird hier also nicht an die Stelle der Objekterzeugung verlagert, sondern an diejenige Stelle, welche die Zuordnung von Objekten zu Selektoren vornimmt. Die Wahl eines konkreten Selektors für den Zugriff könnte z. B. durch eine Benutzereingabe ermittelt worden sein.
  • Generische Programmierung: Ein häufiger Spezialfall des Einsatzes von Polymorphie und dynamischer Bindung besteht im Einsatz generischer Programmiertechniken, wie sie in C++ über Templates oder Java über Generics zur Verfügung stehen.
Zu erwähnen bleibt, dass die Lösung des Single Choice Prinzips über Polymorphie und dynamische Bindung auch eine einfache Anwendung des Open-Closed-Prinzips erlaubt: Durch Hinzufügen neuer Subtypen und entsprechende Aufnahme in den Erzeugungs- oder Auswahl-Mechanismus können neue Verzweigungen eingeführt werden, ohne den Code in den diversen Klienten verändern zu müssen.

Nachteilig an der Lösung über Polymorphie und dynamische Bindung ist, dass sich die Bildung einer Klassenhierarchie nicht in allen Fällen anbietet. Was beispielsweise, wenn sich der betreffende Quellcode bereits in eine vorhandene Klassenhierarchie einbetten muss oder ein anderes Kriterium zur Bildung der Klassenhierarchie als bedeutsamer erscheint? In einigen Fällen können dann noch Rollen-Interfaces aushelfen, aber auch diese Möglichkeit könnte aus verschiedenen Gründen verwehrt sein.

2) First-Class-Funktionen / Zeiger auf Funktionen:
Wenn sich der Mechanismus über Polymorphie und dynamische Bindung nicht anbietet, so können in einigen Programmiersprachen First-Class-Funktionen (oder alternativ – und umständlicher – Zeiger auf Funktionen) behilflich sein. Es muss wiederum ein Selektionsmechanismus bereitstehen, wie z. B. der Zugriff über die Schlüssel einer Map oder eine andere Form von Funktions-Repository. Die Fallunterscheidung wird hier in dasjenige Modul verlagert, welches das Funktions-Repository aufbaut.

3) Sonstige Ideen
  • Einige Interpreter-Sprachen stellen die Möglichkeit zur Evaluierung von Ausdrücken zur Verfügung, welche erst zur Laufzeit zusammengesetzt werden. Hier könnte sich die bedingte Erzeugung von Ausdrücken in einem dafür verantwortlichen Modul anbieten. Diese Variante öffnet zudem die Gelegenheit zur Verwaltung von Ausdrücken in einer Datenbank.
  • In einigen Systemen ist die Nutzung wiederverwendbarer Entscheidungstabellen möglich. Als Beispiele seien das SAP NetWeaver Decision Service Management oder Entscheidungstabellen im Regelmodellierungs-Werkzeug Visual Rules genannt.
Historie
  • Dass Redundanzen im Quellcode vermieden werden sollten, ist sicher schon seit Jahrzehnten bekannt.
  • Die explizite Formulierung des Prinzips in Bezug auf Fallunterscheidungen wurde erstmalig von Betrand Meyer in Object-Oriented Software Construction 2nd Edition vorgestellt [Meyer1998].
  • Eine Verallgemeinerung des Prinzips in Bezug auf die distribution of knowledge stellt „Don't Repeat Yourself“ dar, dass 1999 im Buch The Pragmatic Programmer [Hunt1999] vorgestellt wurde.
Art des Prinzips
  • Grundlegende Einteilung: Produkt.
  • Technologiebezug: Übergreifend.
  • Entwurfsgüte: Strukturprinzip.
  • Handlungsbezug: Erleichterung von Änderung, Wiederverwendung und Überprüfung.
  • Kognitionsbezug: Konsistenz.
(Siehe Kategorisierung der Prinzipien.)
Grad der formalen Spezifikation
Hoch. Redundanzen im Quellcode können mit zahlreichen Werkzeugen zuverlässig ermittelt werden.
Vorteile
  • Einfach verständlich.
  • Gut anwendbares, zweifelsfreies Modularisierungs-Kriterium.
  • Positiver Einfluss auf zahlreiche Qualitätsmerkmale.
Nachteile
  • Die Fallunterscheidung verschwindet aus den Klienten und ist daher an den Verwendungsstellen nicht mehr explizit erkennbar. Es handelt sich hier (aus der Sicht der Klienten) um einen Verstoß gegen das Lokalitätsprinzip.
  • Wenn eine Lösung über Polymorphie nicht verfügbar ist, kann die Verständlichkeit des Codes erschwert werden (z. B. durch Zeiger auf Funktionen).
Übergeordnete Prinzipien
  • Geheimnisprinzip
  • Open-Closed-Prinzip
  • Don't Repeat Yourself
Abgleitete Prinzipien
-
Qualitätsmerkmale
(+) Änderbarkeit, (+) Wiederverwendbarkeit,
(+) Wartbarkeit, (+) Testbarkeit,
(-) Verständlichkeit

Quellen

[Hunt1999] – The Pragmatic Programmer. From Journeyman to Master, Andrew Hunt, David Thomas, Ward Cunningham (1999)
[Mayer1998] - Objekt-oriented Software Construction 2nd Edition, Bertrand Meyer (1999)