SlideShare ist ein Scribd-Unternehmen logo
1 von 35
Downloaden Sie, um offline zu lesen
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 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
Aufgabe: Welche 
Programmierprinzipien 
kennen Sie noch? 
(c) Carsten Hetzel, 2014
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
Ein einfaches Beispiel 
(c) Carsten Hetzel, 2014
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
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
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
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
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
Ein komplexeres Beispiel 
(c) Carsten Hetzel, 2014
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
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
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
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
Ein komplexeres Beispiel 
 Schlecht lesbar 
 Nicht wiederverwendbar 
 Fehleranfällig 
 ... 
16 (c) Carsten Hetzel, 2014
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 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
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
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 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
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
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
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
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
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ü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
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
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
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
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
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
Aufgabe: Markierung 
von "besonderen" 
Rechnungsposten 
(c) Carsten Hetzel, 2014
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
Vielen Dank für Ihre Aufmerksamkeit! 
35 (c) Carsten Hetzel, 2014

Weitere ähnliche Inhalte

Empfohlen

Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...DevGAMM Conference
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationErica Santiago
 
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellSaba Software
 

Empfohlen (20)

Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
 
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
 

Tell, Don't Ask - Aussagekräftige Schnittstellen

  • 1. Tell, Don‘t Ask! Erstellung aussagekräftiger Schnittstellen (c) Carsten Hetzel, 2014
  • 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. Aufgabe: Welche Programmierprinzipien kennen Sie noch? (c) Carsten Hetzel, 2014
  • 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. Ein einfaches Beispiel (c) Carsten Hetzel, 2014
  • 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. 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. 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. 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. 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. Ein komplexeres Beispiel (c) Carsten Hetzel, 2014
  • 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. 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. 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. 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. Ein komplexeres Beispiel  Schlecht lesbar  Nicht wiederverwendbar  Fehleranfällig  ... 16 (c) Carsten Hetzel, 2014
  • 17. Konkretisierungen können auch die API erweitern (c) Carsten Hetzel, 2014
  • 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. 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. Vermitteln der Intention (c) Carsten Hetzel, 2014
  • 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. 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. 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. 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. 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. Von verstreuter Business-Logik zu Value-Objects (c) Carsten Hetzel, 2014
  • 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. 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. 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. 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. 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. 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. Aufgabe: Markierung von "besonderen" Rechnungsposten (c) Carsten Hetzel, 2014
  • 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. Vielen Dank für Ihre Aufmerksamkeit! 35 (c) Carsten Hetzel, 2014