© Markus Knauß, 2008 Entwurfsmuster Entwurfsmuster Markus Knauß [email protected] 1 © Markus Knauß, 2008 Motivation Entwurfsmuster helfen nicht nur bei der Erstellung eines Entwurfs, sie sind auch nützlich, um ein bestehendes Programm neu zu strukturieren. Entwurfsmuster In den kommenden zwei Vorlesungsterminen wird ein bestehendes Programm mit Entwurfsmustern neu strukturiert. 2 Praktische Anwendung eines Entwurfsmusters Erkennen der Vorteile und Nachteile von Entwurfsmustern Überblick über objektorientierte Konzepte und Entwurfsmuster Entwurfsmuster © Markus Knauß, 2008 Ziele 3 Nutzung einer Datenbank für die Speicherung der Eintragungen eines Geburtstagskalenders. Entwurfsmuster © Markus Knauß, 2008 Geburtstagskalender 4 © Markus Knauß, 2008 Entwurfsmuster Inhalt Java Database Connectivity (JDBC) Objektorientierte Konzepte Template Method Pattern Entwurfsmuster Datenbankzugriff mit Entwurfsmustern Zusammenfassung Literatur und Links 5 © Markus Knauß, 2008 Java Database Connectivity (JDBC) JDBC bietet eine plattformunabhängige Schnittstelle für die Nutzung von Datenbanken in Java-Programmen. Entwurfsmuster Das JDBC-API definiert Interfaces, welche die zentralen Datenbankfunktionen kapseln. Verbinden mit einer Datenbank Eine Anweisung ausführen Ergebnisse einer Anweisung auswerten 6 © Markus Knauß, 2008 JDBC Übersicht ResultSet erzeugt Statement erzeugt PreparedStatement erzeugt CallableStatement Entwurfsmuster erzeugt Connection erzeugt DriverManager implementiert herstellerspezifischer Datenbanktreiber greift zu auf java.sql.* implementiert JDBC-ODBC Brücke Datenbank ODBC Treiber greift zu auf 7 © Markus Knauß, 2008 Entwurfsmuster java.sql.Driver Ein Datenbankanbieter, der eine JDBC-Implementierung anbieten möchte, muss eine eigene Implementierung für das Interface java.sql.Driver liefern. Der JDBC DriverManager (java.sql.DriverManager) verwaltet in einer JVM verfügbare Datenbanktreiber (java.sql.Driver) und bietet Zugriff auf die Funktionen der verfügbaren Treiber. Um einen Treiber in der JVM verfügbar zu machen, muss dessen implementierende Klasse mit dem Klassenlader geladen werden. Hinweis: Der DriverManager ist ein Broker, der Zugriff auf die Driver-Implementierungen erlaubt. Driver-Implementierungen sind Factories für den Zugriff auf eine Datenbank. 8 © Markus Knauß, 2008 JDBC am Beispiel // Datenbanktreiber laden Class.forName( "org.apache.derby.jdbc.EmbeddedDriver"); // Verbindung zur Datenbank herstellen Connection con = DriverManager.getConnection( "jdbc:derby:/Users/knaussms/tmp/address"); // Auszuführende Anweisung erstellen Entwurfsmuster Statement stmt = con.createStatement(); // Anweisung ausführen ResultSet rs = stmt.executeQuery( "SELECT * FROM persons"); // Ergebnisse auswerten while (rs.next()) { int id = rs.getInt("id"); String firstName = rs.getString("first_name"); String lastName = rs.getString("last_name"); Date dateOfBirth = rs.getDate("date_of_birth"); DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM); System.out.println(Integer.toString(id) + ": " + firstName + " " + lastName + ", " + dateFormat.format(dateOfBirth)); } Die einzige herstellerspezifische Anweisung ist das Laden des Datenbanktreibers. Konfigurationsspezifische Anweisungen sind das Verbinden mit der Datenbank und der Zugriff auf Tabellen und Spalten. Alle weiteren Befehle sind unabhängig von der verwendeten Datenbank. // Verbindung zur Datenbank schließen con.close(); SimpleJDBC.java 9 © Markus Knauß, 2008 Einschub: PersonDB <<interface>> IPerson +setFirstName(firstName:String) +getFirstName():String +setLastName(lastName:String) +getLastName():String +setDateOfBirth(dateOfBirth:Calendar) +getDateOfBirth():Calendar <<interface>> IDBObject +getId():int Entwurfsmuster PersonBean Die Klasse PersonDB implementiert alle Funktionen für die Speicherung, das Laden, das Ändern und das Löschen von GeburtstagskalenderEinträgen in einer Datenbank. -firstName:String -lastName:String -dateOfBirth:Calendar PersonDB -id:int +create(person:IPerson):PersonDB +read(id:int):PersonDB +select(qbe:IPerson):List<PersonDB> +read() +update() +delete() implementiert erbt von 10 © Markus Knauß, 2008 Entwurfsmuster Aufgabe Markieren Sie die einzelnen Schritte für den Datenbankzugriff in den Methoden create, read, update, delete und select der Klasse PersonDB. Welche Anweisungssequenzen können Sie identifizieren? Welche Teile der Sequenzen sind gleich, wo unterscheiden sie sich? con = DriverManager.getConnection(DB_URL); Mit Datenbank verbinden PreparedStatement ps = con.prepareStatement(DELETE_STMT); Statement erstellen ps.setInt(1, getId()); ps.execute(); Statement intialisieren und ausführen con.close(); Verbindung schließen PersonDB.java 11 © Markus Knauß, 2008 Entwurfsmuster Beispiel einer Lösung: Gleichbleibende Anweisungssequenzen public void read() { Connection con = null; try { con = DriverManager.getConnection(DB_URL); PreparedStatement ps = con.prepareStatement(READ_STMT); ps.setInt(1, getId()); ResultSet rs = ps.executeQuery(); while (rs.next()) { setFirstName(rs.getString("fist_name")); setLastName(rs.getString("last_name")); setDateOfBirth(toCalendar(rs.getDate("date_of_birth"))); } } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } } } } Verbindung herstellen Statement erstellen und initialisieren Statement ausführen Resultate auswerten Verbindung schließen 12 © Markus Knauß, 2008 Entwurfsmuster Beispiel einer Lösung: Unterschiede public void read() { Connection con = null; try { con = DriverManager.getConnection(DB_URL); PreparedStatement ps = con.prepareStatement(READ_STMT); ps.setInt(1, getId()); ResultSet rs = ps.executeQuery(); while (rs.next()) { setFirstName(rs.getString("fist_name")); setLastName(rs.getString("last_name")); setDateOfBirth(toCalendar(rs.getDate("date_of_birth"))); } } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } } } } Statements Initialisierung Auswertung 13 © Markus Knauß, 2008 Entwurfsmuster Inhalt Java Database Connectivity (JDBC) Objektorientierte Konzepte Template Method Pattern Entwurfsmuster Datenbankzugriff mit Entwurfsmustern Zusammenfassung Literatur und Links 14 © Markus Knauß, 2008 Klassen ModelFactory -instance:ModelFactory +getInstance():ModelFactory Eine Klasse ist eine Schablone für Objekte, die aus der Klasse erzeugt werden können. Entwurfsmuster Jede Klasse existiert nur einmal in einem laufenden Programm. Eine Klasse kann einen Zustand haben und Methoden anbieten. 15 © Markus Knauß, 2008 Entwurfsmuster Objekte Person -firstName:String -lastName:String -dateOfBirth:Calendar +getFirstName():String +setFirstName(firstName:String) +getLastName():String +setLastName(lastName:String) +getDateOfBirth():Calendar +setDateOfBirth(dateOfBirth:Calendar) JaggerMick:Person RichardsKeith:Person -firstName=“Mick“ -lastName=“Jagger“ -dateOfBirth='26.7.1943' -firstName=“Keith“ -lastName=“Richards“ -dateOfBirth='18.12.1943' Objekte werden aus Klassen erzeugt (instantiiert). Aus jeder Klassen können beliebig viele Objekte erzeugt werden. Jedes Objekt hat eine Identität und einen Zustand. 16 © Markus Knauß, 2008 Entwurfsmuster Vererbung Vererbung drückt eine „ist ein“Beziehung aus. Person -firstName:String -lastName:String -dateOfBirth:Calendar +getFirstName():String +setFirstName(firstName:String) +getLastName():String +setLastName(lastName:String) +getDateOfBirth():Calendar +setDateOfBirth(dateOfBirth:Calendar) Objekte von erbenden Klassen können an Stelle von Objekten der vererbenden Klasse verwendet werden. List<Person> persons = new ArrayList<Person>(); -id:int public void store(Person person) { persons.add(person); } +create(person:IPerson):PersonDB +read(id:int):PersonDB +select(qbe:IPerson):List<PersonDB> +read() +update() +delete() public static void main(String[] args) { PersonBean personBean = new PersonBean(); PersonDB personDB = new PersonDB(); store(personBean); store(personDB); } PersonBean PersonDB 17 © Markus Knauß, 2008 Polymorphie Erbende Klassen können Methoden überschreiben. Durch das Überschreiben kann das Verhalten einer Methode verändert werden. Entwurfsmuster Polymorphie = Vielgestaltigkeit, Verschiedengestaltigkeit 18 © Markus Knauß, 2008 Polymorphie Person -firstName:String Entwurfsmuster +getFirstName():String +setFirstName(firstName:String) public String getFirstName() { read(); return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; update(); } PersonBean PersonDB +getFirstName():String +setFirstName(firstName:String) +getFirstName():String +setFirstName(firstName:String) +read() +update() public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } 19 © Markus Knauß, 2008 Abstrakte Klassen, abstrakte Methoden Person DBObject Entwurfsmuster +getFirstName():String +setFirstName(firstName:String) +getLastName():String +setLastName(lastName:String) +getDateOfBirth():Calendar +setDateOfBirth(dateOfBirth:Calendar) PersonBean -firstName:String -lastName:String -dateOfBirth:Calendar +getId():int +read() +update() +delete() PersonDB -id:int +create(person:IPerson):PersonDB +read(id:int):PersonDB +select(qbe:IPerson):List<PersonDB> Von abstrakten Klassen können keine Objekte erzeugt werden. Abstrakte Methoden haben keine Implementierung, sie müssen in der erbenden Klasse überschrieben werden. Abstrakte Methoden können nur in abstrakten Klassen deklariert werden. 20 © Markus Knauß, 2008 Entwurfsmuster Aufgabe Kapseln Sie die spezifischen Anweisungsteile der readMethode in eigenen Methoden. Skizzieren Sie die Klasse DBStatement, in der die Methoden, in denen die spezifischen Anweisungsteile der read-Methode zusammengefasst sind, mit einer Standardimplementierung realisiert sind. Skizzieren Sie die von DBStatement erbende Klasse ReadStatement, in der die Methoden mit den spezifischen Anweisungsteilen der read-Methode enthalten sind. Überarbeiten Sie die Implementierung der read-Methode so, dass nur die Methoden mit den spezifischen Anweisungsteilen aus der Klasse ReadStatement verwendet werden. 21 © Markus Knauß, 2008 Beispiel einer Lösung: Klasse DBStatement public abstract class DBStatement { public abstract String getStatement(); public void initParameter(PreparedStatement stmt) throws SQLException { } Entwurfsmuster public void evaluateResults(ResultSet rs) throws SQLException { } } DBStatement.java 22 © Markus Knauß, 2008 Beispiel einer Lösung: Klasse ReadStatement public class ReadStatement extends DBStatement { private private private private int id; String firstName; String lastName; Calendar dateOfBirth; Entwurfsmuster @Override public String getStatement() { return "SELECT * FROM persons WHERE id = ?"; } @Override public void initParameter(PreparedStatement stmt) throws SQLException { stmt.setInt(1, id); } @Override public void evaluateResults(ResultSet rs) throws SQLException { firstName = rs.getString("fist_name"); lastName = rs.getString("last_name"); dateOfBirth = CalendarUtils.toCalendar(rs.getDate("date_of_birth")); } // Get and set methods for attributes. } ReadStatement.java 23 © Markus Knauß, 2008 Entwurfsmuster Beispiel einer Lösung: read()-Methode private ReadStatement readStatement = new ReadStatement(); public void read() { Connection con = null; try { con = DriverManager.getConnection(DB_URL); PreparedStatement ps = con.prepareStatement(readStatement.getStatement()); readStatement.setId(getId()); readStatement.initParameter(ps); ResultSet rs = ps.executeQuery(); while (rs.next()) { readStatement.evaluateResults(rs); } } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } } } } PersonDB.java 24 © Markus Knauß, 2008 Was wurde erreicht? Die Implementierung der speziellen Teile einer DatenbankAnweisung können in eigenen Klassen gekapselt werden. Entwurfsmuster Der Algorithmus für die Ausführung einer Anweisung ist in allen Methoden gleich! DBStatement +getStatement():String +initParameter(stmt:PreparedStatement) +evaluateResults(rs:ResultSet) CreateStatement ReadStatement UpdateStatement DeleteStatement 25 © Markus Knauß, 2008 Entwurfsmuster Inhalt Java Database Connectivity (JDBC) Objektorientierte Konzepte Template Method Pattern Entwurfsmuster Datenbankzugriff mit Entwurfsmustern Zusammenfassung Literatur und Links 26 © Markus Knauß, 2008 Entwurfsmuster Beobachtungen Die Abfolge der Schritte für den Datenbankzugriff via JDBC ist immer gleich. Verbindung zur Datenbank öffnen Anweisung erstellen Parameter initialisieren Anweisung ausführen Ergebnisse auswerten Verbindung zur Datenbank schließen Die Schritte unterscheiden sich in der ausgeführten Anweisung, den Parametern und der Auswertung der Ergebnisse. 27 © Markus Knauß, 2008 Template Method Zweck Definiere einen Algorithmus in einer Methode. Delegiere einzelne Schritte an erbende Klassen. Entwurfsmuster Die Verwendung einer Template Method ermöglicht es erbenden Klassen, bestimmte Schritte eines Algorithmus zu überschreiben, ohne die Struktur des Algorithmus zu ändern. 28 © Markus Knauß, 2008 Entwurfsmuster Template Method Motivation Zum Beispiel Datenbankzugriff via JDBC: Die Abfolge der Schritte für den Datenbankzugriff mit JDBC ist immer gleich. Die Inhalte der einzelnen Schritte, zum Beispiel die konkrete Anweisung oder die Auswertung der Ergebnisse, sind verschieden. 29 Invariante Teile eines Algorithmus sollen genau einmal implementiert werden. Gemeinsames Verhalten soll in einer Klasse zusammengefasst werden. Kontrolle der Erweiterungen durch Vererbung Entwurfsmuster © Markus Knauß, 2008 Template Method Anwendbarkeit 30 © Markus Knauß, 2008 Template Method Lösung CreateStatement Entwurfsmuster DBStatement +getStatement():String +initParameter(stmt:PreparedStatement) +evaluateResults(rs:ResultSet) +execute() PersonDB ReadStatement -id:int UpdateStatement DeleteStatement SelectStatement +create(person:IPerson):PersonDB +read(id:int):PersonDB +select(qbe:IPerson):List<PersonDB> +read() +update() +delete() NextPrimaryKeyStatement 31 © Markus Knauß, 2008 Entwurfsmuster Template Method Lösung public final void execute() { Connection con = null; try { con = DriverManager.getConnection(DB_URL); PreparedStatement ps = con.prepareStatement(getStatement()); initParameter(ps); ps.execute(); ResultSet rs = ps.getResultSet(); if (rs != null) { while (rs.next()) { evaluateResults(rs); } } } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { log.log(Level.SEVERE, e.getMessage(), e); } } } } DBStatement.java 32 public class PersonDB extends PersonBean implements IPerson, IDBObject { private static final CreateStatement createStatement = new CreateStatement(); private static final ReadStatement readStatement = new ReadStatement(); private static final NextPrimaryKeyStatement nextPrimaryKeyStatement = new NextPrimaryKeyStatement(); //... public static PersonDB create(IPerson person) { nextPrimaryKeyStatement.execute(); int nextPK = nextPrimaryKeyStatement.getNextPrimaryKey(); createStatement.setId(nextPK); createStatement.setFirstName(person.getFirstName()); createStatement.setLastName(person.getLastName()); createStatement.setDateOfBirth(person.getDateOfBirth()); createStatement.execute(); return new PersonDB(nextPK, person); } Entwurfsmuster © Markus Knauß, 2008 Template Method Lösung public static PersonDB read(int id) { readStatement.setId(id); readStatement.execute(); return new PersonDB( id, readStatement.getFirstName(), readStatement.getLastName(), readStatement.getDateOfBirth()); } //... } PersonDB.java 33 Gemeinsames Verhalten wird in einer Klasse gekapselt. Der Kontrollfluss wird invertiert, die vererbende Klasse ruft Methoden der erbenden Klasse auf. Es muss klar sein, welche Methoden überschrieben werden dürfen bzw. überschrieben werden müssen. Entwurfsmuster © Markus Knauß, 2008 Template Methode Konsequenzen 34 Factory, Factory Method: nutzt Template Method zur Objekterzeugung Strategy: Kapselung eines Algorithmus Entwurfsmuster © Markus Knauß, 2008 Template Methode Ähnliche und Verwandte Muster 35 © Markus Knauß, 2008 Entwurfsmuster Inhalt Java Database Connectivity (JDBC) Objektorientierte Konzepte Template Method Pattern Entwurfsmuster Datenbankzugriff mit Entwurfsmustern Zusammenfassung Literatur und Links 36 © Markus Knauß, 2008 Entwurfsmuster Inhalt Java Database Connectivity (JDBC) Objektorientierte Konzepte Template Method Pattern Entwurfsmuster Datenbankzugriff mit Entwurfsmustern Zusammenfassung Literatur und Links 37 © Markus Knauß, 2008 Literatur und Links Das Buch der „Gang of Four“ mit den bekanntesten Entwurfsmuster: Entwurfsmuster Gamma, E., R. Helm, R. Johnson und J. Vlissides (1995): Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. Wikipedia Artikel mit Links und Hintergrundinformationen für den Einstieg ins Themengebiet (25.6.2008): http://de.wikipedia.org/wiki/Entwurfsmuster 38 © Markus Knauß, 2008 Entwurfsmuster Inhalt Java Database Connectivity (JDBC) Objektorientierte Konzepte Template Method Pattern Entwurfsmuster Datenbankzugriff mit Entwurfsmustern Zusammenfassung Literatur und Links 39