SlideShare uma empresa Scribd logo
1 de 162
Baixar para ler offline
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
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
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
Software Design




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



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




 Stabilität   Sicherheit    Flexibilität   Performance
Das Eichhörnchen

Kapsle Daten und Algorithmen.
Von der realen Welt ...

                          • Bob hat eine Autovermietung


                          • Er muss Autos einkaufen


                          • Er vermietet unterschiedliche
                            Modelle


                          • Er vermietet Autos zu verschiedenen
                            Preisen
... 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() {}

 }
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;
             }
         }
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;
             }
         }
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;
                  }

              }
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) {}

}
Bobs Geschäft

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

  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 ...
  }
Bobs Geschäft

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

  public class RentalAction {
    ...

      public void markCarReturend() {
        markCarReturend(new Date());
      }

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

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



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

   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

       public class RentalCompany {

           ...

           private boolean isVehicleAvailable(Vehicle vehicle) {

               for (RentalAction rentalAction : rentalActions) {

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

               }

               return true;
           }

       }
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;
          }

      }
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.
Das Krokodil

Achte das Single-Reponsibility-Prinzip.
Das Single-Responsibility-Prinzip




 „There should never be more than one reason for
 a class to change“
 Robert C. Martin
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
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);

     }       Was ist mit Problemen im Produktivbetrieb?
       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
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;
    }
  }
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;   Code Duplizierung macht die Anwendung
  }
                    langsamer und schwerer zu warten
  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;
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());
    }

    ...

}
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;
      }
    }
}
Wiederverwendbarkeit statt Copy-and-Paste

public class RentalCompany {
  ...

      Weitere Debugging-Ziele, wie E-Mails oder SMS,
  private void debug(String message) {
    switch (DEBUG_MODE) {
         machen die debug()-Methode komplexer
    case ECHO:
      System.out.println(message);
      break;
    case LOG:    und somit fehleranfälliger.
    default:
      logger.info(message);
      break;
    }
  }
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);
  }
}
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);
Atomare Probleme lösen

RentalCompany company;

switch (DEBUG_MODE) {
case ECHO:
  company = new EchoingRentalCompany();
  break;
case LOG:
       Die debug()-Methode steht nur Unterklassen von
default:
  company = new LoggingRentalCompany();

}
  break;
               RentalCompany zur Verfügung.
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);
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);
  }
}
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());
    }

    ...
}
Die Weisheit des Krokodils



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

Achte das Open-Closed-Prinzip.
Das Open-Closed-Prinzip




 „Software entities (classes, modules, functions,
 etc.) should be open for extension, but closed for
 modification.“
 Bertrand Meyer
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());
    }

    ...
}
Komposition statt Vererbung

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

    public RentalCompany() {
      switch (DEBUG_MODE) {
      case ECHO:
        debugger = new DebuggerEcho();
        break;
      case LOG:
      default:    Die RentalCompany Klassen ist von
                      anderen Klassen abhängig
        debugger = new DebuggerLog();
        break;
      }
    }

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

    ...
}
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);
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.
Der Erpel

Achte das Hollywood-Prinzip.
Das Hollywood Prinzip




 „Rufen Sie uns nicht an, wir werden Sie anrufen.“
 Das Hollywood Prinzip
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
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
Dependency Injection
 Debugger debugger = new DebuggerEcho();
 RentalCompany company = new RentalCompany(debugger);   Constructor Injection
 Logger logger = new DateTimeLogger();
 RentalCompany 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
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, ...
Google Guice

• Basiert komplett auf Java-Code


• Keine externe Konfiguration


• Stattdessen Einsatz von Annotations


• Sehr leichtgewichtig
Rufen Sie Bob nicht an, Bob ruft sie an.

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

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

     ...

 }
Interfaces binden

• 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 Objekte
 RentalCompany company = injector.getInstance(RentalCompany.class);
 Car bmw = new Car("BMW", "silver");
 company.addToFleet("bmw", bmw);
Komplexeres Beispiel

• Neuer Typ „Formatter“, inkl. Implemetierungen:

 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

• Einsatz in DebuggerEcho:

 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

• Und Einsatz in MyApplication:

 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

• Benannte Injections:

 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

• Und jetzt die Bindings dazu:

 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);
     }
 }


