Die Kunst des Software Design - Java

162
Die Kunst des Software-Design oder was Software-Entwickler von der Tierwelt lernen können 1&1 Internet AG, 8. Februar 2011 Holger Rüprich und Stephan Schmidt

description

Folien zu einem internen Vortrag bei der 1&1 Internet AG, der auf Folien der PHP World 2009 basiert.

Transcript of Die Kunst des Software Design - Java

Page 1: Die Kunst des Software Design - Java

Die Kunst des Software-Design

oder was Software-Entwickler von der Tierwelt lernen können

1&1 Internet AG, 8. Februar 2011Holger Rüprich und Stephan Schmidt

Page 2: Die Kunst des Software Design - Java

Holger Rüprich

• Head of Sales Processes Access bei der 1&1 Internet AG

• Clean Code Enthusiast

• Gast-Dozent an der Berufs-Akademie Mosbach

• Autor für die Zeitschrift T3N

Page 3: Die Kunst des Software Design - Java

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

Page 4: Die Kunst des Software Design - Java

Software Design

„Software Design is a process of problem solving and planning for a software solution.“

Wikipedia

Page 5: Die Kunst des Software Design - Java

„Das Wort Kunst bezeichnet im weitesten Sinne jede entwickelte Tätigkeit, die auf Wissen, Übung, Wahrnehmung, Vorstellung und Intuition gegründet ist.“

Wikipedia

Kunst

Page 6: Die Kunst des Software Design - Java

Ziele von Software Design

Stabilität Sicherheit Flexibilität Performance

Page 7: Die Kunst des Software Design - Java

Das Eichhörnchen

Kapsle Daten und Algorithmen.

Page 8: Die Kunst des Software Design - Java

Von der realen Welt ...

• Bob hat eine Autovermietung

• Er muss Autos einkaufen

• Er vermietet unterschiedliche Modelle

• Er vermietet Autos zu verschiedenen Preisen

Page 9: Die Kunst des Software Design - Java

... zur programmierten Welt

• Klassen übertragen Dinge aus der realen Welt in die programmierte Welt

public class Car {

private String manufacturer; private String color; private float milage; private boolean engineStarted;

public void startEngine() {} public void driveForward(float miles) {} public void stopEngine() {} public String getManufacturer() {} public String getColor() {} public float getMilage() {}

}

Page 10: Die Kunst des Software Design - Java

Kapsle den Zugriff auf Daten

Kapsle den Zugriff auf Daten immer innerhalb einer Klasse und biete Methoden an, um diese Daten abzufragen.

public class Car {

... Eigenschaften und Methoden ...

public double getDailyRate(int days) { return 75.5; }}

Page 11: Die Kunst des Software Design - Java

Kapsle den Zugriff auf Daten

Kapsle den Zugriff auf Daten immer innerhalb einer Klasse und biete Methoden an, um diese Daten abzufragen.

public class Car {

... Eigenschaften und Methoden ...

public double getDailyRate(int days) { if (days >= 7) { return 65.9; } return 75.5; }}

Page 12: Die Kunst des Software Design - Java

Bobs Kunden

public class Customer { private int id; private String name; public Customer(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; }

}

Page 13: Die Kunst des Software Design - Java

Bobs Firma

public class RentalCompany {

Map<String, Vehicle> fleet = new HashMap<String, Vehicle>();

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); }

public RentalAction rentVehicle(Vehicle vehicle, Customer customer) {} public boolean returnVehicle(Vehicle vehicle) {}

}

Page 14: Die Kunst des Software Design - Java

• Klassen übertragen nicht nur physikalische Dinge, sondern auch Abläufe

Bobs Geschäft

public class RentalAction { ... Eigenschaften ...

public RentalAction(Vehicle vehicle, Customer customer) { this(vehicle, customer, new Date()); }

public RentalAction(Vehicle vehicle, Customer customer, Date date) { this.vehicle = vehicle; this.customer = customer; this.rentDate = date; }

... Getter ...}

Page 15: Die Kunst des Software Design - Java

• Klassen übertragen nicht nur physikalische Dinge, sondern auch Abläufe

Bobs Geschäft

public class RentalAction { ...

public void markCarReturend() { markCarReturend(new Date()); } public void markCarReturend(Date date) { returnDate = date; }

public boolean isReturend() { return returnDate != null; }}

Page 16: Die Kunst des Software Design - Java

Kapsle auch Algorithmen

Kapsle nicht nur Daten sondern auch Algorithmen in den Methoden deiner Klassen, um komplexe Operationen zentral an einer Stelle zu implementieren.

Page 17: Die Kunst des Software Design - Java

public class RentalCompany {

...

public RentalAction rentVehicle(Vehicle vehicle, Customer customer) { if (!fleet.containsValue(vehicle)) { throw new UnknownVehicleException(); }

if (!vehicleIsAvailable(vehicle)) { throw new VehicleNotAvailableException(); }

RentalAction rentalAction = new RentalAction(vehicle, customer); rentalActions.add(rentalAction); return rentalAction; }

}

Bobs Firma

Page 18: Die Kunst des Software Design - Java

Bobs Firma

public class RentalCompany {

...

private boolean isVehicleAvailable(Vehicle vehicle) {

for (RentalAction rentalAction : rentalActions) {

if (rentalAction.getVehicle().equals(vehicle) && !rentalAction.isReturend()) { return false; }

}

return true; }

}

Page 19: Die Kunst des Software Design - Java

Bobs Firma

public class RentalCompany {

...

public boolean returnVehicle(Vehicle vehicle) {

for (RentalAction rentalAction : rentalActions) {

if (rentalAction.getVehicle().equals(vehicle) && !rentalAction.isReturend()) { rentalAction.markCarReturend(); return true; }

}

return false; }

}

Page 20: Die Kunst des Software Design - Java

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.

Die Weisheit des Eichhörnchens

Page 21: Die Kunst des Software Design - Java

Das Krokodil

Achte das Single-Reponsibility-Prinzip.

Page 22: Die Kunst des Software Design - Java

„There should never be more than one reason for a class to change“

Robert C. Martin

Das Single-Responsibility-Prinzip

Page 23: Die Kunst des Software Design - Java

Bob will wissen was los ist

