2. Die Kunst des Software-Design
oder was Software-Entwickler von der Tierwelt lernen können
PHP World Kongress 2009
Holger Rüprich und Stephan Schmidt
3. Holger Rüprich
• Head of Sales Processes Consumer
bei der 1&1 Internet AG
• Clean Code Enthusiast
• Gast-Dozent an der Berufs-
Akademie Mosbach
• Autor für die Zeitschrift T3N
4. Stephan Schmidt
• Head of Web Sales Development
bei der 1&1 Internet AG
• Design Patterns Enthusiast
• Autor von PHP Design Patterns
sowie anderen Büchern und über
30 Fachartikeln
• Redner auf internationalen
Konferenzen
5. Software Design
„Software Design is a process of problem solving
and planning for a software solution.“
Wikipedia
6. Kunst
„Das Wort Kunst bezeichnet im weitesten Sinne
jede entwickelte Tätigkeit, die auf Wissen, Übung,
Wahrnehmung, Vorstellung und Intuition
gegründet ist.“
Wikipedia
18. Von der realen Welt ...
• Bob hat eine Autovermietung
• Er muss Autos einkaufen
• Er vermietet unterschiedliche
Modelle
• Er vermietet Autos zu verschiedenen
Preisen
19. ... zur programmierten Welt
• Klassen übertragen Dinge aus der realen Welt in die programmierte Welt
class Car {
protected $manufacturer;
protected $color;
protected $milage;
protected $engineStarted = false;
public function startEngine() {}
public function driveForward($miles) {}
public function stopEngine() {}
public function getManufacturer() {}
public function getColor() {}
public function getMilage() {}
}
20. Kapsle den Zugriff auf Daten
Kapsle den Zugriff auf Daten immer innerhalb
einer Klasse und biete Methoden an, um diese
Daten abzufragen.
class Car {
... Eigenschaften und Methoden ...
public function getDailyRate($days = 1) {
return 75.5;
}
}
21. Kapsle den Zugriff auf Daten
Kapsle den Zugriff auf Daten immer innerhalb
einer Klasse und biete Methoden an, um diese
Daten abzufragen.
class Car {
... Eigenschaften und Methoden ...
public function getDailyRate($days = 1) {
if ($days >= 7) {
return 65.9;
}
return 75.5;
}
}
22. Bobs Kunden
class Customer {
protected $id;
protected $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
}
23. Bobs Firma
class RentalCompany {
protected $fleet = array();
public function addToFleet($id, Car $car) {
$this->fleet[$id] = $vehicle;
}
public function rentCar(Car $car,
Customer $customer) {}
public function returnCar(Car $car) {}
}
24. Bobs Geschäft
• Klassen übertragen nicht nur physikalische Dinge, sondern auch Abläufe
class RentalAction {
... Eigenschaften ...
public function __construct(Car $car, Customer $customer,
$date = null) {
if ($date === null) {
$date = date('Y-m-d H:i:s');
}
$this->car = $car;
$this->customer = $customer;
$this->rentDate = $date;
}
... Getter ...
}
25. Bobs Geschäft
• Klassen übertragen nicht nur physikalische Dinge, sondern auch Abläufe
class RentalAction {
...
public function markCarReturned($date = null) {
if ($date === null) {
$date = date('Y-m-d H:i:s');
}
$this->returnDate = $date;
}
public function isReturned() {
return $this->returnDate !== null;
}
}
26. Kapsle auch Algorithmen
Kapsle nicht nur Daten sondern auch Algorithmen
in den Methoden deiner Klassen, um komplexe
Operationen zentral an einer Stelle zu
implementieren.
27. Bobs Firma
class RentalCompany {
...
public function rentCar(Car $car, Customer $customer) {
$carId = array_search($car, $this->fleet);
if ($carId === false) {
throw new UnknownCarException();
}
if (!$this->isCarAvailable($car)) {
throw new CarNotAvailableException();
}
$rentalAction = new RentalAction($car, $customer);
$this->rentalActions[] = $rentalAction;
return $rentalAction;
}
}
28. Bobs Firma
class RentalCompany {
...
public function isCarAvailable(Car $car) {
foreach ($this->rentalActions as $rentalAction) {
if ($rentalAction->getCar() !== $car) {
continue;
}
if ($rentalAction->isReturned()) {
continue;
}
return false;
}
return true;
}
}
29. Bobs Firma
class RentalCompany {
...
public function returnCar(Car $car) {
foreach ($this->rentalActions as $rentalAction) {
if ($rentalAction->getCar() !== $car) {
continue;
}
if ($rentalAction->isReturned()) {
continue;
}
$rentalAction->markCarReturned();
return true;
}
return false;
}
}
30. Die Weisheit des Eichhörnchens
Schütze deine Daten.
Verberge Implementierungsdetails und unterbinde den Zugriff auf
interne Datenstrukturen.
Wähle Klassen- und Methodennamen sinnvoll
und achte darauf, dass der resultierende Code
sich wie ein Satz lesen lässt.
34. Bob will wissen was los ist
class RentalCompany {
public function addToFleet($id, Vehicle $vehicle) {
$this->fleet[$id] = $vehicle;
print "Neues Auto im Fuhrpark: " . $vehicle->getManufacturer() . "n";
}
public function rentVehicle(Vehicle $vehicle, Customer $customer) {
...
print "Neuer Mietvorgang: " . $customer->getName() . " leiht " .
$vehicle->getManufacturer() . "n";
return $rentalAction;
}
...
}
> Neues Auto im Fuhrpark: BMW
> Neuer Mietvorgang: Stephan Schmidt leiht BMW
> Rückgabe: Stephan Schmidt gibt BMW zurück
> Neuer Mietvorgang: Gerd Schaufelberger leiht BMW
35. Bob will wissen was los ist
class RentalCompany {
public function addToFleet($id, Vehicle $vehicle) {
$this->fleet[$id] = $vehicle;
print "Neues Auto im Fuhrpark: " . $vehicle->getManufacturer() . "n";
}
Was ist mit Problemen im Produktivbetrieb?
public function rentVehicle(Vehicle $vehicle, Customer $customer) {
...
print "Neuer Mietvorgang: " . $customer->getName() . " leiht " .
$vehicle->getManufacturer() . "n";
return $rentalAction;
}
...
}
> Neues Auto im Fuhrpark: BMW
> Neuer Mietvorgang: Stephan Schmidt leiht BMW
> Rückgabe: Stephan Schmidt gibt BMW zurück
> Neuer Mietvorgang: Gerd Schaufelberger leiht BMW
36. Debugging im Produktivbetrieb
class RentalCompany {
public function addToFleet($id, Vehicle $vehicle) {
$this->fleet[$id] = $vehicle;
switch (DEBUG_MODE) {
case 'echo':
print "Neues Auto im Fuhrpark: " . $vehicle->getManufacturer() . "n";
break;
case 'log':
error_log("Neues Auto im Fuhrpark: " . $vehicle->getManufacturer() . "n",
3, '.RentalCompany.log');
break;
}
}
public function rentVehicle(Vehicle $vehicle, Customer $customer) {
...
switch (DEBUG_MODE) {
case 'echo':
print "Neuer Mietvorgang: " . $customer->getName() . " leiht " .
$vehicle->getManufacturer() . "n";
break;
...
return $rentalAction;
}
37. Debugging im Produktivbetrieb
class RentalCompany {
public function addToFleet($id, Vehicle $vehicle) {
$this->fleet[$id] = $vehicle;
switch (DEBUG_MODE) {
case 'echo':
print "Neues Auto im Fuhrpark: " . $vehicle->getManufacturer() . "n";
break;
case 'log':
error_log("Neues Auto im Fuhrpark: " . $vehicle->getManufacturer() . "n",
Code Duplizierung macht die Anwendung
3, '.RentalCompany.log');
break;
langsamer und schwerer zu warten
}
}
public function rentVehicle(Vehicle $vehicle, Customer $customer) {
...
switch (DEBUG_MODE) {
case 'echo':
print "Neuer Mietvorgang: " . $customer->getName() . " leiht " .
$vehicle->getManufacturer() . "n";
break;
...
return $rentalAction;
}
38. Wiederverwendbarkeit statt Copy-and-Paste
class RentalCompany {
public function addToFleet($id, Vehicle $vehicle) {
$this->fleet[$id] = $vehicle;
$this->debug("Neues Auto im Fuhrpark: " . $vehicle->getManufacturer());
}
public function rentVehicle(Vehicle $vehicle, Customer $customer) {
...
$this->debug("Neuer Mietvorgang: " . $customer->getName() . " leiht " .
$vehicle->getManufacturer());
return $rentalAction;
}
...
}
39. Wiederverwendbarkeit statt Copy-and-Paste
class RentalCompany {
...
protected function debug($message) {
switch (DEBUG_MODE) {
case 'echo':
print "{$message}n";
break;
case 'log':
error_log("{$message}n", 3, './RentalCompany.log');
break;
}
}
}
40. Wiederverwendbarkeit statt Copy-and-Paste
class RentalCompany {
...
Weitere Debugging-Ziele, wie E-Mails oder SMS,
protected function debug($message) {
switch (DEBUG_MODE) {
machen die debug()-Methode komplexer
case 'echo':
print "{$message}n";
break;
case 'log':und somit fehleranfälliger.
error_log("{$message}n", 3, './RentalCompany.log');
break;
}
}
}
41. Atomare Probleme lösen
abstract class RentalCompany {
... Eigenschaften und Methoden der Klasse ...
abstract protected function debug($message);
}
class EchoingRentalCompany extends RentalCompany {
protected function debug($message) {
echo "{$message}n";
}
}
class LoggingRentalCompany extends RentalCompany {
protected function debug($message) {
error_log("{$message}n", 3, './RentalCompany.log');
}
}
42. Atomare Probleme lösen
switch (DEBUG_MODE) {
case 'echo':
$company = new EchoingRentalCompany();
break;
case 'log':
$company = new LoggingRentalCompany();
break;
}
$bmw = new Car('BMW', 'blau');
$stephan = new Customer(1, 'Stephan Schmidt');
$gerd = new Customer(2, 'Gerd Schaufelberger');
$company->addToFleet('bmw1', $bmw);
$company->rentVehicle($bmw, $stephan);
$company->returnVehicle($bmw);
43. Atomare Probleme lösen
switch (DEBUG_MODE) {
case 'echo':
$company = new EchoingRentalCompany();
break;
case 'log':
Die debug()-Methode steht nur Unterklassen von
$company = new LoggingRentalCompany();
break;
}
RentalCompany zur Verfügung.
$bmw = new Car('BMW', 'blau');
$stephan = new Customer(1, 'Stephan Schmidt');
$gerd = new Customer(2, 'Gerd Schaufelberger');
$company->addToFleet('bmw1', $bmw);
$company->rentVehicle($bmw, $stephan);
$company->returnVehicle($bmw);
44. Komposition statt Vererbung
interface Debugger {
public function debug($message);
}
class DebuggerEcho {
public function debug($message) {
echo "{$message}n";
}
}
class DebuggerLog {
public function debug($message) {
error_log("{$message}n", 3, './RentalCompany.log');
}
}
45. Komposition statt Vererbung
class RentalCompany {
... Eigenschaften der Klasse ...
protected $debugger;
public function __construct() {
switch (DEBUG_MODE) {
case 'echo':
$this->debugger = new DebuggerEcho();
break;
case 'log':
$this->debugger = new DebuggerLog();
break;
}
}
public function debug($message) {
$this->debugger->debug($message);
}
}
46. Die Weisheit des Krokodils
Teile und herrsche.
Jedes Modul soll genau eine Verantwortung übernehmen, und jede
Verantwortung soll genau einem Modul zugeordnet werden.
49. Das Open-Closed-Prinzip
„Software entities (classes, modules, functions,
etc.) should be open for extension, but closed for
modification.“
Bertrand Meyer
50. Komposition statt Vererbung
class RentalCompany {
... Eigenschaften der Klasse ...
protected $debugger;
public function __construct() {
switch (DEBUG_MODE) {
case 'echo':
$this->debugger = new DebuggerEcho();
break;
case 'log':
$this->debugger = new DebuggerLog();
break;
}
}
public function debug($message) {
$this->debugger->debug($message);
}
}
51. Komposition statt Vererbung
class RentalCompany {
... Eigenschaften der Klasse ...
protected $debugger;
public function __construct() {
switch (DEBUG_MODE) {
Die RentalCompany Klassen ist von
case 'echo':
$this->debugger = new DebuggerEcho();
break;
case 'log': anderen Klassen abhängig
$this->debugger = new DebuggerLog();
break;
}
}
public function debug($message) {
$this->debugger->debug($message);
}
}
52. Einsatz von Interfaces
class RentalCompany {
... Eigenschaften der Klasse ...
public function __construct(Debugger $debugger) {
$this->debugger = $debugger;
}
... weitere Methoden der Klasse ...
}
$debugger = new DebuggerEcho();
$company = new RentalCompany($debugger);
53. Die Weisheit der Schildkröte
Schütze deinen Code.
Programmiere immer gegen Schnittstellen, nie gegen eine konkrete
Implementierung.
Vermeide feste Abhängigkeiten zwischen einzelnen Klassen deiner
Anwendungen und ziehe immer lose Kopplung der Klassen vor.
56. Das Hollywood Prinzip
„Rufen Sie uns nicht an, wir werden Sie anrufen.“
Das Hollywood Prinzip
57. Das Hollywood Prinzip
• Auch bekannt als „Inversion of
Control“
• Objekte sollen sich ihre
Abhängigkeiten nicht selbst holen
oder erzeugen
• Objekte sollen unabhängig von ihrer
Umgebung sein
58. Dependency Injection
• Objekte erstellen abhängige Objekte nicht selbst
• Abhängige Objekte werden von außen über den Konstruktor (Constructor
Injection) oder über Setter-Methoden (Setter Injection) injiziert
• Abhängige Objekte sind dadurch sehr leicht austauschbar
59. Dependency Injection
$debugger = new DebuggerEcho();
$company = new RentalCompany($debugger); Constructor Injection
$logger = new DateTimeLogger();
$company->setLogger($logger); Setter Injection
• RentalCompany weiß weder, welche Implementierung des Debugger-Interface
noch welche Implementierung des Logger-Interface sie verwendet
• RentalCompany steuert nicht, wie sie an ihre Abhängigkeiten kommt, der
Kontrollfluss wird von außerhalb gesteuert
• Aber: Sehr viel zusätzlicher Client Code, durch Instanziieren und Injizieren der
Objekte nötig
60. Inversion-of-Control-Container
• In der Java-Welt schon weit verbreitet
• Spring, Google Guice, Tapestry IoC, ...
• In PHP noch nicht weit verbreitet
• Stubbles, Garden, XJConf for PHP
61. Stubbles IoC Container
• Portierung von Google Guice (Java)
• Entwickelt als Teil des Stubble Frameworks
• Kann auch außerhalb der Frameworks verwendet werden
• Basiert auf Type Hints und Annotations
• Keine native Unterstützung durch PHP
• Annotations als Teil der PHP-Doc-Kommentare
62. Rufen Sie Bob nicht an, Bob ruft sie an.
class RentalCompany {
... Eigenschaften der Klasse ...
/**
* @Inject Constructor Injection
*/
public function __construc(Debugger $debugger) {
$this->debugger = $debugger;
}
/**
* @Inject Setter Injection
*/
public function setLogger(Logger $logger) {
$this->logger = $logger;
}
...
}
63. Interfaces binden
• Typen werden durch Binder an konkrete Implementierungen gebunden:
$binder = new stubBinder();
$binder->bind('Debugger')->to('DebuggerEcho');
$binder->bind('Logger')->to('DateTimeLogger');
• Binder kann Injector liefern, der Instanzen erzeugt:
$injector = $binder->getInjector();
$company = $injector->getInstance('RentalCompany');
64. Fortgeschrittene Techniken
• Verwenden einer Standardimplementierung
/**
* @ImplementedBy(DateTimeLogger.class)
*/
interface Logger {
• Mehrere Implementierungen für ein Interface
/**
* @Inject
* @Named("Debugger")
public function setLogger(Logger $logger)
$binder = new stubBinder();
$binder->bind('Logger')->to('DateTimeLogger');
$binder->bind('Logger')->named('Debugger')->to('SimpleLogger');
65. Die Weisheit des Erpel
Verwende ein DI Framework.
Erleichtere dir das Verwalten von komplexen Objektstrukturen
durch den Einsatz eines Dependency Injection Frameworks.
68. Konkretes Problem
„Ich möchte Debug-Meldungen auf verschiedene
Arten verarbeiten und diese auswechseln können,
ohne den Code der RentalCompany Klasse
anpassen zu müssen.“
Bob
69. Abstraktes Problem
„Ich möchte eine Aufgabe mit verschiedenen
Algorithmen lösen können. Jede der Lösungen soll
gekapselt sein und nichts von den anderen wissen.
Die einzelnen Lösungen sollen gegeneinander
austauschbar sein, ohne den nutzenden Client
anzupassen.“
Abstract Bob
70. Konkret vs Abstrakt
Abstrakt Konkret
Verarbeiten von Debug-
Aufgabe
Meldungen
Ausgeben per print(),
Algorithmen
Schreiben eines Logfiles
Die Klasse
Client
RentalCompany
71. Konkret vs Abstrakt
Abstrakt Konkret
Persistieren von
Aufgabe
Gästebucheinträgen
Speichern in Datenbank,
Algorithmen
Speichern in XML-Datei
Client Die Klasse Guestbook
73. Design Patterns
• Lösungsmuster für häufig auftretende Entwurfsaufgaben in der Software-
Entwicklung
• Keine Code-Bibliothek
• Organisiert in Pattern-Katalogen (z.B. Gang-of-Four Buch)
• Verschiedene Kategorien: Erzeugungsmuster, Strukturmuster,
Verhaltensmuster, Enterprise-Patterns
74. Erzeugungsmuster
• Erzeugungsmuster werden verwendet, um Objekte zu konstruieren.
• Zu den Erzeugungsmustern gehöhren unter anderem
• Singleton-Pattern
• Factory-Method-Pattern
• Prototype-Pattern
• Abstract-Factory-Pattern
75. Factory-Method-Pattern
• Definiert eine Schnittstelle zur Erzeugung von Objekten
• Verlagert die eigentliche Instanziierung in Unterklassen
• Lässt Unterklassen entscheiden, welche konkrete Implementierung
verwendet wird
76. Factory-Method-Pattern
abstract class AbstractManufacturer {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public function sellVehicle() {
$vehicle = $this->manufactureVehicle();
// weitere Operationen möglich
return $vehicle;
}
public abstract function manufactureVehicle();
}
77. Factory-Method-Pattern
class CarManufacturer extends AbstractManufacturer {
public function manufactureVehicle() {
return new Car($this->name);
}
}
$bmwManufacturer = new CarManufacturer('BMW');
$bmw = $bmwManufacturer->sellVehicle();
78. Factory-Method-Pattern
Das Factory-Method-Pattern definiert eine
Schnittstelle zur Erzeugung von Objekten. Es
verlagert aber die eigentliche Instanziierung in
Unterklassen; es lässt die Unterklassen
entscheiden, welche konkreten
Implementierungen verwendet werden.
79. der abstrak
lichen Objekte zu
4. Verwenden Sie nun diese konk reten Unterklassen, um die tatsäch
ementierungen zu
instanziieren und Ihren Applik ationscode von den konkreten Impl
lösen.
fach darauf, die
verwenden möchten, achten Sie ein
Factory-Method-Pattern
Wann immer Sie eine Fabrikmetho
de
hier gezeigten Schritte durchzuführe
n, und dem Erfolg Ihres Vorhabens
steht nichts mehr
teiligten des Fac-
n die Beziehungen zwischen den Be
im Weg. Abbildung 4-2 zeigt Ihne f die Erzeugung der
tory-Method-Patterns und illustri ert noch einmal, wie das Pattern au
cle-Implementierungen angewa
ndt wurde.
Vehi
sell Vehicle-Methode, ruft
Vehicle-Interface Fabrikmethode auf
Creator
Product
+FactoryMethod() : Product
+MethodA()
AbstractManufacturer- CarManufacturer und
Klasse ConvertibleManufacturer
ConcreteCreator
ConcreteProduct
+FactoryMethod() : Product
Implementierungen des Vehicle- Fabrikmethode manufactureVehicle
anzen
Interfaces wie Car, Convertible et
c. erzeugt Car- bzw. Convertible-Inst
s Factory-Method-Patterns
Abbildung 4-2: UML-Diagramm de
Das Factory-Method-Pattern | 177
80. Prototype-Pattern
• Das Prototype-Pattern erzeugt Objekte durch das Kopieren eines
prototypischen Exemplars
• Es ermöglicht das Hinzufügen neuer "Klassen" zur Laufzeit ohne
Programmierung
• Es hält die Anzahl der benötigten Klassen klein
81. Prototype-Pattern
class SpecialEditionManufacturer {
protected $prototypes = array();
public function addSpecialEdition($edition, Vehicle $prototype) {
$this->prototypes[$edition] = $prototype;
}
public function manufactureVehicle($edition) {
if (!isset($this->prototypes[$edition])) {
throw new UnknownSpecialEditionException(
'No prototype for special edition ' . $edition . ' registered');
}
return clone $this->prototypes[$edition];
}
}
82. Prototype-Pattern
class Car implements Vehicle { ... }
class Convertible implements Vehicle { ... }
$manufacturer = new SpecialEditionManufacturer();
$GolfElvis = new Car('VW', 'silber');
$GolfElvis->setAirConditioned(true);
$GolfElvis->setGraphics('Gitarre');
$manufacturer->addSpecialEdition('Golf Elvis Presley Edition', $GolfElvis);
$GolfStones = new Convertible('VW', 'rot');
$GolfStones->setAirConditioned(false);
$GolfStones->setGraphics('Zunge');
$manufacturer->addSpecialEdition('Golf Rolling Stones Edition', $GolfStones);
$golf1 = $manufacturer->manufactureVehicle('Golf Elvis Presley Edition');
$golf2 = $manufacturer->manufactureVehicle('Golf Rolling Stones Edition');
83. Prototype-Pattern
Das Prototyp-Muster bestimmt die Arten der zu
erzeugenden Objekte durch die Verwendung eines
prototypischen Exemplars, das zur Erzeugung
neuer Instanzen kopiert wird.
84. Prototype-Pattern
!!"""#$#"%&'()*+,,-../0120.)!!..3
4012567.8*.95:;54.)!!8..<<='&.<<
Vehicle-Interface
SpecialEditionManufacturer __clone()-Methode
kennt alle verfügbaren in PHP nicht immer nötig
Prototypen
rface wie
Implementierungen des Vehicle-Inte
Car, Convertible etc.
e-Patterns
Abbildu ng 4-6: UML-Diagramm des Prototyp
plikation
reduziert die Anzahl der Klassen, die Ihre Ap
Der Einsatz des Prototype-Patterns ukte zu
sen bilden müssen, um die einzelnen Prod
benötigt, da Sie weniger Unterklas en von einer Klasse
erzeugen. Stattdessen werden al le Produkte auf Basis der Prototyp
85. Strukturmuster
• Strukturmuster befassen sich mit der Komposition von Objekten
• Zu den Strukturmustern gehöhren unter anderem
• Composite-Pattern
• Proxy-Pattern
• Adapter-Pattern
• Facade-Pattern
86. Composite-Pattern
• Lässt mehrere Instanzen eines Typs nach außen wie eine Instanz aussehen
• Implementieren einer neuen Klasse, die die einzelnen Instanzen aufnimmt
• Muss die selbe Schnittstelle implementieren wie die entsprechenden
Instanzen
87. Composite-Pattern
interface Debugger {
public function debug($message);
}
// Implementierungen
class DebuggerLog implements Debugger {
public function debug($message) {
error_log($mssage, 3, 'debug.log');
}
}
class DebuggerMail implements Debugger {
public function debug($message) {
mail('schst@php.net', 'Error happened', $message);
}
}
88. Composite-Pattern
class DebuggerComposite implements Debugger {
protected $debuggers = array();
public function addDebugger(Debugger $debugger) {
$this->debuggers[] = $debugger;
}
public function debug($message) {
foreach ($this->debuggers as $debugger) {
$debugger->debug($message);
}
}
}
$debuggerComposite = new DebuggerComposite();
$debuggerComposite->addDebugger(new DebuggerLog());
$debuggerComposite->addDebugger(new DebuggerMail());
89. Composite-Pattern
Das Composite-Pattern fügt mehrere Objekte zu
einer Baumstruktur zusammen und ermöglicht es,
diese von außen wie ein einzelnes zu verwenden.
90. Composite-Pattern
!!"""#$#"%&'()*+,,-../0120.)3)..45
012678.9*.:6;<65.)!!9..33='&.33
DebuggerComposite kann
Debugger-Schnittstelle mit beliebig viele Debugger
Methode debug() speichern
Component
+methodA()
+methodB()
Konkrete Debugger-
Implementierungen
Composite
ConcreteComponent
+addChild(child : Component)
+methodA()
+removeChild(child : Component)
+methodB()
+methodA()
+methodB()
DebuggerComposite-Klasse debug()-Methode delegiert
Aufruf an die anderen Debugger
s Composite-Patterns
Abbildung 5-3: UML-Diagramm de
Weitere Anwendungen e Anwendung des
haben Sie bereits eine sehr beliebt
Mit der Debugging-Funktionalität e ähnliche
etet das PEAR-Paket Log , das ein
2
Kompositum-Patterns kenn engelernt. So bi si-
llt, auch eine Klasse, die als Kompo
91. Adapter-Pattern
• Das Adapter-Pattern passt die Schnittstelle eines Objekts an die vom Client
erwartete Schnittstelle.
• Es erleichtert die Nutzung von Fremdcode in eigenen Systemen.
• Das Adapter-Pattern arbeitet ähnlich wie ein Adapter für Steckdosen.
92. Adapter-Pattern
class Automobile {
const DIRECTION_FORWARD = 0;
const DIRECTTION_BACKWARD = 1;
...
public function drive($direction, $miles) {
if ($this->ignited !== true) {
throw new AutomobileException('Zündung ist nicht an.');
$this->milesDriven = $this->milesDriven + $miles;
}
}
...
}
93. Adapter-Pattern
class AutomobileAdapter implements Vehicle {
protected $automobile;
public function __construct(Automobile $automobile) {
$this->automobile = $automobile;
}
...
public function moveForward($miles) {
try {
$this->automobile->drive(Automobile::DIRECTION_FORWARD, $miles);
return true;
} catch (AutomobileException $e) {
return false;
}
}
94. Adapter-Pattern
Das Adapter-Muster passt die Schnittstelle einer
Klasse an die vom Client erwartete Schnittstelle
an. Es ermöglicht die Zusammenarbeit von
Klassen, die eigentlich aufgrund inkompatibler
Schnittstellen nicht zusammenarbeiten können.
95. Sie die Anf
von Fehlern.
5. Beachten Sie Unterschiede beim Signalisieren
prungsobjekt zu
ion das Adapter-Objekt, um das Urs
6. Verwenden Sie in Ihrer Applikat
ummanteln.
Adapter-Pattern
Wenn Sie diese einfachen Schritte
Ab
befolgen, adaptieren Sie leicht die
verschiedensten
bildung 5-4 zeigt Ihnen noch einmal
die am Adap-
Schnittstellen in Ihrer Applikation. oblem der
Interfaces und wie das Pattern auf das Pr
ter-Pattern beteiligten Klassen und wandt wurde.
abweichenden Schn ittstelle der Automobile-Klasse ange
Klassen, die das Vehicle-
Interface nutzen, z.B. Vehicle-Interface
RentalCompany
Client «interface»
Target
+methodA()
Automobile-Klasse
Adaptee
Adapter
+methodB()
+methodA()
AutomobileAdapter
Adapter ruft die entsprechenden
Methoden auf dem Automobile-
Objekt auf
s Ad apter-Patterns
Abbildung 5-4: UML-Diagramm de
96. Verhaltensmuster
• Verhaltensmuster beschreiben die Interaktion zwischen Objekten.
• Zu den Verhaltensmuster gehöhren unter anderem
• Subject/Observer-Pattern
• Template-Method-Pattern
• Command-Pattern
• Iterator-Pattern
97. Template-Method-Pattern
• Definiert die Schritte eines Algorithmus in einer Methode
• Implementierung der einzelnen Schritte bleibt Unterklassen vorbehalten
• Gemeinsames Verhalten muss nur einmal implementiert werden: Änderungen
am Algorithmus nur an einer Stelle notwendig
• Neue Unterklassen müssen nur die konkreten Schritte implementieren
98. Template-Method-Pattern
abstract class AbstractCar implements Vehicle {
...
final public function inspect() {
print "Führe Inspektion durch";
$this->replaceSparkPlugs();
$this->checkTires();
if ($this->isOilLevelLow()) {
$this->refillOil();
}
}
abstract protected function replaceSparkPlugs();
abstract protected function checkTires();
abstract protected function isOilLevelLow();
protected function refillOil() {
print "Fülle ". (300 - $this->oilLevel) . "ml Öl nach.n";
$this->oilLevel = 300;
}
}
99. Template-Method-Pattern
class Car extends AbstractCar {
protected function replaceSparkPlugs() {
print "Ersetze Zündkerzen durch Modell AF34.n";
}
protected function checkTires() {
print "Überprüfe Reifendruck, muss 2,0 bar sein.n";
}
protected function isOilLevelLow() {
if ($this->oilLevel < 200) {
return true;
}
return false;
}
}
100. Template-Method-Pattern
Das Template-Method-Pattern definiert die
Schritte eines Algorithmus in einer Methode und
überlässt die Implementierung der einzelnen
Schritte den Unterklassen. Diese können somit
Teile des Algorithmus modifizieren, ohne dessen
Struktur zu verändern.
101. Template-Method-Pattern
!!"""#$#"%&'()*+,,-../0120.)%)..34
012567.8*.95:;54.)!!8..<<='&.<<
AbstractCar-Klasse
AbstractClass
+templateMethod()
#primitiveOperation()
inspect()-Methode, ruft die
abstrakten Methoden auf,
die in Unterklassen implementiert
werden
Car- und Convertible-Klassen
ConcreteClass
Implementieren checkTires
#primitiveOperation() replaceSparkPlugs etc.
e-Method-Patterns
Abbildu ng 6-2: UML-Diagramm des Templat
faktorieren und
es, gemeinsamen Code herauszu
Schablonenmethoden ermöglichen n zu müssen.
somit gemeinsames Verhalten nur einmal implementiere
102. Command-Pattern
• Kapselt einen Auftrag als Objekt.
• Aufträge (Objekte) sind parametrisierbar
• Aufträge können in einer Queue nacheinander abgearbeitet werden
• Aufträge können rückgängig gemacht werden.
103. Command-Pattern
interface CarWashCommand {
public function execute(Car $car);
}
class CarSimpleWashCommand implements CarWashCommand {
public function execute(Car $car) {
echo "Das Auto wird gewaschen";
}
}
class CarDryingCommand implements CarWashCommand {
public function execute(Car $car) {
echo "Das Auto wird getrocknet";
}
}
104. Command-Pattern
class CarWash {
protected $programmes = array();
public function addProgramme($name, array $commands) {
$this->programmes[$name] = $commands;
}
public function wash($prog, Car $car) {
foreach ($this->programmes[$prog] as $command) {
$command->execute($car);
}
}
}
105. Command-Pattern
$wash = new CarWash();
$wash->addProgramme('standard', array(
new CarSimpleWashCommand(),
new CarDryingCommand()
));
$wash->addProgramme('komfort',
array(
new CarSimpleWashCommand(),
new CarEngineWashCommand(),
new CarDryingCommand(),
new CarWaxingCommand()
));
$wash->wash('standard', $bmw);
106. Command-Pattern
Das Command-Pattern kapselt einen Auftrag als
Objekt. Dadurch wird ermöglicht, andere Objekte
mit Aufträgen zu parametrisieren, Aufträge in
eine Queue zu stellen oder diese rückgängig zu
machen.
107. parametrisieren.
t, also das Objekt, das die
Mit Client ist im aktuell en Beispiel die Waschanlage gemein
Warteschlange gestellt
Befehle verwendet. Dabei müs sen die Befehle nicht immer in eine
denkbar, dass nur
des Command-Patterns ist es auch
werden. Bei anderen Anwendungen ausgeführt
i Eint reten einer bestimmten Bedingung
ein Befehl übergeben wird, der be
Command-Pattern
wird. Es handelt sich trotzdem um ein Command-Pattern, da der Au
zeit ausgetauscht werden kann. Ab
ftrag in einer Klasse
bildung 6-3 zeigt
gekapselt wird und somit zur Lauf se miteinander in Verbin-
Ihnen die im Command-Pa ttern beteiligten Akteure und wie die
dung stehen.
CarWash, also CarWashCommand-
die Waschanlage Interface
«interface»
Client Invoker
Command
+execute()
Car-Objekte, die gewaschen
werden
Receiver ConcreteCommand
-state
+execute()
+action()
Konkrete Implementierungen
der Wasch-Befehle, z.B.
CarMotorWashCommand
Command-Patterns
Abbildung 6-3: UML-Diagramm des
290 | Kapitel 6: Verhaltensmuster
108. Enterprise Patterns
• Kommen aus der Java Welt
• Stark geprägt durch Martin Fowler
• Meistens komplexer als die Gang-of-Four Patterns
• Deswegen ab hier keine Beispiele mehr
• Mehr zu Enterprise Patterns in PHP im Buch "PHP Design Patterns"
http://www.phpdesignpatterns.de
109. Die Weisheit der Biene
Nutze bestehende Lösungen.
Abstrahiere Deine konkreten Probleme und wende Design Patterns
als Vorlage für Deine Lösung an.
112. You Ain‘t Gonna Need It (YAGNI)
„Always implement things when you actually
need them, never when you just foresee that you
need them“
Ronald E Jeffries
113. You Ain‘t Gonna Need It (YAGNI)
• Anforderungen sind in der Software-Entwicklung notorisch ungenau oder
wechselnd
• Ungenaue Anforderungen werden oft durch möglichst flexible und
funktionsfähige Software kompensiert
• Es werden Features entwickelt die keine Anwendung finden
• Dinge die niemand braucht, haben keinen Wert.
114. Do the simplest thing that could possibly work.
• YAGNI kann als Ergänzung des XP-Prinzips "Do the simplest thing that could
possibly work." verstanden werden
• Wann ist ein Design am einfachsten?
• Es verkörpert die Absicht des Entwicklers und besteht alle Tests.
• Es enthält kein Duplizierungen.
• Es enthält das Minimum an Klassen und Methoden
115. Emergenz
„Emergenz ist die spontane Herausbildung von
komplexen Systemen und Strukturen durch eine
Vielzahl von relativ einfachen Interaktionen.“
Wikipedia
116. Saubere Software durch emergentes Design
• Alle Tests bestehen
• Software muss in erster Linie den gewollten Zweck erfüllen.
• Software, die nicht testbar ist, kann nicht verifiziert werden.
• Klassen die dem Single Responsibility Prinzip folgen sind leichter zu
testen.
• Je mehr Tests wir schreiben, desto mehr bemühen wir uns Code zu
schreiben, der einfacher zu testen ist.
117. Saubere Software durch emergentes Design
• Alle Tests bestehen
• Eine starke Kopplung erschwert das Schreiben von Tests
• Je mehr Tests wir schreiben, desto mehr bemühen wir uns, die
Kopplung zu minimieren
118. Saubere Software durch emergentes Design
• Refactoring nach dem Bestehen eines Tests
• Duplizierten Code eliminieren
• Ausdrucksstärke des Codes verbessern
• Anzahl der Klassen und Methoden minimieren
• Tests sichern das bisherige Ergebnis ab
119. Vorsicht: Perfekt ist der Feind von "Gut genug"
• Entwickler tendieren dazu Lösungen danach zu analysieren wie elegant und
optimal sie für die Problemstellung sind.
• Software Entwicklung ist kein Schönheitswettbewerb
• Der Code ist klar, ausdrucksstark, gut dokumentiert und getestet. Geht es
noch besser?
• Klar. Aber es er ist gut genug.
• Verschwende keine Zeit auf der Suche nach dem perfekten Design
121. Permature Optimization
• Premature Optimization beschreibt die Situation in der Design
Entscheidungen aufgrund von Performance-Optimierungen getroffen werden
• Solche Optimierungen resultieren oft in unleserlicherem Code
• M. A. Jackson über Optimierung
• Dont't do it.
• (For experts only) - Don't do it yet
122. Premature Optimization
• Was wenn Performance-Optimierung unumgänglich ist?
• Anwendung & Design entwickeln
• Profiler / Benchmarks einsetzen
• Flaschenhälse identifizieren
• Ein einfaches und elegantes Design ist oft leichter zu optimieren
123. Die Weisheit der Bulldogge
Mit kleinen Schritten zum großen Ziel.
Mach es nicht perfekt, mach es gut genug.
Je länger Entscheidungen aufgeschoben werden,
desto mehr Wissen hat man darüber
127. Essential Complexity
• "Essential Complexity" ist die Komplexität, die dem eigentlichen Problem
innewohnt
• Am besten mit "notwendige Komplexität" übersetzt
• Die Komplexität ist durch das Business getrieben und kann nicht ignoriert
werden
128. Accidential Complexity
• "Accidential Complexity" ist die Komplexität. die durch die technische Lösung
hinzugefügt wird, mit der Absicht, die notwendige Komplexität zu
kontrollieren
• Häufiges Problem von eingekauften Lösungen und generischen Frameworks
• Entwickler werden von Komplexität angezogen
• Entwickler wollen komplexe Probleme lösen und lösen damit oft Probleme,
die die Lösung erst eingeführt hat
131. Vermeide Accidential Complexity
• Verwende Frameworks, die aus
produktivem Code entstanden sind.
• Extrahiere Frameworks aus
bestehendem Code.
• Prüfe, wie viel Prozent des Codes das
tatsächlich vorhandene Problem
adressiert
• Triff keine Entscheidungen aus dem
Elfenbeinturm.
132. Die Weisheit des Regenwurms
Fokussiere Dich auf das Problem.
Implementiere Lösungen, die Probleme der Domäne lösen, ohne
unnötige Komplexität einzuführen.
135. Unit Tests
„Im Unit Test werden kleinere Programmteile in
Isolation von anderen Programmteilen getestet.“
Frank Westphal
136. Unit Tests
• Testen eine einzelne Codeeinheit isoliert.
• Die Granularität der Codeeinheit kann von Methoden über Klassen bis hin
zu Komponenten reichen.
• Unit Test-Frameworks für PHP
• PHPUnit - JUnit Portierung für PHP von Sebastian Bergmann
• SimpleTest - von Marcus Baker
137. Unit Tests
require_once 'RentalCompany.php';
class RentalCompanyTest extends PHPUnit_Framework_TestCase {
protected $rentalCompany;
protected function setUp() {
$this->rentalCompany = new RentalCompany();
}
public function testAddToFleet() {
$this->rentalCompany->addToFleet(new Car('VW', 'silber'));
$carCount = $this->rentalCompany->countCarsInFleet();
$this->assertEquals(1, $carCount);
}
}
138. Unit Tests
$ phpunit RentalCompanyTest
PHPUnit 3.4.2 by Sebastian Bergmann.
.
Time: 0 seconds
OK (1 test, 1 assertion)
139. Integrationstests
„There is a vast gulf between the process mappers
who model business systems pictorially, and the
programmers who grind out the C++ or Java
or .Net services that support those business
systems. Between the two camps lies a fertile land
of opportunity. It's time to jointly explore it.“
Ward Cunningham
140. FIT - Framework for Integrated Tests
• Framework für Integrations- und Akzeptanz-Tests
• Der Kunde schreibt die Testfälle in HTML, Word oder Excel
• Bezieht den Kunden in den Entwicklungsprozess ein und fördert agiles
Vorgehen
• Mittlerweile für sehr viele Sprachen verfügbar, für PHP innerhalb von PEAR
141. Funktionsweise
• Kunde erstellt eine Tabelle mit Eingabe-Parametern und erwarteten
Rückgabe-Werten
• FIT parst die HTML-Tabelle und interpretiert diese als Testfälle
• FIT reicht die Eingabeparameter an den Testcode (Fixture) weiter
• Der Testcode führt den zu testenden Code aus
• FIT holt die das Ergebnis aus dem Testcode
• FIT markiert Abweichungen in der HTML-Tabelle
143. Webtests mit Selenium
• Dem Kunden sind Unit-Tests egal, wenn die Website nicht funktioniert
• Selenium prüft die Anwendung auf der Ebene, die der Kunde sieht
• Steuert den Browser fern und prüft gegen das erwartete Verhalten
• Funktioniert in allen großen Browsern, auch im IE6
• Tests können über Firefox-Extension aufgezeichnet werden
• Selenium RC ermöglicht Fernsteuerung aus PHPUnit Tests
146. Continuous Integration
• Beschreibt den Prozess des regelmäßigen, vollständigen Builds und Testens
einer Anwendung
• Vorteile durch den Einsatz von CI
• Integrations-Probleme werden frühzeitig entdeckt
• Fehler werden nicht verschleppt
• Code Qualität ist über den gesamten Entwicklungsprozess sichtbar
150. Die Weisheit des Kugelfischs
Better safe than sorry.
Schaffe dir Sicherheitsnetze durch automatisierte Tests auf
verschiedenen Ebenen deiner Applikation.
Integriere regelmäßig und stelle Fehler
frühzeitig fest.
153. Coding Standards
• Legen fest, wie lang eine Zeile sein darf, wie eingerückt wird, wo geklammert
wird, wo Leerzeichen stehen, wann Großbuchstaben oder Kleinbuchstaben
verwendet werden, wie eine Funktionsdefinition aussehen soll, wie eine
Klassendefinition aussehen soll, wie eine Methodendefinition aussehen soll,
wie und wo Includes verwendet werden sollen, und, und, und, …
• Haben religiöse Sprengkraft
• Sorgen dafür, dass der Code für alle Entwickler lesbar bleibt
• Entwickler haben dadurch Zeit, sich auf das Wesentliche zu fokussieren
154. Coding Standards in PHP
• PHP_CodeSniffer kann Code gegen verschiedene Regeln prüfen
• Liefert bereits Regelsets mit, ist jedoch erweiterbar (hört auf die Schildkröte)
$ phpcs --standard=pear badClass.php
FILE: /var/www/phpcodesniffer/badClass.php
--------------------------------------------------------------------------------
FOUND 5 ERROR(S) AND 0 WARNING(S) AFFECTING 3 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Missing file doc comment
3 | ERROR | Class name must begin with a capital letter
5 | ERROR | Line indented incorrectly; expected at least 4 spaces, found 1
5 | ERROR | Spaces must be used to indent lines; tabs are not allowed
5 | ERROR | Class constants must be uppercase; expected MYCONSTANT but found
| | MyConstant
--------------------------------------------------------------------------------
155. Verwende Name die ihre Absicht aufdecken
• Namen von Variablen, Funktionen oder Klassen, sollten folgende Fragen
beantworten:
• Warum existiert die Variable (Funktion oder Klasse)?
• Was macht sie?
• Wie wird sie verwendet?
• $d = 1; // elapsed time in days
$elapsedTimeInDays = 1;
156. Benennung
• Verwende aussprechbare Namen
• Verwende ein Wort pro Konzept (z.B. fetch, retrieve oder get)
• Vermeide "Nerd Names" in Klassennamen
• ...Helper, ...Manager oder ...Util
• http://www.classnamer.com/
160. Kommentare
• Gute Kommentare • Schlechte Kommentare
• @todo-Kommentare • Redundante Kommentare
• Informative Kommentare • Postionsmarkierungen
• PHPDocs in öffentlichen APIs • Auskommentierter Code
Kommentare sind keine Ausrede für schlechten Code.
161. DRY - Don‘t Repeat Yourself
„Every piece of knowlege must have a single,
unambiguous, authoritative representation
within a system“
Andrew Hunt and Dave Thomas
162. DRY - Don‘t Repeat Yourself
• Nichts ist einfacher als Copy & Paste
• „Nummer Eins der Gestanksparade“ in Refactoring von Martin Fowler
• Jede Doppelung von Code leistet Inkonsistenzen und Fehlern Vorschub.
163. Fehlerhandling
• Verwende Exceptions
• Reichere deine Exceptions mit sinnvollen Informationen an.
• Definieren Exception-Klassen nach den Bedürfnissen der aufrufenden
Systeme.
• Verwende Exceptions nicht als billige Alternative für goto.
• Gib niemals null zurück.
165. Die Pfadfinder-Regel
• „Don‘t live with Broken Windows“
• Fixe schlechte Designs, falsche Entscheidungen und schlechten Code
sobald du ihn siehst
• Es muss nichts großes sein
• Ändere den Namen einer Variable in einen besseren
Breche eine Funktion auf, die zu groß ist
Eliminiere ein kleine Duplizierung
• Software muss dauerhaft sauber gehalten werden
166. Die Weisheit des Waschbärs
Dreckiger Code führt zu dreckigem Design.
Lass auch bei Detail-Fragen die gleiche Sorgfalt walten wie bei
großen Design-Entscheidungen.
Wissen - Ich muss meine Technologie und die Dom&#xE4;ne kennen
&#xDC;bung - Der erste Entwurf geht schief (&#xDC;berflieger: 1000 Stunden, 1000 Fotos f&#xFC;r die Tonne)
Wahrnehmung - Sehen wo die Probleme liegen, Schnelles Verstehen
Vorstellung - Software ist abstrakt
Intuition - Software-Entwurf ist kein striktes logisches Vorgehen
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall
Stabil - Unser Leben basiert auf Software, wenn unserer Software versagt...
Sicher - Online Banking, Sensible Daten im Netz
Flexibel - "640kb reichen f&#xFC;r jeden"
Performance - Twitter Ausfall