• Und jetzt die Bindings dazu:
 Injector injector = Guice.createInjector(new MyApplicationModule());
 MyApplication app = injector.getInstance(MyApplication.class);
 app.doSomething("Hollywood");
Komplexeres Beispiel




          Was wird jetzt ausgegeben?
Komplexeres Beispiel

• Ausgabe des Beispiels

 Sending 'doowylloH' to Debugger.
 DOOWYLLOH




                                    DebuggerEcho   UppercaseFormatter


         MyApplication


                               ReverseFormatter
Weitere Features von Guice

• 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.
Das selbe nochmal, mit Spring

• Konfiguration über XML:
 <?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

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




• Ausgabe:

 Sending 'doowylloH' to Debugger.
 DOOWYLLOH
Dasselbe nochmal, mit XJConf

• Tag-Definitionen über XML:
  <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>



• Nicht ganz dasselbe:

 • XJConf ist schlecht bei Constructor-Injection. Also alles auf Setter-Injection
   umgestellt.
Dasselbe nochmal, mit XJConf

• Definition der Objekte über XML:
 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
   <Application>
     <EchoDebugger>
       <UppercaseFormatter/>
     </EchoDebugger>
     <ReverseFormatter/>
   </Application>
 </configuration>
Dasselbe nochmal, mit XJConf

• Erzeugen der Objekte:
 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");


• Ausgabe:

 Sending 'doowylloH' to Debugger.
 DOOWYLLOH
Die Weisheit des Erpel



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

Nutze Design Patterns.
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
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
Konkret vs Abstrakt


          Abstrakt            Konkret

                       Verarbeiten von Debug-
          Aufgabe
                             Meldungen

                        Ausgeben per print(),
         Algorithmen
                       Schreiben eines Logfiles

                            Die Klasse
           Client
                          RentalCompany
Konkret vs Abstrakt


          Abstrakt            Konkret

                          Persistieren von
          Aufgabe
                         Gästebucheinträgen

                       Speichern in Datenbank,
         Algorithmen
                       Speichern in XML-Datei

           Client       Die Klasse Guestbook
Bobs erstes Design Pattern




          Strategy-Pattern
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
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
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
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();

 }
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();
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.
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
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
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.");
     }

 }
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");
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.
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
              Abbildung 4-6: UML-Diagramm des Prototyp
                                                                                              plikation
                                                 reduziert die Anzahl der Klassen, die Ihre Ap
              Der Einsatz des Prototype-Patterns                                               ukte zu
                                                  sen bild en 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
Strukturmuster

• Strukturmuster befassen sich mit der Komposition von Objekten


• Zu den Strukturmustern gehören unter anderem


  • Composite-Pattern


  • Proxy-Pattern


  • Adapter-Pattern


  • Facade-Pattern
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
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);
   }
 }
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());
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.
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
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.
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.");
         }
     }

     ...
 }
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;
       }
     }

     ...
 }
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.
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
                                          Interfa ces 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

                                       apter-Patterns
     Abbildung 5-4: UML-Diagramm des Ad
Verhaltensmuster

• Verhaltensmuster beschreiben die Interaktion zwischen Objekten.


• Zu den Verhaltensmuster gehören unter anderem


  • Subject/Observer-Pattern


  • Template-Method-Pattern


  • Command-Pattern


  • Iterator-Pattern
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
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;
     }

     ...
 }
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;
     }

 }
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.
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
                Abbildung 6-2: UML-Diagramm des Templat
                                                                                  ktorieren und
                                                   es, gemeinsamen Code herauszufa
                Schablonenmethoden ermöglichen                      n zu müssen.
                somit gemeinsames Verhalten nur einmal implementiere
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.
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");
   }
 }
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);
       }
     }

 }
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);
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.
parametrisieren.
                                                                    t, also das Objekt, das die
     Mit Client ist im aktuellen 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
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
Die Weisheit der Biene



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

Perfektionismus wird
P-A-R-A-L-Y-S-E buchstabiert
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
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.
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
Emergenz




„Emergenz ist die spontane Herausbildung von
komplexen Systemen und Strukturen durch eine
Vielzahl von relativ einfachen Interaktionen.“
Wikipedia
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.
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
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
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
Permature Optimization




 „Premature optimization is the root of all evil“
 Donald Knuth
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
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
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
Der Regenwurm

Vermeide Accidential Complexity.
Vermeide Accidential Complexity




 „Simplify essential complexity; diminish
 accidental complexity“
 Neal Ford
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
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
Accidential Complexity