public class RentalCompany {

Map<String, Vehicle> fleet = new HashMap<String, Vehicle>();

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); System.out.println("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); }

public void rentVehicle(Vehicle vehicle, Customer customer) { System.out.println("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); }

}

> 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

Page 24: Die Kunst des Software Design - Java

public class RentalCompany {

Map<String, Vehicle> fleet = new HashMap<String, Vehicle>();

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); System.out.println("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); }

public void rentVehicle(Vehicle vehicle, Customer customer) { System.out.println("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); }

}

> 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

Was ist mit Problemen im Produktivbetrieb?

Bob will wissen was los ist

Page 25: Die Kunst des Software Design - Java

Debugging im Produktivbetrieb

public class RentalCompany {

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); switch (DEBUG_MODE) { case ECHO: System.out.println("Neues Auto im Fuhrpark: "+vehicle.getManufacturer()); break; case LOG: default: logger.info("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); break; } }

public void rentVehicle(Vehicle vehicle, Customer customer) { switch (DEBUG_MODE) { case ECHO: System.out.println("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); break; case LOG: default: logger.info("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); break; } }

Page 26: Die Kunst des Software Design - Java

public class RentalCompany {

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); switch (DEBUG_MODE) { case ECHO: System.out.println("Neues Auto im Fuhrpark: "+vehicle.getManufacturer()); break; case LOG: default: logger.info("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); break; } }

public void rentVehicle(Vehicle vehicle, Customer customer) { switch (DEBUG_MODE) { case ECHO: System.out.println("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); break; case LOG: default: logger.info("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); break;

Code Duplizierung macht die Anwendung langsamer und schwerer zu warten

Debugging im Produktivbetrieb

Page 27: Die Kunst des Software Design - Java

Wiederverwendbarkeit statt Copy-and-Paste

public class RentalCompany {

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); debug("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); }

public void rentVehicle(Vehicle vehicle, Customer customer) { debug("Neuer Mietvorgang: " + customer.getName() + " leiht " + vehicle.getManufacturer()); }

...

}

Page 28: Die Kunst des Software Design - Java

Wiederverwendbarkeit statt Copy-and-Paste

public class RentalCompany { ...

private void debug(String message) { switch (DEBUG_MODE) { case ECHO: System.out.println(message); break; case LOG: default: logger.info(message); break; } }}

Page 29: Die Kunst des Software Design - Java

public class RentalCompany { ...

private void debug(String message) { switch (DEBUG_MODE) { case ECHO: System.out.println(message); break; case LOG: default: logger.info(message); break; } }

Weitere Debugging-Ziele, wie E-Mails oder SMS, machen die debug()-Methode komplexer

und somit fehleranfälliger.

Wiederverwendbarkeit statt Copy-and-Paste

Page 30: Die Kunst des Software Design - Java

Atomare Probleme lösen

public abstract class RentalCompany { ... Eigenschaften und Methoden der Klasse ...

protected abstract void debug(String message);}

public class EchoingRentalCompany extends RentalCompany { protected void debug(String message) { System.out.println(message); }}

public class LoggingRentalCompany extends RentalCompany { protected void debug(String message) { Logger.getAnonymousLogger().info(message); }}

Page 31: Die Kunst des Software Design - Java

Atomare Probleme lösen

RentalCompany company;

switch (DEBUG_MODE) {case ECHO: company = new EchoingRentalCompany(); break;case LOG:default: company = new LoggingRentalCompany(); break;}

Car bmw = new Car("BMW", "blau");Customer stephan = new Customer(1, "Stephan Schmidt");Customer gerd = new Customer(2, "Gerd Schaufelberger");

company.addToFleet("bmw1", bmw);company.rentVehicle(bmw, stephan);company.returnVehicle(bmw);

Page 32: Die Kunst des Software Design - Java

RentalCompany company;

switch (DEBUG_MODE) {case ECHO: company = new EchoingRentalCompany(); break;case LOG:default: company = new LoggingRentalCompany(); break;}

Car bmw = new Car("BMW", "blau");Customer stephan = new Customer(1, "Stephan Schmidt");Customer gerd = new Customer(2, "Gerd Schaufelberger");

company.addToFleet("bmw1", bmw);company.rentVehicle(bmw, stephan);company.returnVehicle(bmw);

Die debug()-Methode steht nur Unterklassen von RentalCompany zur Verfügung.

Atomare Probleme lösen

Page 33: Die Kunst des Software Design - Java

Komposition statt Vererbung

public interface Debugger { void debug(String message);}

public class DebuggerEcho implements Debugger { { public void debug(String message) { System.out.println(message); }}

public class DebuggerLog implements Debugger { public void debug(String message) { Logger.getAnonymousLogger().info(message); }}

Page 34: Die Kunst des Software Design - Java

Komposition statt Vererbung

class RentalCompany { ... Eigenschaften der Klasse ...

public RentalCompany() { switch (DEBUG_MODE) { case ECHO: debugger = new DebuggerEcho(); break; case LOG: default: debugger = new DebuggerLog(); break; } }

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); debugger.debug("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); }

...}

Page 35: Die Kunst des Software Design - Java

Die Weisheit des Krokodils

Teile und herrsche.Jedes Modul soll genau eine Verantwortung übernehmen, und jede Verantwortung soll genau einem Modul zugeordnet werden.

Page 36: Die Kunst des Software Design - Java

Die Schildkröte

Achte das Open-Closed-Prinzip.

Page 37: Die Kunst des Software Design - Java

Das Open-Closed-Prinzip

„Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.“

Bertrand Meyer

Page 38: Die Kunst des Software Design - Java

Komposition statt Vererbung

class RentalCompany { ... Eigenschaften der Klasse ...

public RentalCompany() { switch (DEBUG_MODE) { case ECHO: debugger = new DebuggerEcho(); break; case LOG: default: debugger = new DebuggerLog(); break; } }

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); debugger.debug("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); }

...}

Page 39: Die Kunst des Software Design - Java

class RentalCompany { ... Eigenschaften der Klasse ...

public RentalCompany() { switch (DEBUG_MODE) { case ECHO: debugger = new DebuggerEcho(); break; case LOG: default: debugger = new DebuggerLog(); break; } }

public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); debugger.debug("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); }

...}

Die RentalCompany Klassen ist von anderen Klassen abhängig

Komposition statt Vererbung

Page 40: Die Kunst des Software Design - Java

Einsatz von Interfaces

public class RentalCompany { ... Eigenschaften der Klasse ...

public RentalCompany(Debugger debugger) { this.debugger = debugger; }

... weitere Methoden der Klasse ...}

Debugger debugger = new DebuggerEcho();RentalCompany company = new RentalCompany(debugger);

Page 41: Die Kunst des Software Design - Java

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.

Page 42: Die Kunst des Software Design - Java

Der Erpel

Achte das Hollywood-Prinzip.

Page 43: Die Kunst des Software Design - Java

Das Hollywood Prinzip

„Rufen Sie uns nicht an, wir werden Sie anrufen.“

Das Hollywood Prinzip

Page 44: Die Kunst des Software Design - Java

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

Page 45: Die Kunst des Software Design - Java

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

Page 46: Die Kunst des Software Design - Java

Dependency Injection

Debugger debugger = new DebuggerEcho();RentalCompany company = new RentalCompany(debugger);

Logger logger = new DateTimeLogger();RentalCompany company.setLogger(logger);

• 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

Constructor Injection

Setter Injection

Page 47: Die Kunst des Software Design - Java

Inversion-of-Control-Container

• Verringern den nötigen Boilerplate-Code

• Code, der nur nötig ist, um die Objekte zu „verdrahten“

• In der Java-Welt schon weit verbreitet

• Spring, Google Guice, Tapestry IoC, ...

Page 48: Die Kunst des Software Design - Java

Google Guice

• Basiert komplett auf Java-Code

• Keine externe Konfiguration

• Stattdessen Einsatz von Annotations

• Sehr leichtgewichtig

Page 49: Die Kunst des Software Design - Java

class RentalCompany { ... Eigenschaften der Klasse ...

@Inject public void setLogger(Logger logger) { this.logger = logger; }

...

}

Rufen Sie Bob nicht an, Bob ruft sie an.

Setter Injection

Page 50: Die Kunst des Software Design - Java

• Typen werden durch Modul an konkrete Implementierungen gebunden:

public class RentalCompanyModule extends AbstractModule { @Override protected void configure() { bind(Debugger.class).to(DebuggerEcho.class); }}

• Guice kann Injector liefern, der Instanzen erzeugt:

Injector injector = Guice.createInjector(new RentalCompanyModule());Debugger debugger = injector.getInstance(Debugger.class);debugger.debug("Debugging with Guice.");

// Injiziert Debugger auch in andere ObjekteRentalCompany company = injector.getInstance(RentalCompany.class);Car bmw = new Car("BMW", "silver");company.addToFleet("bmw", bmw);

Interfaces binden

Page 51: Die Kunst des Software Design - Java

public interface Formatter { public String format(String s);}

public class ReverseFormatter implements Formatter { public String format(String s) { return new StringBuffer(s).reverse().toString(); }}

public class UppercaseFormatter implements Formatter { public String format(String s) { return s.toUpperCase(); }}

Komplexeres Beispiel

• Neuer Typ „Formatter“, inkl. Implemetierungen:

Page 52: Die Kunst des Software Design - Java

public class DebuggerEcho implements Debugger { Formatter formatter;

@Inject public DebuggerEcho(Formatter formatter) { this.formatter = formatter; }

@Override public void debug(String message) { System.out.println(this.formatter.format(message)) }}

Komplexeres Beispiel

• Einsatz in DebuggerEcho:

Page 53: Die Kunst des Software Design - Java

public class MyApplication { private Debugger debugger; private Formatter formatter;

@Inject public MyApplication(Debugger debugger) { this.debugger = debugger; }

@Inject public void setFormatter(Formatter f) { formatter = f; }

public void doSomething(String s) { s = this.formatter.format(s); System.out.println("Sending '" + s + "' to Debugger."); this.debugger.debug(s); }}

Komplexeres Beispiel

• Und Einsatz in MyApplication:

Page 54: Die Kunst des Software Design - Java

public class MyApplication { private Debugger debugger; private Formatter formatter;

@Inject public MyApplication(Debugger debugger) { this.debugger = debugger; }

@Inject public void setFormatter(@Named("myFormatter") Formatter f) { formatter = f; }

public void doSomething(String s) { s = this.formatter.format(s); System.out.println("Sending '" + s + "' to Debugger."); this.debugger.debug(s); }}

Komplexeres Beispiel

• Benannte Injections:

Page 55: Die Kunst des Software Design - Java

public class MyApplicationModule extends AbstractModule {

@Override protected void configure() { bind(Debugger.class).to(DebuggerEcho.class); bind(Formatter.class).to(UppercaseFormatter.class); bind(Formatter.class).annotatedWith(Names.named("myFormatter")). to(ReverseFormatter.class); }}

Komplexeres Beispiel

• Und jetzt die Bindings dazu:

Injector injector = Guice.createInjector(new MyApplicationModule());MyApplication app = injector.getInstance(MyApplication.class);app.doSomething("Hollywood");

• Und jetzt die Bindings dazu:

Page 56: Die Kunst des Software Design - Java

Komplexeres Beispiel

Was wird jetzt ausgegeben?

Page 57: Die Kunst des Software Design - Java

Sending 'doowylloH' to Debugger.DOOWYLLOH

Komplexeres Beispiel

• Ausgabe des Beispiels

MyApplication

ReverseFormatter

DebuggerEcho UppercaseFormatter

Page 58: Die Kunst des Software Design - Java

• Verwenden einer Standardimplementierung.

• Objekte in einem Scope erzeugen, z.B. Singleton.

• Typen an Provider binden, die dann die tatsächlichen Objekte benötigen.

• Primitive Typen binden.

• Unterstützung für eigene Binding-Annotations.

Weitere Features von Guice

Page 59: Die Kunst des Software Design - Java

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="...">

<bean id="application" class="net.schst.tiercode.ioc.spring.MyApplication"> <constructor-arg ref="echo-debugger"/> <property name="formatter" ref="reverse-formatter"/> </bean>

<bean id="reverse-formatter" class="net.schst.tiercode.ioc.spring.ReverseFormatter"> </bean>

<bean id="uppercase-formatter" class="net.schst.tiercode.ioc.spring.UppercaseFormatter"> </bean>

<bean id="echo-debugger" class="net.schst.tiercode.ioc.spring.DebuggerEcho"> <constructor-arg ref="uppercase-formatter"/> </bean>

</beans>

Das selbe nochmal, mit Spring

• Konfiguration über XML:

Page 60: Die Kunst des Software Design - Java

ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-beans.xml");MyApplication app = context.getBean("application", MyApplication.class);app.doSomething("Hollywood");

Das selbe nochmal, mit Spring

• Erzeugen der Beans

Sending 'doowylloH' to Debugger.DOOWYLLOH

• Ausgabe:

Page 61: Die Kunst des Software Design - Java

<defines> <tag name="EchoDebugger" type="net.schst.tiercode.ioc.xjconf.DebuggerEcho" setter="setDebugger"/>

<tag name="ReverseFormatter" type="net.schst.tiercode.ioc.xjconf.ReverseFormatter" setter="setFormatter"/>

<tag name="UppercaseFormatter" type="net.schst.tiercode.ioc.xjconf.UppercaseFormatter" setter="setFormatter"/>

<tag name="Application" type="net.schst.tiercode.ioc.xjconf.MyApplication"/></defines>

Dasselbe nochmal, mit XJConf

• Tag-Definitionen über XML:

• Nicht ganz dasselbe:

• XJConf ist schlecht bei Constructor-Injection. Also alles auf Setter-Injection umgestellt.

Page 62: Die Kunst des Software Design - Java

<?xml version="1.0" encoding="UTF-8"?><configuration> <Application> <EchoDebugger> <UppercaseFormatter/> </EchoDebugger> <ReverseFormatter/> </Application></configuration>

Dasselbe nochmal, mit XJConf

• Definition der Objekte über XML:

Page 63: Die Kunst des Software Design - Java

DefinitionParser tagParser = new DefinitionParser();NamespaceDefinitions defs = tagParser.parse(".../xjconf/defines/defines.xml");

XmlReader conf = new XmlReader();conf.setTagDefinitions(defs);

conf.parse("src/main/resources/xjconf/conf/conf.xml");MyApplication app = (MyApplication) conf.getConfigValue("Application");app.doSomething("Hollywood");

Dasselbe nochmal, mit XJConf

• Erzeugen der Objekte:

Sending 'doowylloH' to Debugger.DOOWYLLOH

• Ausgabe:

Page 64: Die Kunst des Software Design - Java

Verwende ein DI Framework.Erleichtere dir das Verwalten von komplexen Objektstrukturen durch den Einsatz eines Dependency Injection Frameworks.

Die Weisheit des Erpel

Page 65: Die Kunst des Software Design - Java

Die Biene

Nutze Design Patterns.

Page 66: Die Kunst des Software Design - Java

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

Page 67: Die Kunst des Software Design - Java

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

Page 68: Die Kunst des Software Design - Java

Konkret vs Abstrakt

Abstrakt Konkret

AufgabeVerarbeiten von Debug-

Meldungen

AlgorithmenAusgeben per print(),

Schreiben eines Logfiles

ClientDie Klasse

RentalCompany

Page 69: Die Kunst des Software Design - Java

Konkret vs Abstrakt

Abstrakt Konkret

AufgabePersistieren von

Gästebucheinträgen

AlgorithmenSpeichern in Datenbank, Speichern in XML-Datei

Client Die Klasse Guestbook

Page 70: Die Kunst des Software Design - Java

Bobs erstes Design Pattern

Strategy-Pattern

Page 71: Die Kunst des Software Design - Java

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

Page 72: Die Kunst des Software Design - Java

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

Page 73: Die Kunst des Software Design - Java

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

Page 74: Die Kunst des Software Design - Java

Factory-Method-Pattern

public abstract class AbstractManufacturer<T extends Vehicle> { protected String name; public AbstractManufacturer(String name) { this.name = name; } public T sellVehicle() { return manufactureVehicle(); } protected abstract T manufactureVehicle();

}

Page 75: Die Kunst des Software Design - Java

Factory-Method-Pattern

public class CarManufacturer extends AbstractManufacturer<Car> {

protected Car manufactureVehicle() { return new Car(name, "black"); }

}

CarManufacturer bmwManufacturer = new CarManufacturer("BMW");Car bmw = bmwManufacturer.sellVehicle();

Page 76: Die Kunst des Software Design - Java

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.

Page 77: Die Kunst des Software Design - Java

Factory-Method-Pattern

Das Factory-Method-Pattern | 177

Definition des Patterns

Das Fabrikmethoden-Muster definiert eine Schnittstelle zur Erzeugung von Objekten. Es

verlagert aber die eigentliche Instanziierung in Unterklassen; es lässt die Unterklassen ent-

scheiden, welche konkreten Implementierungen verwendet werden.

Um dieses Ziel zu erreichen, müssen Sie die folgenden Schritte durchführen:

1. Implementieren Sie eine abstrakte Klasse, in der Sie eine oder mehrere abstrakte

Methoden deklarieren, die die Schnittstelle zum Erzeugen von Objekten vorgeben.

2. Fügen Sie dieser Klasse weitere Methoden hinzu, die Logik enthalten, die bei allen

konkreten Implementierungen identisch sind. Sie können in diesen Methoden be-

reits auf die abstrakte Fabrikmethode zugreifen.

3. Bilden Sie beliebig viele Unterklassen, in denen Sie verschiedene Implementierungen

der abstrakten Methode einfügen.

4. Verwenden Sie nun diese konkreten Unterklassen, um die tatsächlichen Objekte zu

instanziieren und Ihren Applikationscode von den konkreten Implementierungen zu

lösen.

Wann immer Sie eine Fabrikmethode verwenden möchten, achten Sie einfach darauf, die

hier gezeigten Schritte durchzuführen, und dem Erfolg Ihres Vorhabens steht nichts mehr

im Weg. Abbildung 4-2 zeigt Ihnen die Beziehungen zwischen den Beteiligten des Fac-

tory-Method-Patterns und illustriert noch einmal, wie das Pattern auf die Erzeugung der

Vehicle-Implementierungen angewandt wurde.

Abbildung 4-2: UML-Diagramm des Factory-Method-Patterns

+FactoryMethod() : Product

ConcreteCreator

+FactoryMethod() : Product+MethodA()

Creator

ConcreteProduct

Product

Vehicle-Interface

AbstractManufacturer-Klasse

Fabrikmethode manufactureVehicleerzeugt Car- bzw. Convertible-Instanzen

sell Vehicle-Methode, ruftFabrikmethode auf

CarManufacturer undConvertibleManufacturer

Implementierungen des Vehicle-Interfaces wie Car, Convertible etc.

!!"""#$#"%&'()*+,,-../0120.344..56012789.:*.;7<=76.)!!:..33>'&.33

Page 78: Die Kunst des Software Design - Java

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

Page 79: Die Kunst des Software Design - Java

Prototype-Pattern

public class SpecialEditionManufacturer { protected Map<String, Vehicle> prototypes = new HashMap<String, Vehicle>(); public void addSpecialEdition(String edition, Vehicle prototype) { prototypes.put(edition, prototype); } public Vehicle manufactureVehicle(String edition) throws UnknownSpecialEditionException {

if (prototypes.containsKey(edition)) { return prototypes.get(edition).clone(); }

throw new UnknownSpecialEditionException( "No prototype for special edition " + edition + " registered."); }

}

Page 80: Die Kunst des Software Design - Java

Prototype-Pattern

SpecialEditionManufacturer manufacturer = new SpecialEditionManufacturer();

Car golfElvis = new Car("VW", "silber");golfElvis.setAirConditioned(true); golfElvis.setGraphics("Gitarre"); manufacturer.addSpecialEdition("Golf Elvis Presley Edition", golfElvis);

Convertible golfStones = new Convertible("VW", "rot"); golfStones.setAirConditioned(false); golfStones.setGraphics("Zunge");manufacturer.addSpecialEdition("Golf Rolling Stones Edition", golfStones);

Vehicle golf1 = manufacturer.manufactureVehicle("Golf Elvis Presley Edition");Vehicle golf2 = manufacturer.manufactureVehicle("Golf Rolling Stones Edition");

Page 81: Die Kunst des Software Design - Java

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.

Page 82: Die Kunst des Software Design - Java

Prototype-Pattern

200 | Kapitel 4: Erzeugungsmuster

Der Einsatz des Prototype-Patterns reduziert die Anzahl der Klassen, die Ihre Applikation

benötigt, da Sie weniger Unterklassen bilden müssen, um die einzelnen Produkte zu

erzeugen. Stattdessen werden alle Produkte auf Basis der Prototypen von einer Klasse

erzeugt.

FallstrickeDas Prototype-Pattern verlangt lediglich, dass alle Ihre Klassen, aus denen Prototypen

gebildet werden sollen, geklont werden können. Dies erscheint auf den ersten Blick

jedoch einfacher, als dies in der Realität oft der Fall ist. Solange Ihre Prototypen ledig-

lich skalare Werte in ihren Eigenschaften speichern, reicht die Verwendung des clone-

Operators aus, aggregieren Ihre Prototypen jedoch weitere Objekte, müssen Sie eine

eigene __clone()-Methode implementieren, die sich um das Erstellen von Kopien für

diese Objekte kümmert.

Im folgenden Beispiel speichert eine Instanz eines Autos nicht nur die Information, ob

eine Klimaanlage vorhanden ist, sondern stattdessen direkt die Instanz dieser Klimaan-

lage. Für das Beispiel reicht eine konkrete Implementierung für alle Klimaanlagen aus, in

einer echten Anwendung würden Sie hier sicher ein Interface sowie verschiedene kon-

krete Implementierungen einsetzen. Die Klimaanlage speichert im Beispiel nur die Grad-

zahl, auf die sie eingestellt ist:

namespace de\phpdesignpatterns\vehicles\addons;

class AirCondition {

protected $degrees = 20;

Abbildung 4-6: UML-Diagramm des Prototype-Patterns

Vehicle-Interface

SpecialEditionManufacturerkennt alle verfügbarenPrototypen

__clone()-Methodein PHP nicht immer nötig

Implementierungen des Vehicle-Interface wieCar, Convertible etc.

!!"""#$#"%&'()*+,,-../0120.)!!..34012567.8*.95:;54.)!!8..<<='&.<<

Page 83: Die Kunst des Software Design - Java

Strukturmuster

• Strukturmuster befassen sich mit der Komposition von Objekten

• Zu den Strukturmustern gehören unter anderem

• Composite-Pattern

• Proxy-Pattern

• Adapter-Pattern

• Facade-Pattern

Page 84: Die Kunst des Software Design - Java

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

Page 85: Die Kunst des Software Design - Java

Composite-Pattern

public interface Debugger { void debug(String message);}

public class DebuggerEcho implements Debugger { { public void debug(String message) { System.out.println(message); }}

public class DebuggerLog implements Debugger { public void debug(String message) { Logger.getAnonymousLogger().info(message); }}

Page 86: Die Kunst des Software Design - Java

Composite-Pattern

public class CompositeDebugger implements Debugger {

protected List<Debugger> debuggers = new ArrayList<Debugger>(); public void addDebugger(Debugger debugger) { debuggers.add(debugger); } public void debug(String message) { for (Debugger debugger : debuggers) { debugger.debug(message); } }

}

CompositeDebugger compositeDebugger = new CompositeDebugger();compositeDebugger.addDebugger(new DebuggerEcho());compositeDebugger.addDebugger(new DebuggerLog());

Page 87: Die Kunst des Software Design - Java

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.

Page 88: Die Kunst des Software Design - Java

Composite-Pattern

212 | Kapitel 5: Strukturmuster

Weitere Anwendungen

Mit der Debugging-Funktionalität haben Sie bereits eine sehr beliebte Anwendung des

Kompositum-Patterns kennengelernt. So bietet das PEAR-Paket Log2, das eine ähnliche

Funktionalität wie das Debugging-Beispiel bereitstellt, auch eine Klasse, die als Komposi-

tum fungiert.

Eine weitere Anwendung findet das Kompositum zum Beispiel in Authentifizierungs-Fra-

meworks. Stellen Sie sich vor, in Ihrem System authentifizieren Sie Benutzer gegen den

firmeninternen LDAP-Server. Um allen Regeln des Software-Designs zu folgen, haben Sie

sämtlichen Quellcode, der für die Authentifizierung verwendet wird, in einer eigenen

Klasse gekapselt. Nun soll für eine neue Anwendung der Benutzerkreis erweitert werden,

und Sie möchten auch Ihren Kunden Zugriff erlauben. Kundendaten werden allerdings

nicht im LDAP-Server, sondern in einer Datenbank gespeichert, und somit muss auch die

Authentifizierung gegen diese Datenbank erfolgen. Sie möchten also die eingegebenen

Daten zuerst im LDAP-Server suchen und, wenn Sie dort keinen gültigen Benutzer gefun-

den haben, anhand der Datenbank entscheiden, ob es sich dabei um die Zugangsdaten

eines Kunden handelt. Auch dieses Problem lässt sich leicht über das Kompositum-Pat-

tern lösen, da dieses beide Authentifizierungsobjekte zu einem Baum zusammenführen

kann, gegen den Sie dann die Authentifizierung durchführen können.

Abbildung 5-3: UML-Diagramm des Composite-Patterns

2 http://pear.php.net/package/Log

+methodA()+methodB()

Component

+methodA()+methodB()

ConcreteComponent

+addChild(child : Component)+removeChild(child : Component)+methodA()+methodB()

Composite

Debugger-Schnittstelle mitMethode debug()

Konkrete Debugger-Implementierungen

DebuggerComposite-Klasse debug()-Methode delegiertAufruf an die anderen Debugger

DebuggerComposite kannbeliebig viele Debuggerspeichern

!!"""#$#"%&'()*+,,-../0120.)3)..45012678.9*.:6;<65.)!!9..33='&.33

Page 89: Die Kunst des Software Design - Java

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.

Page 90: Die Kunst des Software Design - Java

Adapter-Pattern

public class Automobile { private boolean ignited; private float milesDriven; public void drive(Direction direction, float miles) throws AutomobileException {

if (ignited) { milesDriven += miles; } else { throw new AutomobileException("ZŸndung ist nicht an."); } }

...}

Page 91: Die Kunst des Software Design - Java

Adapter-Pattern

public class AutomobileAdapter implements Vehicle { protected Automobile automobile; public AutomobileAdapter(Automobile automobile) { this.automobile = automobile; } public boolean moveForward(float miles) { try { automobile.drive(Direction.FORWARD, miles); return true; } catch (AutomobileException e) { return false; } }

...}

Page 92: Die Kunst des Software Design - Java

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.

Page 93: Die Kunst des Software Design - Java

Adapter-Pattern

220 | Kapitel 5: Strukturmuster

Definition des Patterns

Das Adapter-Muster passt die Schnittstelle einer Klasse an die vom Client erwartete Schnitt-

stelle an. Es ermöglicht die Zusammenarbeit von Klassen, die eigentlich aufgrund inkompa-

tibler Schnittstellen nicht zusammenarbeiten können.

Zur Verwendung des Adapters, um zwischen zwei inkompatiblen Schnittstellen zu ver-

mitteln, sind die folgenden Schritte nötig:

1. Lokalisieren Sie die Unterschiede zwischen der angebotenen und der geforderten

Schnittstelle.

2. Implementieren Sie eine neue Klasse, die die geforderte Schnittstelle bereitstellt.

3. Schaffen Sie eine Möglichkeit, das zu adaptierende Objekt an den Adapter zu über-

geben, verwenden Sie dazu zum Beispiel Dependency Injection.

4. Implementieren Sie alle von der Schnittstelle geforderten Methoden und delegieren

Sie die Anfragen an die entsprechenden Methoden des Ursprungsobjekts weiter.

5. Beachten Sie Unterschiede beim Signalisieren von Fehlern.

6. Verwenden Sie in Ihrer Applikation das Adapter-Objekt, um das Ursprungsobjekt zu

ummanteln.

Wenn Sie diese einfachen Schritte befolgen, adaptieren Sie leicht die verschiedensten

Schnittstellen in Ihrer Applikation. Abbildung 5-4 zeigt Ihnen noch einmal die am Adap-

ter-Pattern beteiligten Klassen und Interfaces und wie das Pattern auf das Problem der

abweichenden Schnittstelle der Automobile-Klasse angewandt wurde.

Abbildung 5-4: UML-Diagramm des Adapter-Patterns

+methodA()

+methodA()

«interface»Target

Client

Klassen, die das Vehicle-Interface nutzen, z.B.RentalCompany

Vehicle-Interface

Automobile-Klasse

AutomobileAdapterAdapter ruft die entsprechendenMethoden auf dem Automobile-Objekt auf

Adapter

+methodB()

Adaptee

!!"""#$#"%&'()*+,,-../0120.))!..34012567.8*.95:;54.)!!8..<<='&.<<

Page 94: Die Kunst des Software Design - Java

Verhaltensmuster

• Verhaltensmuster beschreiben die Interaktion zwischen Objekten.

• Zu den Verhaltensmuster gehören unter anderem

• Subject/Observer-Pattern

• Template-Method-Pattern

• Command-Pattern

• Iterator-Pattern

Page 95: Die Kunst des Software Design - Java

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

Page 96: Die Kunst des Software Design - Java

Template-Method-Pattern

public abstract class AbstractCar implements Vehicle {

public final void inspect() { System.out.println("FŸhre Inspektion durch"); replaceSparkPlugs(); checkTires(); if (isOilLevelLow()) { refillOil(); } } protected abstract void replaceSparkPlugs(); protected abstract void checkTires(); protected abstract boolean isOilLevelLow(); protected void refillOil() { System.out.println("FŸlle " + (300 - oilLevel) + "ml …l nach."); oilLevel = 300; }

...}

Page 97: Die Kunst des Software Design - Java

Template-Method-Pattern

public class Car extends AbstractCar {

protected void replaceSparkPlugs() { System.out.println("Ersetze Zündkerzen durch Modell AF34."); }

protected void checkTires() { System.out.println("Überprüfe Reifendruck, muss 2,0 bar sein."); }

protected boolean isOilLevelLow() { return oilLevel < 200; }

}

Page 98: Die Kunst des Software Design - Java

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.

Page 99: Die Kunst des Software Design - Java

Template-Method-Pattern

282 | Kapitel 6: Verhaltensmuster

Schablonenmethoden ermöglichen es, gemeinsamen Code herauszufaktorieren und

somit gemeinsames Verhalten nur einmal implementieren zu müssen.

Weitere Anwendungen

Eine weitere Anwendung der Schablonenmethode haben Sie bereits in Kapitel 3 beim

Einsatz des Factory-Method-Patterns kennengelernt. Dabei wird in einer Unterklasse der

Fabrik entschieden, wie ein Objekt erzeugt werden muss. Tatsächlich wird die Schablo-

nenmethode sehr häufig eingesetzt, wenn eine Fabrikmethode implementiert wird.

Eine abgewandelte Schablonenmethode bringt PHP schon mit. Mit der Funktion sort()

ermöglicht Ihnen PHP, ein Array mit skalaren Werten nach der Größe zu sortieren.

Sehen Sie sich dazu das folgende Beispiel an:

$ints = array(4053, 23, 283, 20032);

sort($ints);

foreach($ints as $int) {

print "{$int}\n";

}

PHP verwendet dazu einen Sortieralgorithmus, bei dem zwei nebeneinander liegende

Werte miteinander verglichen und, falls der zweite Wert kleiner ist als der erste, ver-

tauscht werden. Dies wird so lange wiederholt, bis sich die Werte in der richtigen Rei-

henfolge befinden.

Abbildung 6-2: UML-Diagramm des Template-Method-Patterns

#primitiveOperation()

ConcreteClass

+templateMethod()#primitiveOperation()

AbstractCar-Klasse

inspect()-Methode, ruft dieabstrakten Methoden auf,die in Unterklassen implementiertwerden

Car- und Convertible-Klassen

Implementieren checkTiresreplaceSparkPlugs etc.

AbstractClass

!!"""#$#"%&'()*+,,-../0120.)%)..34012567.8*.95:;54.)!!8..<<='&.<<

Page 100: Die Kunst des Software Design - Java

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.

Page 101: Die Kunst des Software Design - Java

Command-Pattern

public interface CarWashCommand { void execute(Car car); }

public class CarSimpleWashCommand implements CarWashCommand { public void execute(Car car) { System.out.println("Das Auto wird gewaschen"); }}

public class CarDryingCommand implements CarWashCommand { public void execute(Car car) { System.out.println("Das Auto wird getrocknet"); }}

Page 102: Die Kunst des Software Design - Java

Command-Pattern

public class CarWash {

protected Map<String, CarWashCommand[]> programs = new HashMap<String, CarWashCommand[]>(); public void addProgram(String name, CarWashCommand... commands) { programs.put(name, commands); } public void wash(String program, Car car) { for (CarWashCommand command : programs.get(program)) { command.execute(car); } }

}

Page 103: Die Kunst des Software Design - Java

Command-Pattern

CarWash wash = new CarWash();

wash.addProgram("standard", new CarSimpleWashCommand(), new CarDryingCommand());

wash.addProgram("komfort", new CarSimpleWashCommand(), new CarEngineWashCommand(), new CarDryingCommand(), new CarWaxingCommand());

Car bmw = new Car("BMW", "silber");wash.wash("standard", bmw);

Page 104: Die Kunst des Software Design - Java

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.

Page 105: Die Kunst des Software Design - Java

Command-Pattern

290 | Kapitel 6: Verhaltensmuster

Definition des Patterns

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.

Um das Command-Pattern zu implementieren, sind also die folgenden Schritte nötig:

1. Definieren Sie eine Schnittstelle für die einzelnen Befehle.

2. Implementieren Sie die konkreten Befehle, die diese Schnittstelle bereitstellen und

die einzelnen Aufträge in einer Klasse kapseln.

3. Schaffen Sie eine Möglichkeit, den Client mit einem oder mehreren dieser Befehle zu

parametrisieren.

Mit Client ist im aktuellen Beispiel die Waschanlage gemeint, also das Objekt, das die

Befehle verwendet. Dabei müssen die Befehle nicht immer in eine Warteschlange gestellt

werden. Bei anderen Anwendungen des Command-Patterns ist es auch denkbar, dass nur

ein Befehl übergeben wird, der bei Eintreten einer bestimmten Bedingung ausgeführt

wird. Es handelt sich trotzdem um ein Command-Pattern, da der Auftrag in einer Klasse

gekapselt wird und somit zur Laufzeit ausgetauscht werden kann. Abbildung 6-3 zeigt

Ihnen die im Command-Pattern beteiligten Akteure und wie diese miteinander in Verbin-

dung stehen.

Abbildung 6-3: UML-Diagramm des Command-Patterns

-state

+execute()+action()

CarWash, alsodie Waschanlage

Car-Objekte, die gewaschenwerden

Konkrete Implementierungender Wasch-Befehle, z.B.CarMotorWashCommand

CarWashCommand-Interface

+execute()

«interface»Command

Receiver

InvokerClient

ConcreteCommand

!!"""#$#"%&'()*+,,-../0120.)3!..45012678.3*.96:;65.)!!3..<<='&.<<

Page 106: Die Kunst des Software Design - Java

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

Page 107: Die Kunst des Software Design - Java

Nutze bestehende Lösungen.Abstrahiere Deine konkreten Probleme und wende Design Patterns als Vorlage für Deine Lösung an.

Die Weisheit der Biene

Page 108: Die Kunst des Software Design - Java

Die Bulldogge

Perfektionismus wird P-A-R-A-L-Y-S-E buchstabiert

Page 109: Die Kunst des Software Design - Java

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

Page 110: Die Kunst des Software Design - Java

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.

Page 111: Die Kunst des Software Design - Java

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

Page 112: Die Kunst des Software Design - Java

Emergenz

„Emergenz ist die spontane Herausbildung von komplexen Systemen und Strukturen durch eine Vielzahl von relativ einfachen Interaktionen.“

Wikipedia

Page 113: Die Kunst des Software Design - Java

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 (auf den Tipp des Krokodils hören) sind leichter zu testen.

• Je mehr Tests wir schreiben, desto mehr bemühen wir uns Code zu schreiben, der einfacher zu testen ist.

Page 114: Die Kunst des Software Design - Java

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

Page 115: Die Kunst des Software Design - Java

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

Page 116: Die Kunst des Software Design - Java

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

Page 117: Die Kunst des Software Design - Java

Permature Optimization

„Premature optimization is the root of all evil“Donald Knuth

Page 118: Die Kunst des Software Design - Java

Permature Optimization

• Premature Optimization beschreibt die Situation in der Design Entscheidungen aufgrund von Performance-Optimierungen getroffen werden

• Solche Optimierungen resultieren oft in unleserlicherem Code

• Michael A. Jackson über Optimierung

• Dont't do it.

• (For experts only) - Don't do it yet

Page 119: Die Kunst des Software Design - Java

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

Page 120: Die Kunst des Software Design - Java

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

Die Weisheit der Bulldogge

Page 121: Die Kunst des Software Design - Java

Der Regenwurm

Vermeide Accidential Complexity.

Page 122: Die Kunst des Software Design - Java

Vermeide Accidential Complexity

„Simplify essential complexity; diminish accidental complexity“

Neal Ford

Page 123: Die Kunst des Software Design - Java

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

Page 124: Die Kunst des Software Design - Java

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

Page 125: Die Kunst des Software Design - Java

Accidential Complexity

• JPA

• Rules Engine

• Aspect Oriented Programming

• komplexes Build System

• Stateful Webservices

Page 126: Die Kunst des Software Design - Java

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.

Page 127: Die Kunst des Software Design - Java

Fokussiere Dich auf das Problem.Implementiere Lösungen, die Probleme der Domäne lösen, ohne unnötige Komplexität einzuführen.

Die Weisheit des Regenwurms

Page 128: Die Kunst des Software Design - Java

Der Kugelfisch

Teste, teste, teste.

Page 129: Die Kunst des Software Design - Java

Unit Tests

„Im Unit Test werden kleinere Programmteile in Isolation von anderen Programmteilen getestet.“

Frank Westphal

Page 130: Die Kunst des Software Design - Java

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

• JUnit

• TestNG

Page 131: Die Kunst des Software Design - Java

Unit Tests

public class RentalCompanyTest extends TestCase { private RentalCompany rentalCompany; public void setUp() { rentalCompany = new RentalCompany(); } public void testAddToFleet() { rentalCompany.addToFleet("vw", new Car("VW", "silber")); int carCount = rentalCompany.countCarsInFleet(); assertEquals(1, carCount); }

}

Page 132: Die Kunst des Software Design - Java

Unit Tests

$ mvn test...------------------------------------------------------- T E S T S-------------------------------------------------------Running net.schst.tiercode.RentalCompanyTestTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.039 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESSFUL[INFO] ------------------------------------------------------------------------

Page 133: Die Kunst des Software Design - Java

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

Page 134: Die Kunst des Software Design - Java

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, auch für Java

Page 135: Die Kunst des Software Design - Java

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

Page 136: Die Kunst des Software Design - Java

Funktionsweise

Page 137: Die Kunst des Software Design - Java

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 JUnit Tests

Page 138: Die Kunst des Software Design - Java

Selenium in Bildern

Page 139: Die Kunst des Software Design - Java

Selenium in Bildern

Page 140: Die Kunst des Software Design - Java

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

Page 141: Die Kunst des Software Design - Java

Continuous Integration

SVN Repository CI Server

Page 142: Die Kunst des Software Design - Java

Hudson

Page 143: Die Kunst des Software Design - Java

Hudson @ 1&1

Page 144: Die Kunst des Software Design - Java

Mehr Tools

Page 145: Die Kunst des Software Design - Java

Better safe than sorry.Schaffe dir Sicherheitsnetze durch automatisierte Tests auf verschiedenen Ebenen deiner Applikation.

Integriere regelmäßig und stelle Fehlerfrühzeitig fest.

Die Weisheit des Kugelfischs

Page 146: Die Kunst des Software Design - Java

Der Waschbär

Halte deinen Code sauber.

Page 147: Die Kunst des Software Design - Java

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

Page 148: Die Kunst des Software Design - Java

• Checkstyle kann Code gegen verschiedene Regeln prüfen

• Liefert bereits die Regelsets von Sun mit, ist jedoch erweiterbar (hört auf die Schildkröte)

Coding Standards in Java

Page 149: Die Kunst des Software Design - Java

Verwende Name die ihre Absicht aufdecken

• Namen von Variablen, Methoden oder Klassen, sollten folgende Fragen beantworten:

• Warum existiert die Variable (Methode oder Klasse)?

• Was macht sie?

• Wie wird sie verwendet?

• int d = 1; // elapsed time in daysint elapsedTimeInDays = 1;

Page 150: Die Kunst des Software Design - Java

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/

Page 151: Die Kunst des Software Design - Java

Benennung

Page 152: Die Kunst des Software Design - Java

Kommentare

• Gute Kommentare

• @todo-Kommentare

• Informative Kommentare

• Javadocs in öffentlichen APIs

• Schlechte Kommentare

• Redundante Kommentare

• Postionsmarkierungen

• Auskommentierter Code

Kommentare sind keine Ausrede für schlechten Code.

Page 153: Die Kunst des Software Design - Java

DRY - Don‘t Repeat Yourself

„Every piece of knowlege must have a single, unambiguous, authoritative representation within a system“

Andrew Hunt and Dave Thomas

Page 154: Die Kunst des Software Design - Java

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.

Page 155: Die Kunst des Software Design - Java

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.

Page 156: Die Kunst des Software Design - Java

Die Pfadfinder-Regel

„Leave the campground cleaner than you found it“

Robert C. Martin

Page 157: Die Kunst des Software Design - Java

Die Pfadfinder-Regel

Page 158: Die Kunst des Software Design - Java

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 besserenBreche eine Funktion auf, die zu groß istEliminiere ein kleine Duplizierung

• Software muss dauerhaft sauber gehalten werden

Page 159: Die Kunst des Software Design - Java

Dreckiger Code führt zu dreckigem Design.Lass auch bei Detail-Fragen die gleiche Sorgfalt walten wie bei großen Design-Entscheidungen.

Die Weisheit des Waschbärs

Page 160: Die Kunst des Software Design - Java

Die Ente

Das Ende.

Page 161: Die Kunst des Software Design - Java

Die Leseratte

Page 162: Die Kunst des Software Design - Java

Fragen?

Vielen Dank für die Aufmerksamkeit.

Bildquellen © iStockphoto.com

[email protected]@1und1.de