Tell, Don‘t Ask! 
Erstellung aussagekräftiger Schnittstellen 
(c) Carsten Hetzel, 2014
Tell, Don't Ask - Erstellung 
aussagekräftiger Schnittstellen 
 Es gibt eine Vielzahl von Programmierprinzipien im Bereic...
Aufgabe: Welche 
Programmierprinzipien 
kennen Sie noch? 
(c) Carsten Hetzel, 2014
Programmierprinzipien 
 - Information Hiding 
- Least Knowledge Principle 
- Lose Kopplung, starke Bindung (Kohärenz) 
- ...
Ein einfaches Beispiel 
(c) Carsten Hetzel, 2014
Ein einfaches Beispiel 
 Eine der häufigsten Anwendungen dieses Prinzips ist die 
Kapselung von Statusabfragen. 
Welche N...
Ein einfaches Beispiel 
if ($myClass->getStatus() == 10) { 
// Do something ... 
} else { 
// Do something else 
} 
 Die ...
Ein einfaches Beispiel 
 Was ist mit folgender Änderung? 
if ($myClass->getStatus() == MyClass::PREMIUM_USER) { 
// Do so...
Ein einfaches Beispiel 
 if ($myClass->getStatus() == MyClass::PREMIUM_USER) { 
// Do something ... 
} else { 
// Do some...
Ein einfaches Beispiel 
 Wenn wir die Abfrage in eine Methode verlagern (siehe 
Abfragemethode/QueryMethod), dann wird de...
Ein komplexeres Beispiel 
(c) Carsten Hetzel, 2014
Ein komplexeres Beispiel 
 Stellen Sie sich folgende Situation vor: Sie verwenden ein Framework, welches Ihnen die 
Verar...
Ein komplexeres Beispiel 
 Einer Form können Sie also verschiedene Elemente wie z.B. 
ein Eingabefeld oder eine Checkbox ...
Ein komplexeres Beispiel 
 class ContactForm extends Form 
{ 
const KEY_NAME = 'name'; 
const KEY_AGE = 'age'; 
const KEY...
Ein komplexeres Beispiel 
 Der Controller sieht etwa folgendermaßen aus: 
 class UglyContactController 
{ 
// ... 
publi...
Ein komplexeres Beispiel 
 Schlecht lesbar 
 Nicht wiederverwendbar 
 Fehleranfällig 
 ... 
16 (c) Carsten Hetzel, 201...
Konkretisierungen 
können auch die API 
erweitern 
(c) Carsten Hetzel, 2014
Konkretisierungen können auch die API 
erweitern 
 Da wir sowieso eine separate ContactForm-Klasse haben, können wir uns ...
Konkretisierungen können auch die API 
erweitern 
 Diese Version des Controllers ist schon deutlich einfacher zu 
lesen u...
Vermitteln der Intention 
(c) Carsten Hetzel, 2014
Vermitteln der Intention 
 Was soll denn an der Stelle passieren, wenn die 
Eingabedaten des Formulars korrekt waren und ...
Vermitteln der Intention 
 class BetterContactController 
{ 
// ... 
public function createNewContact() 
{ 
// ... 
$form...
Vermitteln der Intention 
 Inzwischen sieht der Code leserlich aus. Aber will man 
wirklich explizit ausdrücken, dass die...
Vermitteln der Intention 
 class ContactController 
{ 
// ... 
public function createNewContact() 
{ 
// ... 
$contact = ...
Vermitteln der Intention 
 Im Grunde hat sich nur folgendes geändert: Die Methode 
"fillContact()" muss nicht mehr expliz...
Von verstreuter 
Business-Logik 
zu Value-Objects 
(c) Carsten Hetzel, 2014
Von verstreuter Business-Logik 
zu Value-Objects 
 Angenommen Sie sollen ein System von Behältern 
beschreiben, welche Fl...
Von verstreuter Business-Logik 
zu Value-Objects 
 class Bottle 
{ 
private $liters; 
// ... 
} 
class AppController 
{ 
...
Von verstreuter Business-Logik 
zu Value-Objects 
 Welche Probleme hat diese Lösung? 
 Was wollen wir eigentlich machen?...
Von verstreuter Business-Logik 
zu Value-Objects 
 class Transfusion 
{ 
private $liters; 
public function __construct($l...
Von verstreuter Business-Logik 
zu Value-Objects 
 Entsprechend sieht die Bottle-Klasse folgendermaßen aus: 
 class Bott...
Von verstreuter Business-Logik 
zu Value-Objects 
 Und abschließend der Controller: 
 class AppController 
{ 
public fun...
Aufgabe: Markierung 
von "besonderen" 
Rechnungsposten 
(c) Carsten Hetzel, 2014
Aufgabe: Markierung von "besonderen" 
Rechnungsposten 
 Auf dem Bildschirm sollen alle Posten einer Rechnung 
aufgelistet...
Vielen Dank für Ihre Aufmerksamkeit! 
35 (c) Carsten Hetzel, 2014
Nächste SlideShare
Wird geladen in …5
×

Tell, Don't Ask - Aussagekräftige Schnittstellen

555 Aufrufe

Veröffentlicht am

Tell, Don't Ask! Ein Prinzip der objektorientierten Softwareentwicklung, das hilft, aussagekräftige Schnittstellen zu verwenden.

Veröffentlicht in: Präsentationen & Vorträge
0 Kommentare
0 Gefällt mir
Statistik
Notizen
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Keine Downloads
Aufrufe
Aufrufe insgesamt
555
Auf SlideShare
0
Aus Einbettungen
0
Anzahl an Einbettungen
2
Aktionen
Geteilt
0
Downloads
4
Kommentare
0
Gefällt mir
0
Einbettungen 0
Keine Einbettungen

Keine Notizen für die Folie

Tell, Don't Ask - Aussagekräftige Schnittstellen

  1. 1. Tell, Don‘t Ask! Erstellung aussagekräftiger Schnittstellen (c) Carsten Hetzel, 2014
  2. 2. Tell, Don't Ask - Erstellung aussagekräftiger Schnittstellen  Es gibt eine Vielzahl von Programmierprinzipien im Bereich objektorientiert Softwareentwicklung aber keines hilft wie "Tell, Don't Ask!" dabei, aussagekräftige Schnittstellen für Klassen zu erstellen.  "Tell, Don't Ask!" (TDA) besagt, dass wir statt Objekte nach ihrem Zustand zu fragen und diesen anschließend auszuwerten, die Klasse selber aussagekräftige Methoden anbieten lassen sollen, die die Auswertung oder Verarbeitung übernehmen. 2 (c) Carsten Hetzel, 2014
  3. 3. Aufgabe: Welche Programmierprinzipien kennen Sie noch? (c) Carsten Hetzel, 2014
  4. 4. Programmierprinzipien  - Information Hiding - Least Knowledge Principle - Lose Kopplung, starke Bindung (Kohärenz) - Die SOLID-Prinzipien - Programmiere gegen Interfaces, nicht gegen Implementierungen -Verwende Abhängigkeiten zu Abstraktionen, nicht zu konkreten Klassen - Don't Repeat Yourself (DRY) - Kapsle variable Bestandteile (Strategie-Pattern) - Bevorzuge Komposition statt Vererbung - Das "Hollywood Prinzip" (Don't call us, we call you!) - Strebe lose gekoppelte Systeme an - Keep It Simple and Stupid (KISS) 4 (c) Carsten Hetzel, 2014
  5. 5. Ein einfaches Beispiel (c) Carsten Hetzel, 2014
  6. 6. Ein einfaches Beispiel  Eine der häufigsten Anwendungen dieses Prinzips ist die Kapselung von Statusabfragen. Welche Nachteile hat z.B. folgender Code:  if ($myClass->getStatus() == 10) { // Do something ... } else { // Do something else } 6 (c) Carsten Hetzel, 2014
  7. 7. Ein einfaches Beispiel if ($myClass->getStatus() == 10) { // Do something ... } else { // Do something else }  Die Bedeutung des Werts 10 ist nicht klar - der Code ist also undurchsichtig (Opazität/Opacity hoch)  Wenn wir diese Abfrage häufiger benötigen, verteilt sie sich über den gesamten Client Code  Sollte sich Abfrage ändern (z.B. ein weiterer Status zu beachten sein), müssen wir viele Stellen anpassen 7 (c) Carsten Hetzel, 2014
  8. 8. Ein einfaches Beispiel  Was ist mit folgender Änderung? if ($myClass->getStatus() == MyClass::PREMIUM_USER) { // Do something ... } else { // Do something else } 8 (c) Carsten Hetzel, 2014
  9. 9. Ein einfaches Beispiel  if ($myClass->getStatus() == MyClass::PREMIUM_USER) { // Do something ... } else { // Do something else }  Jetzt verstehen wir zwar die Bedeutung der Abfrage besser, aber die anderen Probleme bestehen weiterhin.  Schlimmer noch: Wir haben die Klasse um Elemente (Konstanten) erweitert, die dazu verleiten wenn nicht sogar geradezu rechtfertigen weitere Bedingungen im Client Code zu fromulieren. 9 (c) Carsten Hetzel, 2014
  10. 10. Ein einfaches Beispiel  Wenn wir die Abfrage in eine Methode verlagern (siehe Abfragemethode/QueryMethod), dann wird der Code kompakt und aussagekräftig: if ($myClass->isPremiumUser()) { // Do something ... } else { // Do something else }  Sogar eine Veränderung der Bedingung kann nun ohne Anpassung des Client Codes vorgenommen werden. 10 (c) Carsten Hetzel, 2014
  11. 11. Ein komplexeres Beispiel (c) Carsten Hetzel, 2014
  12. 12. Ein komplexeres Beispiel  Stellen Sie sich folgende Situation vor: Sie verwenden ein Framework, welches Ihnen die Verarbeitung von Formularen erlaubt. Diese Klasse sieht etwa wie folgt aus: class Form { // ... public function getValue($key) { // ... return $value; } public function addFormElement($key, FormElement $element) { // ... } } 12 (c) Carsten Hetzel, 2014
  13. 13. Ein komplexeres Beispiel  Einer Form können Sie also verschiedene Elemente wie z.B. ein Eingabefeld oder eine Checkbox hinzufügen.  Um ihre Dialoge wieder verwenden zu können entscheiden Sie sich konkrete Ableitungen dieser Klasse zu erstellen, z.B. ein Kontaktformular, um von neuen Kunden Kontaktdaten aufzunehmen: 13 (c) Carsten Hetzel, 2014
  14. 14. Ein komplexeres Beispiel  class ContactForm extends Form { const KEY_NAME = 'name'; const KEY_AGE = 'age'; const KEY_EMAIL = 'email'; // ... public function __construct() { $nameInput = $this->createNameInputField(); $this->addFormElement(self::KEY_NAME, $nameInput); $ageInput = $this->createAgeInputField(); $this->addFormElement(self::KEY_AGE, $ageInput); // ... } }  Um Fehler beim Zugriff auf die einzelnen Form-Felder zu vermeiden bietet die Klasse Konstanten an, welche dann beim Auslesen verwendet werden können. 14 (c) Carsten Hetzel, 2014
  15. 15. Ein komplexeres Beispiel  Der Controller sieht etwa folgendermaßen aus:  class UglyContactController { // ... public function createNewContact() { // ... $form = new ContactForm(); // ... if ($form->isValid()) { $contact = new Contact(); $contact->setName($form->getValue(ContactForm::KEY_NAME)); $contact->setAge($form->getValue(ContactForm::KEY_AGE)); $contact->setEMail($form->getValue(ContactForm::KEY_AGE)); $contact->save(); } else { // ... } // ... } }  Was halten Sie von dieser Lösung? Welche Probleme sehen Sie? 15 (c) Carsten Hetzel, 2014
  16. 16. Ein komplexeres Beispiel  Schlecht lesbar  Nicht wiederverwendbar  Fehleranfällig  ... 16 (c) Carsten Hetzel, 2014
  17. 17. Konkretisierungen können auch die API erweitern (c) Carsten Hetzel, 2014
  18. 18. Konkretisierungen können auch die API erweitern  Da wir sowieso eine separate ContactForm-Klasse haben, können wir uns die Verwendung der Konstanten sparen, indem wir direkt entsprechende Getter anbieten:  class BetterContactController { public function createNewContact() { // ... $form = new ContactForm(); // ... if ($form->isValid()) { $contact = new Contact(); $contact->setName($form->getName()); $contact->setAge($form->getAge()); $contact->setEMail($form->getEMail()); $contact->save(); } else { // ... } // ... } } 18 (c) Carsten Hetzel, 2014
  19. 19. Konkretisierungen können auch die API erweitern  Diese Version des Controllers ist schon deutlich einfacher zu lesen und Fehler werden schneller entdeckt. Aber wenn wir die Klasse Contact erweitern - z.B. um eine Telefonnummer - dann müssen wir alle Controller nach solchen Code-Stellen durchsuchen und sie anpassen.  Verwenden wir die Klasse ContactForm, dann müssen wir auch den Code-Block mit den Setter-Aufrufen kopieren und haben einen klassischen Fall von Code-Duplizierung. 19 (c) Carsten Hetzel, 2014
  20. 20. Vermitteln der Intention (c) Carsten Hetzel, 2014
  21. 21. Vermitteln der Intention  Was soll denn an der Stelle passieren, wenn die Eingabedaten des Formulars korrekt waren und gespeichert werden können?  Im Bereich mit den Setter-Aufrufen sollen die Daten aus dem Formular in die Contact-Instanz übertragen werden. Eine Methode wie "transferToContact“ oder "fillContact()" verdeutlichen die Intention besser: 21 (c) Carsten Hetzel, 2014
  22. 22. Vermitteln der Intention  class BetterContactController { // ... public function createNewContact() { // ... $form = new ContactForm(); // ... if ($form->isValid()) { $contact = new Contact(); $form->fillContact($contact); $contact->save(); } else { // ... } // ... } } 22 (c) Carsten Hetzel, 2014
  23. 23. Vermitteln der Intention  Inzwischen sieht der Code leserlich aus. Aber will man wirklich explizit ausdrücken, dass die Daten aus dem Formular in den Kontakt übertragen werden?  Eigentlich würden wir doch erwarten, dass das automatisch passiert, oder? 23 (c) Carsten Hetzel, 2014
  24. 24. Vermitteln der Intention  class ContactController { // ... public function createNewContact() { // ... $contact = new Contact(); $form = new ContactForm($contact); // ... if ($form->isValid()) { $contact->save(); } else { // ... } // ... } } 24 (c) Carsten Hetzel, 2014
  25. 25. Vermitteln der Intention  Im Grunde hat sich nur folgendes geändert: Die Methode "fillContact()" muss nicht mehr explizit aufgerufen werden, sondern gehört quasi zum Lebenszyklus des Formulars. Der Client-Code ist schlanker geworden und entspricht eher unseren Erwartungen.  Darüber hinaus haben wir einen weiteren Vorteil erreicht: Die Validierung der Eingabedaten kann nun von Contact durchgeführt werden. Es ist viel sinnvoller die Validierung der Attribute eines Modells dem Modell zu überlassen, schließlich können Eingabedaten von allen möglichen Stellen des Systems und unterschiedlichsten Schnittstellen her kommen. 25 (c) Carsten Hetzel, 2014
  26. 26. Von verstreuter Business-Logik zu Value-Objects (c) Carsten Hetzel, 2014
  27. 27. Von verstreuter Business-Logik zu Value-Objects  Angenommen Sie sollen ein System von Behältern beschreiben, welche Flüssigkeiten aufnehmen können (z.B. Flaschen).  In diesem System kann es beliebig viele Behälter geben, die Menge an Wasser soll aber immer gleich bleiben.  Wasser kann immer nur zwischen zwei Behältern ausgetauscht werden.  Wie würden Sie dieses Problem lösen? 27 (c) Carsten Hetzel, 2014
  28. 28. Von verstreuter Business-Logik zu Value-Objects  class Bottle { private $liters; // ... } class AppController { public function transfuseAction($amount, $sourceBottleId, $targetBottleId) { // .. $source = $this->getBottleById($sourceBottleId); $target = $this->getBottleById($targetBottleId); $source->setLiters($source->getLiters() - $amount); $target->setLiters($target->getLiters() + $amount); } } 28 (c) Carsten Hetzel, 2014
  29. 29. Von verstreuter Business-Logik zu Value-Objects  Welche Probleme hat diese Lösung?  Was wollen wir eigentlich machen?  Finden wir eine sprechendere Lösung, die auch direkt die Rahmenbedingungen des Systems einhält: 29 (c) Carsten Hetzel, 2014
  30. 30. Von verstreuter Business-Logik zu Value-Objects  class Transfusion { private $liters; public function __construct($liters, Bottle $source, Bottle $target) { $source->reduceBy($this); $target->fillBy($this); } public function getLiters() { return $this->liters; } } 30 (c) Carsten Hetzel, 2014
  31. 31. Von verstreuter Business-Logik zu Value-Objects  Entsprechend sieht die Bottle-Klasse folgendermaßen aus:  class Bottle { // ... public function reduceBy(Transfusion $t) { $this->liters -= $t->getLiters(); } // ... } 31 (c) Carsten Hetzel, 2014
  32. 32. Von verstreuter Business-Logik zu Value-Objects  Und abschließend der Controller:  class AppController { public function transfuseAction($amount, $sourceBottleId, $targetBottleId) { // .. $source = $this->getBottleById($sourceBottleId); $target = $this->getBottleById($targetBottleId); $transfusion = new Transfusion($amount, $source, $target); } }  Welche Vorteile hat diese Lösung? 32 (c) Carsten Hetzel, 2014
  33. 33. Aufgabe: Markierung von "besonderen" Rechnungsposten (c) Carsten Hetzel, 2014
  34. 34. Aufgabe: Markierung von "besonderen" Rechnungsposten  Auf dem Bildschirm sollen alle Posten einer Rechnung aufgelistet und diejenigen Posten mit einem "X" markiert werden, die einen Wert von über 100€ haben. 34 (c) Carsten Hetzel, 2014
  35. 35. Vielen Dank für Ihre Aufmerksamkeit! 35 (c) Carsten Hetzel, 2014

×