• JPA


• Rules Engine


• Aspect Oriented Programming


• komplexes Build System


• Stateful Webservices
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.
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.
Der Kugelfisch

Teste, teste, teste.
Unit Tests




 „Im Unit Test werden kleinere Programmteile in
 Isolation von anderen Programmteilen getestet.“
 Frank Westphal
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
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);
     }

 }
Unit Tests



 $ mvn test
 ...
 -------------------------------------------------------
  T E S T S
 -------------------------------------------------------
 Running net.schst.tiercode.RentalCompanyTest
 Tests 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] ------------------------------------------------------------------------
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
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
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
Funktionsweise
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
Selenium in Bildern
Selenium in Bildern
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
Continuous Integration

      SVN Repository     CI Server
Hudson
Hudson @ 1&1
Mehr Tools
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.
Der Waschbär

Halte deinen Code sauber.
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
Coding Standards in 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)
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 days
    int elapsedTimeInDays = 1;
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/
Benennung
Kommentare

• Gute Kommentare                     • Schlechte Kommentare


  • @todo-Kommentare                     • Redundante Kommentare


  • Informative Kommentare               • Postionsmarkierungen


  • Javadocs in öffentlichen APIs        • Auskommentierter Code




         Kommentare sind keine Ausrede für schlechten Code.
DRY - Don‘t Repeat Yourself




 „Every piece of knowlege must have a single,
 unambiguous, authoritative representation
 within a system“
 Andrew Hunt and Dave Thomas
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.
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.
Die Pfadfinder-Regel




 „Leave the campground cleaner than you found it“
 Robert C. Martin
Die Pfadfinder-Regel
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
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.
Die Ente
Das Ende.
Die Leseratte
Fragen?
Vielen Dank für die Aufmerksamkeit.




Bildquellen © iStockphoto.com

Kontakt
holger.rueprich@1und1.de
stephan.schmidt@1und1.de

Mais conteúdo relacionado

Semelhante a Die Kunst des Software Design - Java

Agiles Modellieren mit Domain Specific Languages
Agiles Modellieren mit Domain Specific LanguagesAgiles Modellieren mit Domain Specific Languages
Agiles Modellieren mit Domain Specific LanguagesDominik Hirt
 
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...GFU Cyrus AG
 
Domain-Driven Design (DDD): Implementierung einer universellen Turing-Maschine
Domain-Driven Design (DDD): Implementierung einer universellen Turing-MaschineDomain-Driven Design (DDD): Implementierung einer universellen Turing-Maschine
Domain-Driven Design (DDD): Implementierung einer universellen Turing-Maschinegedoplan
 
Dokumentation durch automatisierte Akzeptanztests
Dokumentation durch automatisierte AkzeptanztestsDokumentation durch automatisierte Akzeptanztests
Dokumentation durch automatisierte AkzeptanztestsSebastian Sanitz
 
YAFOWIL - Webformulare in Python ohne Kopfschmerzen
YAFOWIL - Webformulare in Python ohne KopfschmerzenYAFOWIL - Webformulare in Python ohne Kopfschmerzen
YAFOWIL - Webformulare in Python ohne KopfschmerzenJens Klein
 
Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...
Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...
Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...e-dialog GmbH
 
Von 0 auf 100 - Performance im Web
Von 0 auf 100 - Performance im WebVon 0 auf 100 - Performance im Web
Von 0 auf 100 - Performance im WebSebastian Springer
 
Typ-sichere DSLs
Typ-sichere DSLsTyp-sichere DSLs
Typ-sichere DSLsWerner Keil
 
Große Applikationen mit AngularJS
Große Applikationen mit AngularJSGroße Applikationen mit AngularJS
Große Applikationen mit AngularJSSebastian Springer
 
Best Practices für TDD in JavaScript
Best Practices für TDD in JavaScriptBest Practices für TDD in JavaScript
Best Practices für TDD in JavaScriptSebastian Springer
 
Connecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbindenConnecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbindeninovex GmbH
 
Connecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbindenConnecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbindeninovex GmbH
 
APIs mit Zend\Expressive erstellen
APIs mit Zend\Expressive erstellenAPIs mit Zend\Expressive erstellen
APIs mit Zend\Expressive erstellenRalf Eggert
 
Open icf (open identity connector framework) @ forgerock deutsch
Open icf (open identity connector framework) @ forgerock   deutschOpen icf (open identity connector framework) @ forgerock   deutsch
Open icf (open identity connector framework) @ forgerock deutschHanns Nolan
 
Lightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPALightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPAmh0708
 
.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1Manfred Steyer
 

Semelhante a Die Kunst des Software Design - Java (20)

Mut zur Fachlichkeit
Mut zur FachlichkeitMut zur Fachlichkeit
Mut zur Fachlichkeit
 
Agiles Modellieren mit Domain Specific Languages
Agiles Modellieren mit Domain Specific LanguagesAgiles Modellieren mit Domain Specific Languages
Agiles Modellieren mit Domain Specific Languages
 
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
Java Code Quality: Gute Software braucht guten Code - Regeln für verständlich...
 
Domain-Driven Design (DDD): Implementierung einer universellen Turing-Maschine
Domain-Driven Design (DDD): Implementierung einer universellen Turing-MaschineDomain-Driven Design (DDD): Implementierung einer universellen Turing-Maschine
Domain-Driven Design (DDD): Implementierung einer universellen Turing-Maschine
 
Typescript
TypescriptTypescript
Typescript
 
Dokumentation durch automatisierte Akzeptanztests
Dokumentation durch automatisierte AkzeptanztestsDokumentation durch automatisierte Akzeptanztests
Dokumentation durch automatisierte Akzeptanztests
 
YAFOWIL - Webformulare in Python ohne Kopfschmerzen
YAFOWIL - Webformulare in Python ohne KopfschmerzenYAFOWIL - Webformulare in Python ohne Kopfschmerzen
YAFOWIL - Webformulare in Python ohne Kopfschmerzen
 
Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...
Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...
Google Analytics Konferenz 2015: WORKSHOP: Enhanced ECommerce (Michaela Linha...
 
Von 0 auf 100 - Performance im Web
Von 0 auf 100 - Performance im WebVon 0 auf 100 - Performance im Web
Von 0 auf 100 - Performance im Web
 
Typ-sichere DSLs
Typ-sichere DSLsTyp-sichere DSLs
Typ-sichere DSLs
 
Angular2
Angular2Angular2
Angular2
 
Große Applikationen mit AngularJS
Große Applikationen mit AngularJSGroße Applikationen mit AngularJS
Große Applikationen mit AngularJS
 
GWT – Google Web Toolkit in der Praxis
GWT – Google Web Toolkit in der PraxisGWT – Google Web Toolkit in der Praxis
GWT – Google Web Toolkit in der Praxis
 
Best Practices für TDD in JavaScript
Best Practices für TDD in JavaScriptBest Practices für TDD in JavaScript
Best Practices für TDD in JavaScript
 
Connecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbindenConnecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbinden
 
Connecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbindenConnecting Android - Externe Hardware mit dem grünen Roboter verbinden
Connecting Android - Externe Hardware mit dem grünen Roboter verbinden
 
APIs mit Zend\Expressive erstellen
APIs mit Zend\Expressive erstellenAPIs mit Zend\Expressive erstellen
APIs mit Zend\Expressive erstellen
 
Open icf (open identity connector framework) @ forgerock deutsch
Open icf (open identity connector framework) @ forgerock   deutschOpen icf (open identity connector framework) @ forgerock   deutsch
Open icf (open identity connector framework) @ forgerock deutsch
 
Lightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPALightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPA
 
.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1.NET Summit 2016 in München: ASP.NET Core 1
.NET Summit 2016 in München: ASP.NET Core 1
 

Mais de Stephan Schmidt

Das Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based ServicesDas Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based ServicesStephan Schmidt
 
23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software Entwicklung in Teams wissen solltenStephan Schmidt
 
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software-Entwicklung in Teams wissen solltenStephan Schmidt
 
Continuous Integration mit Jenkins
Continuous Integration mit JenkinsContinuous Integration mit Jenkins
Continuous Integration mit JenkinsStephan Schmidt
 
Der Erfolgreiche Programmierer
Der Erfolgreiche ProgrammiererDer Erfolgreiche Programmierer
Der Erfolgreiche ProgrammiererStephan Schmidt
 
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.Stephan Schmidt
 
Software-Entwicklung Im Team
Software-Entwicklung Im TeamSoftware-Entwicklung Im Team
Software-Entwicklung Im TeamStephan Schmidt
 
JSON-RPC Proxy Generation with PHP 5
JSON-RPC Proxy Generation with PHP 5JSON-RPC Proxy Generation with PHP 5
JSON-RPC Proxy Generation with PHP 5Stephan Schmidt
 
Declarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPDeclarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPStephan Schmidt
 
XML and Web Services with PHP5 and PEAR
XML and Web Services with PHP5 and PEARXML and Web Services with PHP5 and PEAR
XML and Web Services with PHP5 and PEARStephan Schmidt
 
The Big Documentation Extravaganza
The Big Documentation ExtravaganzaThe Big Documentation Extravaganza
The Big Documentation ExtravaganzaStephan Schmidt
 
Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5Stephan Schmidt
 
Component and Event-Driven Architectures in PHP
Component and Event-Driven Architectures in PHPComponent and Event-Driven Architectures in PHP
Component and Event-Driven Architectures in PHPStephan Schmidt
 
Session Server - Maintaing State between several Servers
Session Server - Maintaing State between several ServersSession Server - Maintaing State between several Servers
Session Server - Maintaing State between several ServersStephan Schmidt
 
XML Transformations With PHP
XML Transformations With PHPXML Transformations With PHP
XML Transformations With PHPStephan Schmidt
 
Inroduction to XSLT with PHP4
Inroduction to XSLT with PHP4Inroduction to XSLT with PHP4
Inroduction to XSLT with PHP4Stephan Schmidt
 
XML-Socket-Server zur Kommunikation mit Flash
XML-Socket-Server zur Kommunikation mit FlashXML-Socket-Server zur Kommunikation mit Flash
XML-Socket-Server zur Kommunikation mit FlashStephan Schmidt
 
Interprozesskommunikation mit PHP
Interprozesskommunikation mit PHPInterprozesskommunikation mit PHP
Interprozesskommunikation mit PHPStephan Schmidt
 

Mais de Stephan Schmidt (20)

Das Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based ServicesDas Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based Services
 
23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software Entwicklung in Teams wissen sollten
 
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten
 
Continuous Integration mit Jenkins
Continuous Integration mit JenkinsContinuous Integration mit Jenkins
Continuous Integration mit Jenkins
 
PHP mit Paul Bocuse
PHP mit Paul BocusePHP mit Paul Bocuse
PHP mit Paul Bocuse
 
Der Erfolgreiche Programmierer
Der Erfolgreiche ProgrammiererDer Erfolgreiche Programmierer
Der Erfolgreiche Programmierer
 
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.
23 Dinge, die Sie über Software-Entwicklung in Teams wissen sollten.
 
Software-Entwicklung Im Team
Software-Entwicklung Im TeamSoftware-Entwicklung Im Team
Software-Entwicklung Im Team
 
JSON-RPC Proxy Generation with PHP 5
JSON-RPC Proxy Generation with PHP 5JSON-RPC Proxy Generation with PHP 5
JSON-RPC Proxy Generation with PHP 5
 
Declarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHPDeclarative Development Using Annotations In PHP
Declarative Development Using Annotations In PHP
 
XML and Web Services with PHP5 and PEAR
XML and Web Services with PHP5 and PEARXML and Web Services with PHP5 and PEAR
XML and Web Services with PHP5 and PEAR
 
The Big Documentation Extravaganza
The Big Documentation ExtravaganzaThe Big Documentation Extravaganza
The Big Documentation Extravaganza
 
Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5Go OO! - Real-life Design Patterns in PHP 5
Go OO! - Real-life Design Patterns in PHP 5
 
Component and Event-Driven Architectures in PHP
Component and Event-Driven Architectures in PHPComponent and Event-Driven Architectures in PHP
Component and Event-Driven Architectures in PHP
 
Session Server - Maintaing State between several Servers
Session Server - Maintaing State between several ServersSession Server - Maintaing State between several Servers
Session Server - Maintaing State between several Servers
 
XML Transformations With PHP
XML Transformations With PHPXML Transformations With PHP
XML Transformations With PHP
 
PEAR For The Masses
PEAR For The MassesPEAR For The Masses
PEAR For The Masses
 
Inroduction to XSLT with PHP4
Inroduction to XSLT with PHP4Inroduction to XSLT with PHP4
Inroduction to XSLT with PHP4
 
XML-Socket-Server zur Kommunikation mit Flash
XML-Socket-Server zur Kommunikation mit FlashXML-Socket-Server zur Kommunikation mit Flash
XML-Socket-Server zur Kommunikation mit Flash
 
Interprozesskommunikation mit PHP
Interprozesskommunikation mit PHPInterprozesskommunikation mit PHP
Interprozesskommunikation mit PHP
 

Die Kunst des Software Design - Java

  • 1. 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
  • 2. 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
  • 3. 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
  • 4. Software Design „Software Design is a process of problem solving and planning for a software solution.“ Wikipedia
  • 5. Kunst „Das Wort Kunst bezeichnet im weitesten Sinne jede entwickelte Tätigkeit, die auf Wissen, Übung, Wahrnehmung, Vorstellung und Intuition gegründet ist.“ Wikipedia
  • 6. Ziele von Software Design Stabilität Sicherheit Flexibilität Performance
  • 8. Von der realen Welt ... • Bob hat eine Autovermietung • Er muss Autos einkaufen • Er vermietet unterschiedliche Modelle • Er vermietet Autos zu verschiedenen Preisen
  • 9. ... 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() {} }
  • 10. 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; } }
  • 11. 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; } }
  • 12. 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; } }
  • 13. 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) {} }
  • 14. Bobs Geschäft • Klassen übertragen nicht nur physikalische Dinge, sondern auch Abläufe 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 ... }
  • 15. Bobs Geschäft • Klassen übertragen nicht nur physikalische Dinge, sondern auch Abläufe public class RentalAction { ... public void markCarReturend() { markCarReturend(new Date()); } public void markCarReturend(Date date) { returnDate = date; } public boolean isReturend() { return returnDate != null; } }
  • 16. Kapsle auch Algorithmen Kapsle nicht nur Daten sondern auch Algorithmen in den Methoden deiner Klassen, um komplexe Operationen zentral an einer Stelle zu implementieren.
  • 17. Bobs Firma 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; } }
  • 18. 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; } }
  • 19. 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; } }
  • 20. 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.
  • 21. Das Krokodil Achte das Single-Reponsibility-Prinzip.
  • 22. Das Single-Responsibility-Prinzip „There should never be more than one reason for a class to change“ Robert C. Martin
  • 23. 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
  • 24. 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); } Was ist mit Problemen im Produktivbetrieb? 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
  • 25. 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; } }
  • 26. 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; Code Duplizierung macht die Anwendung } langsamer und schwerer zu warten 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;
  • 27. 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()); } ... }
  • 28. 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; } } }
  • 29. Wiederverwendbarkeit statt Copy-and-Paste public class RentalCompany { ... Weitere Debugging-Ziele, wie E-Mails oder SMS, private void debug(String message) { switch (DEBUG_MODE) { machen die debug()-Methode komplexer case ECHO: System.out.println(message); break; case LOG: und somit fehleranfälliger. default: logger.info(message); break; } }
  • 30. 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); } }
  • 31. 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);
  • 32. Atomare Probleme lösen RentalCompany company; switch (DEBUG_MODE) { case ECHO: company = new EchoingRentalCompany(); break; case LOG: Die debug()-Methode steht nur Unterklassen von default: company = new LoggingRentalCompany(); } break; RentalCompany zur Verfügung. 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);
  • 33. 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); } }
  • 34. 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()); } ... }
  • 35. Die Weisheit des Krokodils Teile und herrsche. Jedes Modul soll genau eine Verantwortung übernehmen, und jede Verantwortung soll genau einem Modul zugeordnet werden.
  • 36. Die Schildkröte Achte das Open-Closed-Prinzip.
  • 37. Das Open-Closed-Prinzip „Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.“ Bertrand Meyer
  • 38. 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()); } ... }
  • 39. Komposition statt Vererbung class RentalCompany { ... Eigenschaften der Klasse ... public RentalCompany() { switch (DEBUG_MODE) { case ECHO: debugger = new DebuggerEcho(); break; case LOG: default: Die RentalCompany Klassen ist von anderen Klassen abhängig debugger = new DebuggerLog(); break; } } public void addToFleet(String id, Vehicle vehicle) { fleet.put(id, vehicle); debugger.debug("Neues Auto im Fuhrpark: " + vehicle.getManufacturer()); } ... }
  • 40. 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);
  • 41. 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.
  • 42. Der Erpel Achte das Hollywood-Prinzip.
  • 43. Das Hollywood Prinzip „Rufen Sie uns nicht an, wir werden Sie anrufen.“ Das Hollywood Prinzip
  • 44. 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
  • 45. 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
  • 46. Dependency Injection Debugger debugger = new DebuggerEcho(); RentalCompany company = new RentalCompany(debugger); Constructor Injection Logger logger = new DateTimeLogger(); RentalCompany 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
  • 47. 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, ...
  • 48. Google Guice • Basiert komplett auf Java-Code • Keine externe Konfiguration • Stattdessen Einsatz von Annotations • Sehr leichtgewichtig
  • 49. Rufen Sie Bob nicht an, Bob ruft sie an. class RentalCompany { ... Eigenschaften der Klasse ... @Inject public void setLogger(Logger logger) { Setter Injection this.logger = logger; } ... }
  • 50. Interfaces binden • 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 Objekte RentalCompany company = injector.getInstance(RentalCompany.class); Car bmw = new Car("BMW", "silver"); company.addToFleet("bmw", bmw);
  • 51. Komplexeres Beispiel • Neuer Typ „Formatter“, inkl. Implemetierungen: 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(); } }
  • 52. Komplexeres Beispiel • Einsatz in DebuggerEcho: 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)) } }
  • 53. Komplexeres Beispiel • Und Einsatz in MyApplication: 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); } }
  • 54. Komplexeres Beispiel • Benannte Injections: 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); } }
  • 55. Komplexeres Beispiel • Und jetzt die Bindings dazu: 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); } } • Und jetzt die Bindings dazu: Injector injector = Guice.createInjector(new MyApplicationModule()); MyApplication app = injector.getInstance(MyApplication.class); app.doSomething("Hollywood");
  • 56. Komplexeres Beispiel Was wird jetzt ausgegeben?
  • 57. Komplexeres Beispiel • Ausgabe des Beispiels Sending 'doowylloH' to Debugger. DOOWYLLOH DebuggerEcho UppercaseFormatter MyApplication ReverseFormatter
  • 58. Weitere Features von Guice • 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.
  • 59. Das selbe nochmal, mit Spring • Konfiguration über XML: <?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>
  • 60. Das selbe nochmal, mit Spring • Erzeugen der Beans ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-beans.xml"); MyApplication app = context.getBean("application", MyApplication.class); app.doSomething("Hollywood"); • Ausgabe: Sending 'doowylloH' to Debugger. DOOWYLLOH
  • 61. Dasselbe nochmal, mit XJConf • Tag-Definitionen über XML: <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> • Nicht ganz dasselbe: • XJConf ist schlecht bei Constructor-Injection. Also alles auf Setter-Injection umgestellt.
  • 62. Dasselbe nochmal, mit XJConf • Definition der Objekte über XML: <?xml version="1.0" encoding="UTF-8"?> <configuration> <Application> <EchoDebugger> <UppercaseFormatter/> </EchoDebugger> <ReverseFormatter/> </Application> </configuration>
  • 63. Dasselbe nochmal, mit XJConf • Erzeugen der Objekte: 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"); • Ausgabe: Sending 'doowylloH' to Debugger. DOOWYLLOH
  • 64. Die Weisheit des Erpel Verwende ein DI Framework. Erleichtere dir das Verwalten von komplexen Objektstrukturen durch den Einsatz eines Dependency Injection Frameworks.
  • 66. 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
  • 67. 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
  • 68. Konkret vs Abstrakt Abstrakt Konkret Verarbeiten von Debug- Aufgabe Meldungen Ausgeben per print(), Algorithmen Schreiben eines Logfiles Die Klasse Client RentalCompany
  • 69. Konkret vs Abstrakt Abstrakt Konkret Persistieren von Aufgabe Gästebucheinträgen Speichern in Datenbank, Algorithmen Speichern in XML-Datei Client Die Klasse Guestbook
  • 70. Bobs erstes Design Pattern Strategy-Pattern
  • 71. 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
  • 72. 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
  • 73. 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
  • 74. 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(); }
  • 75. 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();
  • 76. 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.
  • 77. 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
  • 78. 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
  • 79. 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."); } }
  • 80. 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");
  • 81. 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.
  • 82. 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 Abbildung 4-6: UML-Diagramm des Prototyp plikation reduziert die Anzahl der Klassen, die Ihre Ap Der Einsatz des Prototype-Patterns ukte zu sen bild en 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
  • 83. Strukturmuster • Strukturmuster befassen sich mit der Komposition von Objekten • Zu den Strukturmustern gehören unter anderem • Composite-Pattern • Proxy-Pattern • Adapter-Pattern • Facade-Pattern
  • 84. 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
  • 85. 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); } }
  • 86. 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());
  • 87. 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.
  • 88. 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
  • 89. 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.
  • 90. 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."); } } ... }
  • 91. 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; } } ... }
  • 92. 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.
  • 93. 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 Interfa ces 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 apter-Patterns Abbildung 5-4: UML-Diagramm des Ad
  • 94. Verhaltensmuster • Verhaltensmuster beschreiben die Interaktion zwischen Objekten. • Zu den Verhaltensmuster gehören unter anderem • Subject/Observer-Pattern • Template-Method-Pattern • Command-Pattern • Iterator-Pattern
  • 95. 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
  • 96. 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; } ... }
  • 97. 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; } }
  • 98. 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.
  • 99. 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 Abbildung 6-2: UML-Diagramm des Templat ktorieren und es, gemeinsamen Code herauszufa Schablonenmethoden ermöglichen n zu müssen. somit gemeinsames Verhalten nur einmal implementiere
  • 100. 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.
  • 101. 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"); } }
  • 102. 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); } } }
  • 103. 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);
  • 104. 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.
  • 105. parametrisieren. t, also das Objekt, das die Mit Client ist im aktuellen 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
  • 106. 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
  • 107. Die Weisheit der Biene Nutze bestehende Lösungen. Abstrahiere Deine konkreten Probleme und wende Design Patterns als Vorlage für Deine Lösung an.
  • 109. 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
  • 110. 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.
  • 111. 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
  • 112. Emergenz „Emergenz ist die spontane Herausbildung von komplexen Systemen und Strukturen durch eine Vielzahl von relativ einfachen Interaktionen.“ Wikipedia
  • 113. 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.
  • 114. 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
  • 115. 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
  • 116. 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
  • 117. Permature Optimization „Premature optimization is the root of all evil“ Donald Knuth
  • 118. 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
  • 119. 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
  • 120. 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
  • 122. Vermeide Accidential Complexity „Simplify essential complexity; diminish accidental complexity“ Neal Ford
  • 123. 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
  • 124. 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
  • 125. Accidential Complexity • JPA • Rules Engine • Aspect Oriented Programming • komplexes Build System • Stateful Webservices
  • 126. 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.
  • 127. 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.
  • 129. Unit Tests „Im Unit Test werden kleinere Programmteile in Isolation von anderen Programmteilen getestet.“ Frank Westphal
  • 130. 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
  • 131. 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); } }
  • 132. Unit Tests $ mvn test ... ------------------------------------------------------- T E S T S ------------------------------------------------------- Running net.schst.tiercode.RentalCompanyTest Tests 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] ------------------------------------------------------------------------
  • 133. 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
  • 134. 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
  • 135. 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
  • 137. 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
  • 140. 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
  • 141. Continuous Integration SVN Repository CI Server
  • 142. Hudson
  • 145. 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.
  • 146. Der Waschbär Halte deinen Code sauber.
  • 147. 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
  • 148. Coding Standards in 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)
  • 149. 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 days int elapsedTimeInDays = 1;
  • 150. 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/
  • 152. Kommentare • Gute Kommentare • Schlechte Kommentare • @todo-Kommentare • Redundante Kommentare • Informative Kommentare • Postionsmarkierungen • Javadocs in öffentlichen APIs • Auskommentierter Code Kommentare sind keine Ausrede für schlechten Code.
  • 153. DRY - Don‘t Repeat Yourself „Every piece of knowlege must have a single, unambiguous, authoritative representation within a system“ Andrew Hunt and Dave Thomas
  • 154. 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.
  • 155. 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.
  • 156. Die Pfadfinder-Regel „Leave the campground cleaner than you found it“ Robert C. Martin
  • 158. 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
  • 159. 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.
  • 162. Fragen? Vielen Dank für die Aufmerksamkeit. Bildquellen © iStockphoto.com Kontakt holger.rueprich@1und1.de stephan.schmidt@1und1.de