CodingStyle.StudipCsDpAp History

Hide minor edits - Show changes to markup

 
 
August 23, 2008, at 12:31 AM by chueser -
Changed lines 978-980 from:

(Quelle: Factory Method, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: PHPBuilder (01.03.2008): Factory Method. Online im Internet: URL: http://phpbuilder.com/manual/en/language.oop5.patterns.php [01.03.2008, 16:30 GMT+1])

Changed lines 1129-1131 from:

(Quelle: Factory-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Abstrakte Fabrik. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.creational-patterns.abstract-factory.html [02.03.2008, 21:00 GMT+1])

Changed lines 1187-1189 from:

(Quelle: Singleton-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Singleton. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.creational-patterns.singleton.html [02.03.2008, 21:00 GMT+1])

Changed lines 1248-1250 from:

(Quelle: Singleton, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: PHPBuilder (01.03.2008): Singleton. Online im Internet: URL: http://phpbuilder.com/manual/en/language.oop5.patterns.php [01.03.2008, 16:30 GMT+1])

Changed lines 1289-1291 from:

(Quelle: Abstract Singleton, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: php::bar (01.03.2008): Abstract Singleton. Online im Internet: URL: http://www.phpbar.de/w/Abstract_Singleton [01.03.2008, 16:30 GMT+1])

Changed lines 1429-1431 from:

(Quelle: Template Method-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Schablone. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.behavioral-patterns.template-method.html [02.03.2008, 21:00 GMT+1])

Added lines 1445-1446:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Iterator. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.behavioral-patterns.iterator.html [02.03.2008, 21:00 GMT+1])

 
 
August 23, 2008, at 12:16 AM by chueser -
Changed lines 1524-1526 from:

(Quelle: Aufzählen in PHP 3 & 4, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Iteratoren. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.iterators.html [02.03.2008, 21:00 GMT+1])

Changed lines 1643-1645 from:

(Quelle: Iterator-Schnittstelle von PHP 5, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Die Iterator-Schnittstelle von PHP 5. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.iterators.interfaces.html [02.03.2008, 21:00 GMT+1])

Changed lines 1764-1766 from:

(Quelle: Standard PHP Library, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Die Standard PHP Library (SPL). Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.iterators.spl.html [02.03.2008, 21:00 GMT+1])

Changed lines 1894-1896 from:

(Quelle: ArrayAccess Schnittstelle, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Die Schnittstelle ArrayAccess. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.iterators.arrayaccess.html [01.03.2008, 16:30 GMT+1])

Changed lines 1962-1964 from:

(Quelle: Proxy-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Stellvertreter. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.structural-patterns.proxy.html [01.03.2008, 16:30 GMT+1])

Deleted line 1966:
Changed line 2026 from:

(Quelle: Wikipedia (01.03.2008): Anti Pattern. Online im Internet: URL: http://de.wikipedia.org/wiki/Anti-Pattern [01.03.2008, 16:30 GMT+1])

to:

(Quelle: Wikipedia (01.03.2008): Anti-Pattern. Online im Internet: URL: http://de.wikipedia.org/wiki/Anti-Pattern [01.03.2008, 16:30 GMT+1])

 
 
August 22, 2008, at 11:59 PM by chueser -
Changed line 2027 from:

(Quelle: AntiPattern, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Wikipedia (01.03.2008): Anti Pattern. Online im Internet: URL: http://de.wikipedia.org/wiki/Anti-Pattern [01.03.2008, 16:30 GMT+1])

 
 
August 22, 2008, at 11:51 PM by chueser -
Changed lines 56-58 from:

(Quelle: Motivation, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Motivation. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.motivation.html [01.03.2008, 16:30 GMT+1])

Changed lines 248-250 from:

(Quelle: Serialization, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Serialisierung von Objekten. Online im Internet: URL: [01.03.2008, 16:30 GMT+1])

Changed lines 349-352 from:

(Quelle: Reflection, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Die Reflection API. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.reflection-api.html [01.03.2008, 16:30 GMT+1])

Changed lines 811-813 from:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Observer-Muster. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.behavioral-patterns.observer.html [01.03.2008, 16:30 GMT+1])

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Beobachter. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.behavioral-patterns.observer.html [01.03.2008, 16:30 GMT+1])

Changed lines 943-946 from:

(Quelle: Decorator-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Dekorierer. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.structural-patterns.decorator.html [01.03.2008, 16:30 GMT+1])

Changed lines 1025-1026 from:

(Quelle: Factory Method, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: php::bar (01.03.2008): Factory Method. Online im Internet: URL: http://www.phpbar.de/w/Factory_Method [01.03.2008, 16:30 GMT+1])

 
 
August 22, 2008, at 11:34 PM by chueser -
Changed lines 162-165 from:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.classes-objects.html [01.03.2008, 16:30 GMT+1])

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Klassen und Objekte. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.classes-objects.html [01.03.2008, 16:30 GMT+1])

Changed lines 209-211 from:

(Quelle: Polymorphie, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Polymorphie. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.polymorphism.html [01.03.2008, 16:30 GMT+1])

Changed lines 404-407 from:

(Quelle: Migration, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Migration von PHP4 zu PHP5. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.migration.html [01.03.2008, 16:30 GMT+1])

Changed lines 420-423 from:

(Quelle: Interzeptionsmethoden, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Interzeptionsmethoden. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.interceptors.html [01.03.2008, 16:30 GMT+1])

Changed lines 812-815 from:

(Quelle: Observer-Muster, Sebastian Bergmann, 01.03.2008, 17:00 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Observer-Muster. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/design-patterns.behavioral-patterns.observer.html [01.03.2008, 16:30 GMT+1])

 
 
August 22, 2008, at 11:25 PM by chueser -
Changed line 162 from:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.classes-objects.html [01.03.2008, 16:30 GMT+1])

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.classes-objects.html [01.03.2008, 16:30 GMT+1])

 
 
August 22, 2008, at 11:23 PM by chueser -
Changed lines 162-163 from:

(Quelle: OOP, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Bergman, Sebastian (2005): Professionelle Softwareentwicklung mit PHP5. Online im Internet: URL: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.classes-objects.html [01.03.2008, 16:30 GMT+1])

 
 
August 22, 2008, at 09:52 PM by chueser - navigation & toc
Changed lines 3-40 from:
to:
Deleted line 9:

Deleted line 164:

Deleted line 166:

Changed line 210 from:

to:
Deleted line 249:

Deleted line 422:

Deleted line 450:

Deleted line 454:

Deleted line 586:

Deleted line 1133:

Changed line 1292 from:

to:
Deleted line 1432:

Deleted line 1898:

Deleted line 1966:

Changed line 1969 from:

to:
Changed line 1974 from:

to:
Changed line 1979 from:

to:
Changed line 1984 from:

to:
Changed line 1989 from:

to:
Changed line 1994 from:

to:
Changed line 1999 from:

to:
Changed line 2004 from:

to:
Changed line 2009 from:

to:
Changed line 2014 from:

to:
Changed line 2019 from:

to:
Changed line 2024 from:

to:
 
 
May 05, 2008, at 12:40 AM by chueser -
Changed lines 57-58 from:

if ($connection === FALSE)

  handle_error();
to:

if ($connection === FALSE) {

    handle_error();

}

Changed lines 61-63 from:

if (mysql_select_db('test', $connection) === FALSE)

  handle_error(mysql_error($connection));
to:

if (mysql_select_db('test', $connection) === FALSE) {

    handle_error(mysql_error($connection));
 }
Changed lines 66-67 from:
  'SELECT spalte FROM tabelle',
  $connection
to:
    'SELECT spalte FROM tabelle',
    $connection
Changed lines 70-74 from:

if ($result === FALSE)

  handle_error(mysql_error($connection));

while(($row = mysql_fetch_assoc($result)) !== FALSE) {

  // …
to:

if ($result === FALSE) {

    handle_error(mysql_error($connection));
Added lines 73-76:

while(($row = mysql_fetch_assoc($result)) !== FALSE) {

  // …

}

Changed lines 80-81 from:

function handle_error($message = '') {

to:

function handle_error($message = '') {

Changed lines 96-98 from:

class DB_MySQL {

  private $connection = NULL;
  private $result = NULL;
to:

class Db_MySql {

    private $connection = NULL;
    private $result = NULL;
Changed lines 101-107 from:
  public function connect($host, $database, $user, $pass) {
    $this->connection = mysql_connect(
      $host,
      $user,
      $pass,
      TRUE
    );
to:
    public function connect($host, $database, $user, $pass) 
    {
        $this->connection = mysql_connect(
            $host,
            $user,
            $pass,
            TRUE
        )
        mysql_select_db($database, $this->connection);
    }
Changed lines 112-113 from:
    mysql_select_db($database, $this->connection);
  }
to:
    public function disconnect() 
    {
        if (is_resource($this->connection)) {
            mysql_close($this->connection);
        }
    }
Changed lines 119-123 from:
  public function disconnect() {
    if (is_resource($this->connection)) {
      mysql_close($this->connection);
    }
  }
to:
    public function query($query) {
        if (is_resource($this->connection)) {
            if (is_resource($this->result)) {
                mysql_free_result($this->result);
            }
Changed lines 125-129 from:
  public function query($query) {
    if (is_resource($this->connection)) {
      if (is_resource($this->result)) {
        mysql_free_result($this->result);
      }
to:
            $this->result = mysql_query(
                $query,
                $this->connection
            );
        }
    }
Changed lines 132-137 from:
      $this->result = mysql_query(
        $query,
        $this->connection
      );
    }
  }
to:
    public function fetchRow() 
    {
        if (is_resource($this->result)) {
            $row = mysql_fetch_assoc($this->result);
Changed lines 137-145 from:
  public function fetchRow() {
    if (is_resource($this->result)) {
      $row = mysql_fetch_assoc($this->result);

      if (is_array($row)) {
        return $row;
      } else {
        return FALSE;
      }
to:
            if (is_array($row)) {
                return $row;
            } else {
                return FALSE;
            }
        }
Deleted line 143:
  }
Changed lines 169-172 from:

class Vector {

  public function add(Vector $vector) {
    // …
  }
to:

class Vector {

    public function add(Vector $vector) 
    {
        // …
    }
Changed lines 181-185 from:

class Vector {

  public function add($vector) {
    if (!($vector instanceof Vector)) {
      die('Parameter muss vom Typ Vector sein.');
    }
to:

class Vector {

    public function add($vector) 
    {
        if (!($vector instanceof Vector)) {
            die('Parameter muss vom Typ Vector sein.');
        }
Changed lines 189-190 from:
    // …
  }
to:
        // …
    }
 
 
May 05, 2008, at 12:31 AM by chueser -
Changed lines 429-431 from:

(Quelle: Migration, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

to:

(Quelle: Migration, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Changed line 446 from:

(Quelle: Interzeptionsmethoden, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

to:

(Quelle: Interzeptionsmethoden, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

 
 
May 05, 2008, at 12:24 AM by chueser -
Changed line 3 from:
to:
 
 
April 23, 2008, at 02:55 PM by chueser -
Changed line 1417 from:

Beispiel 7.9: Die Klasse PHPUnit2_Extensions_CodeCoverage_Renderer_Text

to:

Beispiel: Die Klasse PHPUnit2_Extensions_CodeCoverage_Renderer_Text

 
 
March 13, 2008, at 11:09 AM by chueser -
Deleted lines 1321-1326:

Das Command-Muster

Das Adapter & Facade Muster

 
 
March 08, 2008, at 03:08 PM by chueser -
Added lines 1-2069:

OO-Design & Entwurfsmuster

[ Zurück: Kapselung von lokalen Besonderheiten? | Index: Übersicht | Vor: Code Style ]

Übersicht

Motivation

Warum OOP?

Wir wollen uns zunächst fragen, warum wir eine PHP-Anwendung überhaupt objektorientiert entwerfen wollen und nicht den vermeintlich einfacheren Weg der prozeduralen Programmierung wählen.

Für Code, der für die Verwendung durch andere Entwickler bestimmt ist oder von mehreren Programmierern erstellt wird, sollte vor dem eigentlichen Schreiben zunächst ein Design erstellt werden. Der Sinn eines solchen Designs ist es, gut organisierten und konsistenten Code zu schreiben, der einfach zu erweitern und zu warten ist. Ende der 60er, Anfang der 70er Jahre des vergangenen Jahrhunderts hielten neue Sprachkonstrukte, und mit ihnen ein neues Programmierparadigma, Einzug in Programmiersprachen wie Simula oder Smalltalk. Sie sollten die Formulierung des Designs in der Programmiersprache gegenüber dem etablierten prozeduralen Ansatz erleichtern.

So leistet der prozedurale Code in dem Beispiel unten zwar die von ihm erwartete Aufgabe, ist aber in mehrerlei Hinsicht "unschön". An eine Wiederverwendung des Codes im eigentlichen Sinne von "einmal schreiben, mehrfach verwenden" ist allerdings nicht zu denken. Höchstens durch Duplizierung und Anpassung an den neuen Kontext kann er an anderer Stelle eingesetzt werden.

Beispiel: Zugriff auf eine MySQL-Datenbank ohne objektorientierte Konzepte

(:source lang=php linenum:)
<?php
$connection = mysql_connect('localhost', 'root', '');

if ($connection === FALSE)
  handle_error();

if (mysql_select_db('test', $connection) === FALSE)
  handle_error(mysql_error($connection));

$result = mysql_query(
  'SELECT spalte FROM tabelle',
  $connection
);

if ($result === FALSE)
  handle_error(mysql_error($connection));

while(($row = mysql_fetch_assoc($result)) !== FALSE) {
  // ...
}

mysql_free_result($result);

function handle_error($message = '') {
  // ...
}
?>

Wünschenswert wäre eine wiederverwendbare Einheit, die die Datenbankverbindung und die mit ihr assoziierten Operationen zusammenfasst. Ist sie einmal mit den Verbindungsparametern initialisiert, kümmert sich diese Einheit für ihren Verwender unsichtbar um buchhalterische Aufgaben wie Verbindungsauf- und -abbau, Verarbeitung von Anfragen und dergleichen mehr.

Um eine solche Einheit programmiertechnisch umsetzen zu können, bedarf es eines zusätzlichen Sichtbarkeitsbereiches (englisch: scope) neben dem von Hauptprogramm und Prozedur. Dieser neue Sichtbarkeitsbereich soll Variablen und Prozeduren, die diese Variablen manipulieren, in einer Einheit zusammenfassen.

(Quelle: Motivation, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Eine Klasse für den Zugriff auf eine MySQL-Datenbank

(:source lang=php linenum:)
<?php
class DB_MySQL {
  private $connection = NULL;
  private $result = NULL;

  public function connect($host, $database, $user, $pass) {
    $this->connection = mysql_connect(
      $host,
      $user,
      $pass,
      TRUE
    );

    mysql_select_db($database, $this->connection);
  }

  public function disconnect() {
    if (is_resource($this->connection)) {
      mysql_close($this->connection);
    }
  }

  public function query($query) {
    if (is_resource($this->connection)) {
      if (is_resource($this->result)) {
        mysql_free_result($this->result);
      }

      $this->result = mysql_query(
        $query,
        $this->connection
      );
    }
  }

  public function fetchRow() {
    if (is_resource($this->result)) {
      $row = mysql_fetch_assoc($this->result);

      if (is_array($row)) {
        return $row;
      } else {
        return FALSE;
      }
    }
  }
}
?>

Verwendung der MySQL-Klasse

(:source lang=php linenum:)
<?php
require_once 'DB_MySQL.php';

$mysql = new DB_MySQL;
$mysql->connect('localhost', 'test', 'root', '');
$mysql->query('SELECT spalte FROM tabelle');

while ($row = $mysql->fetchRow()) {
  // ...
}

$mysql->disconnect();
?>

Bei der Deklaration von Methoden erlaubt PHP die Angabe eines Klassen- oder Schnittstellennamens für als Parameter übergebene Objekte. Im Gegensatz zu statisch getypten Programmiersprachen erfolgt die Typprüfung jedoch nicht zum Zeitpunkt der Kompilierung, sondern erst zur Laufzeit. Diese so genannten Type Hints ersparen dem Programmierer Schreibarbeit, wie an folgenden Beispielen zu erkennen ist.

Typprüfung mit Type Hints

(:source lang=php linenum:)
<?php
class Vector {
  public function add(Vector $vector) {
    // ...
  }
}
?>

Typprüfung mit dem instanceof-Operator

(:source lang=php linenum:)
<?php
class Vector {
  public function add($vector) {
    if (!($vector instanceof Vector)) {
      die('Parameter muss vom Typ Vector sein.');
    }

    // ...
  }
}
?>

Die Beispiele sind semantisch äquivalent und unterscheiden sich nur in der Art der Typprüfung durch die Type Hints beziehungsweise den instanceof-Operator.

(Quelle: OOP, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

OOP in PHP

Polymorphie

Mit dem Prinzip der Polymorphie (Vielgestaltigkeit) wird ein dynamisches Verhalten von Methoden verfolgt, das von Anzahl und Typ der übergebenen Parameter abhängt. Im Falle einer dynamisch getypten Programmiersprache wie PHP gestaltet sich dies jedoch anders als in einer statisch getypten Programmiersprache wie beispielsweise Java. Für unterschiedliche Anzahl oder Typen der erwarteten Parameter einer Methode schreibt man beispielsweise in Java mehrere Methoden mit dem gleichen Namen.

Beispiel: Polymorphie in Java

(:source lang=java linenum:)
class Klasse {
  public void methode(String variable) {
    System.out.println("Ein String wurde übergeben.");
  }

  public void methode(int variable) {
    System.out.println("Ein Integerwert wurde übergeben.");
  }
}

In PHP ist die Deklaration von mehreren Methoden des gleichen Namens nicht vorgesehen. Polymorphes Verhalten von Methoden kann in PHP auf eine der folgenden Arten erreicht werden:

Eine Methode kann wegen der dynamischen Typisierung einen Parameter akzeptieren, der unterschiedliche Typen enthalten darf.

Eine Methode kann eine variable Anzahl an Parametern akzeptieren, indem optionale Parameter mit Standardwerten versehen und an das Ende der Parameterliste gesetzt werden.

Häufig verwendet man ein assoziatives Array als einzigen Parameter einer Methode. Dies ermöglicht eine variable Anzahl an (benannten) Parametern für die Methode, deren Reihenfolge aufgrund der Assoziativität beliebig sein kann.

Beispiel: Polymorphie in PHP

(:source lang=php linenum:)
<?php
class Klasse {
  public function methode($variable) {
    if (is_string($variable)) {
      print "Ein String wurde übergeben.\n";
    }

    else if (is_integer($variable)) {
      print "Ein Integerwert wurde übergeben.\n";
    }
  }
}
?>

(Quelle: Polymorphie, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Serialisierung von Objekten

Objekte existieren nur zur Laufzeit, können aber durch die Speicherung ihres Zustandes persistent (dauerhaft) gemacht werden. Sie können somit gespeichert und zu einem späteren Zeitpunkt wieder geladen werden. Die Speicherung eines Objektes und seines Zustands wird auch Serialisierung genannt.

PHP bietet die Funktionen serialize() und unserialize(), um ein Objekt zu serialisieren beziehungsweise aus der serialisierten Form wieder ein Objekt zu erstellen. Hierbei erzeugt serialize() aus einem Objekt einen String, in dem die relevanten Daten kodiert sind. Dieser String kann dann beispielsweise in eine Datei geschrieben oder in einer Datenbank abgelegt werden. Analog erwartet unserialize() einen String in diesem Format, um ein Objekt aus dem kodierten String wiederherzustellen.

Verfügt die Klasse des Objektes, das serialisiert werden soll, über eine __sleep-Methode, so wird diese automatisch vor der eigentlichen Serialisierung auf dem Objekt aufgerufen. Diese Methode muss ein Array mit den Namen derjenigen Instanzvariablen zurückliefern, die serialisiert werden sollen. So kann die __sleep-Methode einer Klasse, die eine Datenbankverbindung kapselt, beispielsweise Sorge dafür tragen, dass nur die für den Verbindungsaufbau nötigen Parameter gespeichert werden, nicht aber die Ressource-ID der aktuell bestehenden Verbindung.

Verfügt die Klasse des Objektes, das deserialisiert werden soll, über eine __wakeup-Methode, so wird diese automatisch nach der eigentlichen Deserialisierung auf dem Objekt aufgerufen.

Wird ein Objekt durch Ablegen in dem Array $_SESSION[] als Session-Variable registriert, so kümmert sich PHP automatisch um Serialisierung und Deserialisierung des Objektes zwischen den einzelnen Requests der Session.

Beispiel: Verwendung der Methoden __sleep() und __wakeup()

(:source lang=php linenum:)
<?php
class Klasse {
  function __sleep() {
    print "__sleep() aufgerufen.\n";
    return get_class_vars(get_class($this));
  }

  function __wakeup() {
    print "__wakeup() aufgerufen.\n";
  }
}

$objekt               = new Klasse;
$serialisiertesObjekt = serialize($objekt);
$objekt               = unserialize($serialisiertesObjekt);
?>

__sleep() aufgerufen. __wakeup() aufgerufen.

Weiterhin ist hier das Konzept Object-relational mapping (ORM) zu erwähnen, welches die Speichern von Objekten in einer relationalen Datenbank ermöglicht.

(Quelle: Serialization, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Die Reflection API

Die strukturelle Reflexion, auch Introspektion genannt, macht die Struktur eines objektorientierten Systems durch die Programmiersprache des Systems zugänglich. Dies ermöglicht das Schreiben von generischem Code, der zur Laufzeit Informationen über Klassen und deren Objekte abfragt und diese entsprechend verarbeitet. Diese Generizität wird beispielsweise für das Schreiben von Code- und Dokumentationsgeneratoren benötigt. Ferner bildet sie die Grundlage für Werkzeuge wie PHPUnit, die "fremden" Code analysieren und ausführen müssen.

  • Für den "Blick in das Innere" von Klassen und Objekten bietet PHP die so genannte Reflection API an, die die folgenden Klassen und Schnittstellen umfasst:
  • Reflector ist die Schnittstelle, die von allen Klassen der Reflection API implementiert wird.
  • Reflection ist eine statische Hilfsklasse, die die Arbeit mit Reflector-Objekten erleichtert.
  • ReflectionException ist die von Exception abgeleitete Ausnahme-Klasse, die für das Signalisieren von Fehlern innerhalb der Reflection API verwendet wird.
  • ReflectionExtension repräsentiert die Informationen über eine PHP-Erweiterung.
  • ReflectionFunction repräsentiert die Informationen über eine Funktion.
  • ReflectionParameter repräsentiert die Informationen über einen Parameter einer Funktion oder Methode.
  • ReflectionClass repräsentiert die Informationen über eine Klasse.
  • ReflectionObject ist eine Erweiterung von ReflectionClass und repräsentiert die Informationen über ein Objekt.
  • ReflectionMethod ist eine Erweiterung von ReflectionFunction und repräsentiert die Informationen über eine Methode.
  • ReflectionProperty repräsentiert die Informationen über eine Instanzvariable einer Klasse oder eines Objektes.

Allen Klassen der Reflection API gemein ist die statische Methode export(). Diese liefert eine textuelle Darstellung des reflektierten Sprachobjekts.

Beispiel: Verwendung von ReflectionClass::export()

(:source lang=php linenum:)
<?php
class Klasse {
  public $public;
  protected $protected;
  private $privateStatic;

  public function methode(Klasse $objekt) {
    $objekt = new ReflectionObject($objekt);
    print $objekt->getName();
  }
}

print ReflectionClass::export('Klasse');
?>
Class [ <user> class Klasse ] {
  @@ /home/sb/export.php 2-11

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [3] {
    Property [ <default> public $public ]
    Property [ <default> protected $protected ]
    Property [ <default> private $privateStatic ]
  }

  - Methods [1] {
    Method [ <user> public method methode ] {
      @@ /home/sb/export.php 7 - 10

      - Parameters [1] {
        Parameter #0 [ <required> Klasse $objekt ]
      }
    }
  }
}

Stellvertretend für die Klassen der Reflection API betrachten wir die Klasse ReflectionClass. Diese bietet die folgenden Methoden:

  • getFileName(), getStartLine(), getEndLine(), getDocComment(), getExtension() und getExtensionName() liefern Informationen über die Deklaration der Klasse in einer Quelltextdatei beziehungsweise in einer PHP-Erweiterung.
  • isInternal(), isUserDefined(), isInstantiable(), isInterface(), isAbstract(), isFinal(), getParentClass(), isSubclassOf(), implementsInterface() und isIterateable() liefern Informationen über Eigenschaften der Klasse wie beispielsweise Vererbungsbeziehungen.
  • getMethod(), getMethods(), getProperty(), getProperties(), getConstants() und getConstant() liefern beispielsweise Objekte der Klassen ReflectionMethod und ReflectionProperty, um mit den Methoden, Instanzvariablen und Konstanten einer Klasse zu arbeiten.

Im nächsten Beispiel erzeugen wir zunächst ein Objekt der Klasse ReflectionClass für die Klasse Klasse aus vorherigem Beispiel. Über die Methode getMethod() dieses Objektes gelangen wir an ein Objekt der Klasse ReflectionMethod, das die Methode Klasse::Methode repräsentiert. Diese Methode können wir mit ReflectionMethod::invoke() ausführen. Als ersten Parameter müssen wir das Objekt der zugehörigen Klasse (in unserem Beispiel Klasse) übergeben, auf dem die Methode ausgeführt werden soll. Danach folgen die Parameter der Methode, die aufgerufen werden soll.

Beispiel: ReflectionClass und ReflectionMethod im Einsatz

(:source lang=php linenum:)
<?php
require_once 'Klasse.php';

$klasse = new ReflectionClass('Klasse');
$objekt = new Klasse;

$methode = $klasse->getMethod('methode');
$methode->invoke($objekt, $objekt);
?>

Klasse

(Quelle: Reflection, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Migration von PHP 4 zu PHP 5

Bei der Entwicklung von PHP 5 wurde versucht, die Abwärtskompatibilität zu PHP 4 zu wahren. In diesem Abschnitt finden Sie eine Übersicht über Änderungen in PHP 5, die eine Änderung von bestehenden PHP-4-Anwendungen erforderlich macht.

Kopie versus Referenz

Mit der Einführung des neuen Objektmodells werden Objekte standardmäßig per Referenz übergeben. In PHP 4 wurde stattdessen stets eine Kopie übergeben. Für PHP-Programme, die wie von PHP 4 gewohnt eine Kopie statt einer Referenz erwarten, kann die php.ini-Direktive zend.ze1_compatibility_mode auf On gesetzt werden.

Konstruktor

In PHP 4 entsprach der Name des Konstruktors dem Namen der Klasse. In PHP 5 heißt der Konstruktor nun __construct. Wird in einer Klasse keine Methode mit dem Namen __construct deklariert, so wird nach einer Methode gesucht, die den Namen der Klasse trägt. Wird eine solche Methode gefunden, so wird sie als Konstruktor benutzt. Wird eine Methode mit dem Namen __construct gefunden, so wird diese in jedem Fall (unabhängig davon, ob auch eine Methode mit dem Namen der Klasse existiert) benutzt.

Klassendeklaration vor Objekterzeugung

In PHP 4 war es möglich, ein Objekt einer Klasse zu erzeugen, die zum Zeitpunkt der Instanzierung noch nicht deklariert war. In PHP 5 ist dies nicht mehr möglich, wenn die Klasse Sprachmerkmale verwendet, die mit PHP 5 eingeführt wurden.

Klassen, die nur Sprachmerkmale enthalten, die bereits in PHP 4 zur Verfügung standen, können weiterhin vor ihrer Deklarierung verwendet werden.

Neue Schlüsselwörter

In PHP 5 sind eine Reihe von neuen Schlüsselwörtern hinzugekommen, die nicht mehr als Namen von Klassen, Konstanten, Methoden oder Funktionen verwendet werden können:

  • abstract
  • catch
  • clone
  • final
  • implements
  • interface
  • private
  • protected
  • public
  • throw
  • try

Bei der Migration von PHP 4 nach PHP 5 müssen Klassen, Konstanten, Methoden oder Funktionen, die einen dieser Namen tragen, umbenannt werden.

Besondere Methoden

In PHP 5 sind einige Methodennamen hinzugekommen, die mit einer besonderen Semantik verknüpft sind:

  • __autoload
  • __call
  • __clone
  • __construct
  • __destruct
  • __get
  • __set
  • __toString

Bei der Migration von PHP 4 nach PHP 5 müssen Methoden, die einen dieser Namen tragen, umbenannt werden.

(Quelle: Migration, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

Interzeptionsmethoden

Neben Konstruktor und Destruktor sowie den Methoden __sleep() und __wakeup() bietet PHP noch eine Reihe weiterer spezieller Methoden an, die für bestimmte Ereignisse automatisch aufgerufen werden. Da diese Methoden die entsprechenden Ereignisse in gewisser Weise "abfangen", nennt man sie Interzeptormethoden.

PHP bietet die folgenden Interzeptormethoden an. Sie werden automatisch aufgerufen beim Zugriff auf nicht deklarierte Instanzvariablen und Methoden eines Objektes, beim Versuch, ein Objekt einer nicht deklarierten Klasse zu erzeugen, sowie bei der Typumwandlung eines Objektes in einen String.

  • __autoload($className): Wird aufgerufen, wenn ein Objekt der Klasse $className erzeugt werden soll, die Klasse aber nicht deklariert ist.
  • __get($memberName): Wird aufgerufen, wenn lesend auf die Instanzvariable $memberName eines Objektes zugegriffen wird, die Instanzvariable aber nicht gesetzt ist.
  • __set($memberName, $value): Wird aufgerufen, wenn schreibend auf die Instanzvariable $memberName eines Objektes zugegriffen wird und sie vorher nicht gesetzt war. Der zweite Parameter $value enthält den Wert, den die Instanzvariable erhalten soll.
  • __call($methodName, $parameters): Wird aufgerufen, wenn eine nicht deklarierte Methode $methodName auf einem Objekt aufgerufen wird. Der zweite Parameter $parameters enthält die Parameter des Methodenaufrufes.
  • __toString(): Wird aufgerufen, wenn eine Typumwandlung eines Objektes in einen String durchgeführt werden soll.

(Quelle: Interzeptionsmethoden, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

Entwurfsprinzipien

Die OO-Basics (Abstraktion, Kapselung, Polymorphismus, Vererbung) machen nicht allein ein gutes OO-Design. Gute Entwürfe sind wiederverwendbar, erweiterbar und wartbar.

  1. Kapselung von veränderbaren Teilen: Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  2. Supertyp durch Interface oder abstrakte Superklasse: Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
  3. Komposition: Ziehen Sie Komposition (HAT-EIN) der Vererbung (IST-EIN) vor.

Während der letzten Jahre haben sich in der objektorientierten Programmierung einige sehr nützliche Entwurfsmuster, englisch Design Patterns, herausgebildet.

Entwurfsmuster dienen dazu, gewonnene Erfahrungen über die Lösung wiederkehrender Probleme zu vermitteln. Diese Kombination von Problem und Lösung wird zudem mit einem prägnanten Namen versehen, um ein einheitliches Vokabular zur Diskussion von Problem und Lösung zur Verfügung zu stellen.

Die Vorteile der Entwurfsmuster liegen auf der Hand: Durch die Nutzung von vorhandenem Wissen spart man Zeit bei der Entwicklung und kann Fehler vermeiden, die bereits von anderen gemacht wurden. Die Aufgabe des Softwareentwicklers verlagert sich unter Verwendung von Entwurfsmustern von der Erfindung des Rads zur Auswahl des richtigen Rads und seiner kreativen Verwendung. Hierbei sollte man jedoch die folgenden Punkte stets im Hinterkopf behalten:

  • Entwurfsmuster sind kein Allheilmittel!
Originalität ist nach wie vor bei der Anwendung der Entwurfsmuster gefragt.
  • Entwurfsmuster sind keine Algorithmen!
Algorithmen lösen feinkörnigere Probleme (Suchen, Sortieren) und bieten weniger Freiheitsgrade in der Implementierung.
  • Entwurfsmuster sind keine Frameworks!
Frameworks existieren als konkreter, wiederverwendbarer Code, Entwurfsmuster enthalten nur Beispiele von Code. Frameworks werden für konkrete Anwendungsbereiche eingesetzt, ein Entwurfsmuster kann überall eingesetzt werden.

Es gibt drei Gruppen von Entwurfsmustern:

  • Erzeugungsmuster
  • Strukturmuster
  • Verhaltensmuster

Design-Pattern

(siehe auch: Entwurfsmuster)

Das Strategy-Muster

Das Strategy Pattern definiert eine Familie von Algorithmen, kapselt sie einzeln und macht sie austauschbar. Das Strategy-Muster ermöglicht es, den Algorithmus unabhängig von den Clients die ihn einsetzen, variieren zu lassen.

Reine Vererbung ist zwar im Hinblick auf Wiederverwendbarkeit gut, jedoch schlecht im Hinblick auf Wartbarkeit. Leicht kann es zu Unklarheiten in der Vererbungshierarchie kommen, falls Neues hinzugefügt wird.

Interface Implementierungen wiederum machen es zu jeder Zeit möglich spezielles Verhalten zu definieren und der Typhierarchie hinzuzufügen, jedoch geschieht dies auf Kosten von Code-Redundanz und Code-Duplizierung, was zur Folge hat, dass der Code schlecht zu warten ist.

Durch das Strategy-Pattern, welches ein Verhaltensmuster ist, werden veränderbare Teile aus den konstanten Teilen herausgezogen und Interfaces (Supertyp) definiert. Die Klassen der Verhaltensweisen implementieren die Interfaces. Andere Klassen können nun diese Verhaltensweisen in sich aufnehmen und in Form einer Komposition delegieren. Getter- und Setter-Methoden in diesen KLassen machen den dynamischen Austausch zur Laufzeit möglich. Dadurch sind Verhaltensweisen leicht austauschbar und wiederverwendbar. Der resultierende Code ist gut wiederverwendbar und wartbar. Demnach erfüllt das Strategy-Muster die drei oben aufgeführten Entwurfsprinzipien.

Bsp:

  • Klasse Ente nimmt als Komposition die Supertypen der Verhaltensweisen Flugverhalten und Quakverhalten auf. Klasse Ente hat die Unterklassen StockEnte, MoorEnte, GummiEnte, LockEnte.
  • Flugverhalten und Quakverhalten ist jeweils gekapselt durch Interfaces (Supertyp) und deren Implementierungen. Diese Implementierungen sind leicht austauschbar.
  • Klasse Ente delegiert nun die Verhaltensweisen und ruft in den eigenen Methoden die jeweils entsprechende Methode der Verhaltensweisen (des Supertyps) auf statt diese selbst zu implementieren. Der dynamisch Austausch von Verhaltensweisen zur Laufzeit wird durch Getter- und Setter-Methoden ermöglicht.

––

Problem

Eine Familie von Algorithmen soll gekapselt werden, mit der Möglichkeit, sie beliebig auszutauschen.

Motivation

Das Strategie-Muster bietet sich immer dann an, wenn eine Aufgabe mit unterschiedlichen Verfahren, die sich beispielsweise in Geschwindigkeit und Speicherverbrauch unterscheiden, zu lösen ist. Der Eingabe entsprechend kann so das jeweils beste Verfahren dynamisch zur Laufzeit verwendet werden. Es kann ebenfalls genutzt werden, wenn verwandte Klassen sich nur in ihrem Verhalten unterscheiden oder eine Klasse unterschiedliche Verhaltensweisen definiert und diese mit unübersichtlichen if-then-else- oder switch-case-Konstruktionen in ihren Methoden implementiert sind. Zusammenhängende Zweige dieser Bedingungsanweisungen können übersichtlich in eigene Strategieklassen ausgelagert werden.

Lösung

Die Verwandtschaft der Klassen wird durch Implementieren einer gemeinsamen Schnittstelle zum Ausdruck gebracht. Die Verwenderklasse arbeitet mit einem Objekt einer Klasse, die diese Schnittstelle bereitstellt. Dieses Objekt kann zur Laufzeit durch eine Methode setStrategy($strategy) ausgetauscht werden, wodurch das Verhalten der Verwenderklasse dynamisch geändert werden kann.

Anwendungsbeispiele

Das folgende Beispiel zeigt eine Implementierung des bekannten Sortierverfahrens Bubble-Sort. In der hier gezeigten Variante lässt sich der Vergleich von zwei Elementen der zu sortierenden Menge durch Verwendung einer Strategie austauschen.

Beispiel: Die Schnittstelle CompareStrategy

(:source lang=php linenum:)
<?php
interface CompareStrategy {
    public function compare($a, $b);
}
?>

Beispiel: Die Klasse AscendingCompare

(:source lang=php linenum:)
<?php
require_once 'CompareStrategy.php';

class AscendingCompare implements CompareStrategy {
  public function compare($a, $b) {
    return ($a == $b) ? 0 : ($a > $b) ? 1 : -1;
  }
}
?>

Beispiel: Die Klasse DescendingCompare

(:source lang=php linenum:)
<?php
require_once 'CompareStrategy.php';

class DescendingCompare implements CompareStrategy {
  public function compare($a, $b) {
    return ($a == $b) ? 0 : ($a < $b) ? 1 : -1;
  }
}
?>

Beispiel: Die Klasse BubbleSort

(:source lang=php linenum:)
<?php
require_once 'CompareStrategy.php';
require_once 'AscendingCompare.php';
require_once 'DescendingCompare.php';

class BubbleSort {
  private $strategy;

  public function setStrategy(CompareStrategy $strategy) {
    $this->strategy = $strategy;
  }

  public function sort($array) {
    for ($i = sizeof($array)-1; $i >= 0; --$i) {
      for ($j = 0; $j < $i; ++$j ) {
        $cmp = $this->strategy->compare(
          $array[$j],
          $array[$j+1]
        );

        if ($cmp > 0) {
          $tmp         = $array[$j];
          $array[$j]   = $array[$j+1];
          $array[$j+1] = $tmp;
        }
      }
    }

    return $array;
  }
}

$bs = new BubbleSort;

$bs->setStrategy(new AscendingCompare);
print_r($bs->sort(array(22, 4, 1978)));

$bs->setStrategy(new DescendingCompare);
print_r($bs->sort(array(22, 4, 1978)));
?>
Array
(
    [0] => 4
    [1] => 22
    [2] => 1978
)

Array
(
    [0] => 1978
    [1] => 22
    [2] => 4
)

In seiner Zielrichtung ist das Strategie-Muster mit der Schablonenmethode verwandt. Der Unterschied zwischen diesen beiden Mustern liegt in der Wahl des Mittels, mit dem man das Ziel zu erreichen versucht: Während das Strategie-Muster mittels Delegation den gesamten Algorithmus zur Laufzeit austauschbar macht, nutzt das Muster der Schablonenmethode Vererbung, um einzelne Schritte einer Operation variabel zu gestalten.

(Quelle: Strategy-Muster, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Das Observer-Muster

Problem

Wenn ein Objekt seinen Zustand ändert, sollen davon abhängige Objekte benachrichtigt werden.

Motivation

Eine Änderung an einem Objekt erfordert Änderungen an anderen Objekten, um die Konsistenz des Gesamtsystems zu erhalten. An Stelle einer engen Kopplung wird eine lose Kopplung der Objekte angestrebt, bei der die beteiligten Objekte unabhängig voneinander variiert werden können.

Lösung

Das unter Beobachtung stehende Objekt, Subject genannt, stellt die folgenden Methoden zur Verfügung, über die sich andere Objekte, Observer genannt, für die Benachrichtigung bei Zustandsänderungen an- und abmelden können:

attach($observer)

Registriert das Objekt $observer zur Benachrichtigung bei Änderung des Zustands des Objektes, dessen attach()-Methode aufgerufen wird.

detach($observer)

Hebt die Registrierung des Objektes $observer zur Benachrichtigung bei Änderung des Zustands des Objektes, dessen detach()-Methode aufgerufen wird.

getState()

Liefert den aktuellen Zustand des Objektes. Ein Beobachter-Objekt kann sich so nach der Benachrichtigung über eine Änderung des Zustands des beobachteten Objektes darüber informieren, wie sich dessen Zustand geändert hat.

Die Benachrichtigung der Beobachter-Objekte erfolgt durch den Aufruf der Methode notify() des Subject-Objektes. Diese Methode ruft auf jedem als Beobachter registrierten Objekt dessen update()-Methode auf, die von den Beobachter-Objekten bereitzustellen ist.

Die folgende Abbildung zeigt zwei Klassen Subject und Observer, die die beschriebene Funktionalität zur Verfügung stellen und als Basis für zwei konkrete Klassen, ConcreteSubject und ConcreteObserver, dienen.

Abbildung: Struktur des Beobachter-Musters

Beispiel: Die abstrakte Klasse Subject

(:source lang=php linenum:)
<?php
require_once 'Observer.php';

abstract class Subject {
  protected $observers = array();

  public function attach(Observer $observer) {
      $this->observers[] = $observer;
  }

  public function detach(Observer $observer) {
    for ($i = 0; $i < sizeof($this->observers); $i++) {
      if ($this->observers[$i] === $observer) {
        unset($this->observers[$i]);
      }
    }
  }

  protected function notify() {
    for ($i = 0; $i < sizeof($this->observers); $i++) {
      $this->observers[$i]->update();
    }
  }

  public abstract function getState();
}
?>

Beispiel: Die abstrakte Klasse Observer

(:source lang=php linenum:)
<?php
require_once 'Subject.php';

abstract class Observer {
  protected $subject = NULL;

  public function attach(Subject $subject) {
    $this->subject = $subject;
    $this->subject->attach($this);
  }

  public function detach() {
    if ($this->subject !== NULL) {
      $this->subject->detach($this);
    }
  }

  public abstract function update();
}
?>

Beispiel: Die Klasse ConcreteSubject

(:source lang=php linenum:)
<?php
require_once 'Subject.php';

class ConcreteSubject extends Subject {
  protected $state = NULL;

  public function getState() {
    return $this->state;
  }

  public function doSomething() {
    // ...

    $this->notify();
  }
}
?>

Beispiel: Die Klasse ConcreteObserver

(:source lang=php linenum:)
<?php
require_once 'Observer.php';

class ConcreteObserver extends Observer {
  protected $state = NULL;

  public function __construct(Subject $subject) {
    $this->attach($subject);
  }

  public function update() {
    print 'ConcreteObserver::update()';

    $this->state = $this->subject->getState();
  }
}
?>

Beispiel: Verwendung von Subject und Observer

(:source lang=php linenum:)
<?php
require_once 'ConcreteSubject.php';
require_once 'ConcreteObserver.php';

$subject  = new ConcreteSubject;
$observer = new ConcreteObserver($subject);

$subject->doSomething();
$observer->detach();
$subject->doSomething();
?>

ConcreteObserver::update()

Anwendungsbeispiele

Das Problem, das ursprünglich das Beobachter-Muster motivierte, war die Kommunikation zwischen dem Model-Objekt und dessen View-Objekten in einer Applikation, die dem Model-View-Controller-Prinzip (MVC) folgt. Hierbei wird eine Anwendung in die drei Schichten Datenmodell (Model), Darstellungsschicht (View) und Steuerungsschicht (Controller) unterteilt. Diese Schichten sind voneinander entkoppelt: Das Datenmodell kennt weder Darstellungsschicht noch Steuerungsschicht. Die Darstellungsschicht registriert sich als Beobachter des Datenmodells und stellt dieses dar. Die Steuerungsschicht steuert den Ablauf der Anwendung und nimmt Änderungen am Datenmodell über dessen Programmierschnittstelle vor.

Ein anderes Einsatzgebiet des Beobachter-Musters ist das Protokollieren von Abläufen. Für die Verfolgung, und damit für das Protokollieren, der Testausführung bietet PHPUnit die Schnittstelle PHPUnit2_Framework_TestListener an. Objekte von Klassen, die diese implementieren, können über eine entsprechende Methode (addListener($listener)) an ein Objekt der Klasse PHPUnit2_Framework_TestResult "angehängt" werden, um dieses zu beobachten und so die Testausführung zu protokollieren.

Beispiel: Eine Implementierung der Schnittstelle PHPUnit2_Framework_TestListener

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/TestListener.php';

class SimpleTestListener
implements PHPUnit2_Framework_TestListener {
  public function
  addError(PHPUnit2_Framework_Test $test, Exception $e) {
    printf(
      'Bei der Ausführung des Testfalls "%s"' .
      " trat ein Fehler auf.\n",
      $test->getName()
    );
  }

  public function
  addFailure(PHPUnit2_Framework_Test $test,
             PHPUnit2_Framework_AssertionFailedError $e) {
    printf(
      "Der Testfall \"%s\" schlug fehl.\n",
      $test->getName()
    );
  }

  public function
  addIncompleteTest(PHPUnit2_Framework_Test $test,
                    Exception $e) {
    printf(
      "Der Testfall \"%s\" wurde nicht implementiert.\n",
      $test->getName()
    );
  }

  public function startTest(PHPUnit2_Framework_Test $test) {
    printf(
      "Ausführung des Testfalls \"%s\" wurde gestartet.\n",
      $test->getName()
    );
  }

  public function endTest(PHPUnit2_Framework_Test $test) {
    printf(
      "Ausführung des Testfalls \"%s\" wurde beendet.\n",
      $test->getName()
    );
  }

  public function
  startTestSuite(PHPUnit2_Framework_TestSuite $suite) {
  }

  public function
  endTestSuite(PHPUnit2_Framework_TestSuite $suite) {
  }
}
?>

Im nächsten Beispiel wird zunächst ein neues Objekt der Klasse PHPUnit2_Framework_TestResult erzeugt. Diesem wird im Anschluss ein Objekt der Klasse SimpleTestListener aus dem letzten Beispiel als Listener hinzugefügt. Schließlich wird das PHPUnit2_Framework_TestResult-Objekt genutzt, um den testDoSomething Testfall auszuführen.

Beispiel: Ausführung eines Testfalls unter Beobachtung des SimpleTestListener

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/TestResult.php';

require_once 'SampleTest.php';
require_once 'SimpleTestListener.php';

$test = new SampleTest('testDoSomething');

$result = new PHPUnit2_Framework_TestResult;
$result->addListener(new SimpleTestListener);
$result->run($test);
?>
Ausführung des Testfalls "testDoSomething" wurde gestartet.
Ausführung des Testfalls "testDoSomething" wurde beendet.

(Quelle: Observer-Muster, Sebastian Bergmann, 01.03.2008, 17:00 GMT+1)

Das Decorator-Muster

Problem

Objekte sollen dynamisch um Funktionalität erweitert werden können, ohne die zugehörige Klasse durch Unterklassenbildung statisch erweitern zu müssen.

Motivation

Oft kommt es vor, dass man die Funktionalität eines Objektes dynamisch und transparent erweitern oder verändern möchte. Dem statischen Ansatz der Erweiterung der Klassenhierarchie um entsprechende Unterklassen ist meist eine objektbasierte Lösung vorzuziehen. Hierbei kann die Funktionalität verschiedener Objekte durch lose Kopplung zur Laufzeit "zusammengesteckt" werden.

Lösung

Die erweiterte oder veränderte Funktionalität wird in einem so genannten Dekorierer-Objekt modelliert, das dieselbe Schnittstelle wie das zu dekorierende Objekt anbietet. Dies erlaubt die transparente Benutzung des Dekorierer-Objekts durch Verwender des ursprünglichen Objektes. Das Dekorierer-Objekt leitet Methodenaufrufe zur Ausführung an das aggregierte, zu dekorierende Objekt weiter und kann seine zusätzliche Funktionalität vor oder nach diesem Methodenaufruf ausführen.

Anwendungsbeispiele

Die Klassen FilterIterator und LimitIterator sind zwei Dekorierer. Objekte dieser Iterator-Klassen dekorieren ein anderes Iterator-Objekt und filtern oder limitieren dessen Elemente.

Für die Anpassung und Erweiterung der Testausführung bietet PHPUnit die Dekorierung der Klasse PHPUnit2_Framework_TestCase an. Das Grundgerüst des entsprechenden Dekorierers liegt in Form der Klasse PHPUnit2_Extensions_TestDecorator vor. Diese implementiert die vom PHPUnit-Framework für die Testausführung benötigte Schnittstelle PHPUnit2_Framework_Test und stellt ebenso wie die Klasse PHPUnit2_Framework_TestCase die Zusicherungsmethoden der Klasse PHPUnit2_Framework_Assert bereit. Sie kann daher von jedem Verwender, der eigentlich ein Objekt der Klasse PHPUnit2_Framework_TestCase erwartet, verarbeitet werden.

Beispiel: Die Klasse PHPUnit2_Extensions_TestDecorator

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/Assert.php';
require_once 'PHPUnit2/Framework/Test.php';
require_once 'PHPUnit2/Framework/TestResult.php';

class PHPUnit2_Extensions_TestDecorator
extends PHPUnit2_Framework_Assert
implements PHPUnit2_Framework_Test {
  protected $test = NULL;

  public function
  __construct(PHPUnit2_Framework_Test $test) {
    $this->test = $test;
  }

  public function toString() {
    return $this->test->toString();
  }

  public function
  basicRun(PHPUnit2_Framework_TestResult $result) {
    $this->test->run($result);
  }

  public function countTestCases() {
    return $this->test->countTestCases();
  }

  protected function createResult() {
    return new PHPUnit2_Framework_TestResult;
  }

  public function getTest() {
    return $this->test;
  }

  public function
  run(PHPUnit2_Framework_TestResult $result) {
    $this->basicRun($result);
    return $result;
  }
}
?>

Beispiel: Die Klasse PHPUnit2_Extensions_RepeatedTest

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/Test.php';
require_once 'PHPUnit2/Framework/TestResult.php';
require_once 'PHPUnit2/Extensions/TestDecorator.php';

class PHPUnit2_Extensions_RepeatedTest
extends PHPUnit2_Extensions_TestDecorator {
  private $timesRepeat = 1;

  public function
  __construct(PHPUnit2_Framework_Test $test,
              $timesRepeat = 1) {
      parent::__construct($test);

      if (is_integer($timesRepeat) &&
          $timesRepeat >= 0) {
          $this->timesRepeat = $timesRepeat;
      } else {
          throw new Exception('Illegal argument.');
      }
  }

  public function countTestCases() {
      return $this->timesRepeat *
             $this->test->countTestCases();
  }

  public function
  run(PHPUnit2_Framework_TestResult $result) {
    for ($i = 0;
         $i < $this->timesRepeat && !$result->shouldStop();
         $i++) {
      $this->test->run($result);
    }

    return $result;
  }
}
?>

Für die dekorierte Ausführung eines Tests, in unserem Beispiel also die wiederholte Ausführung einer Testfallmethode, wird zunächst ein Objekt der Testfallklasse erzeugt. Dieses wird anschließend dem Konstruktor der Dekorierer-Klasse (hier PHPUnit2_Extensions_RepeatedTest) übergeben, um ein entsprechendes Dekorierer-Objekt zu erzeugen, das dann für die Testausführung mit PHPUnit2_Framework_TestResult verwendet werden kann.

Beispiel: Wiederholte Testausführung mit PHPUnit2_Extensions_RepeatedTest

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/TestResult.php';
require_once 'PHPUnit2/Extensions/RepeatedTest.php';

require_once 'SampleTest.php';

$result = new PHPUnit2_Framework_TestResult;

// Führt SampleTest::testDoSomething() einmal aus.
$test = new SampleTest('testDoSomething');
$result->run($test);

// Führt SampleTest::testDoSomething() einmal aus.
$decoratedTest = new PHPUnit2_Extensions_RepeatedTest($test, 2);
$result->run($decoratedTest);
?>

(Quelle: Decorator-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

Das Factory-Muster

Das Factory-Muster erlaubt die Instanziierung eines Objektes zur Laufzeit. Da es für die "Herstellung" von Objekten zuständig ist, wird es Factory-Muster gennant.

Beispiel: Factory Method

(:source lang=php linenum:)
<?php
class Example
{
   // The factory method
   public static function factory($type)
   {
       if (include_once 'Drivers/' . $type . '.php') {
           $classname = 'Driver_' . $type;
           return new $classname;
       } else {
           throw new Exception ('Driver not found');
       }
   }
}
?>

Diese Methode definiert in einer Klasse erlaubt das Laden von Treibern "on the fly". Wenn z.B. die Beispiel Klasse eine Datenbank Abstraktionsklasse wäre, so könnten die Treiber MySQL und SQLite wie folgt geladen werden:

(:source lang=php linenum:)
<?php
// Load a MySQL Driver
$mysql = Example::factory('MySQL');

// Load a SQLite Driver
$sqlite = Example::factory('SQLite');
?>

(Quelle: Factory Method, 01.03.2008, 16:30 GMT+1)

Eine Factory ist also ein Hilfsmittel zur Erzeugung von Objekten. Sie wird verwendet, wenn die zur Generierung des Objekts verwendete Klasse erst zur Laufzeit bekannt ist.

Ein weiteres Beispiel für eine Factory Methode könnte folgendermaßen aussehen:

(:source lang=php linenum:)
class Meine_Klasse
{
    static public function factory($className, $params = null)
    {
        if (! is_string($className) || ! strlen($className)) {
            throw new exception(
                'Die zu ladende Klasse muss in einer Zeichenkette benannt werden');
        }

        require_once $className . '.php';
        return new $className($params);
    }
}

$params = array(
    'param1' => null,
    'param2' => null,
);

$object = Meine_Klasse::factory('test_klasse_konkret', $params);

In diesem Beispiel ist MDB2::connect() die factory-Methode und liefert ein Datenbank-Verbindungs-Objekt oder im Fehlerfall ein PEAR-Error-Objekt zurück.

(:source lang=php linenum:)
require_once 'MDB2.php';

$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
    'debug' => 2,
    'result_buffering' => false,
);

$mdb2 = MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
    die($mdb2->getMessage());
}

// ...

$mdb2->disconnect();

(Quelle: Factory Method, 01.03.2008, 16:30 GMT+1)

Abstract Factory

Problem

Objekte verwandter Klassen sollen erzeugt werden, so dass die zu verwendende Klasse erst zur Laufzeit festgelegt werden kann.

Motivation

Eine Aufgabe kann auf unterschiedliche Arten, beispielweise unter Verwendung verschiedener Protokolle oder Verfahren, durchgeführt werden. Die einzelnen Implementierungen sollen zur Laufzeit des Programms austauschbar sein.

Lösung

Die gemeinsame Funktionalität der einzelnen Implementierungen wird in einer abstrakten Basisklasse gekapselt. Die direkte Erzeugung von Objekten der Kindklassen dieser Basisklasse wird durch die Deklaration der entsprechenden Konstruktoren als protected oder private unterbunden. Für die Erzeugung von Objekten bietet die abstrakte Basisklasse eine statische Methode an, die anhand des übergebenen Parameters ein Objekt des gewünschten Typs erzeugt und zur Verfügung stellt.

Anwendungsbeispiele

Betrachten wir als Beispiel einen Online-Shop, der seinen Partnern ein Interface zum Produktkatalog zur Verfügung stellt. Der eine Partner wünscht eine Schnittstelle auf XML-RPC-Basis, ein weiterer würde gerne SOAP nutzen können, während ein dritter Geschäftspartner ein naives Protokoll auf der Basis von HTTP GET und POST verlangt.

Die Programmlogik ist bei den drei Varianten gleich, sie wird also zunächst in einer Basisklasse gekapselt. Von dieser Basisklasse leiten sich drei Klassen, je eine für XML-RPC, SOAP und HTTP GET / POST ab. Diese abgeleiteten Klassen implementieren den jeweiligen Kommunikationsmechanismus.

Bislang haben wir nur mit dem Konzept der Vererbung Coderedundanzen vermieden. Allerdings ist das Anlegen von Instanzen der drei Klassen aus der Applikation heraus noch nicht transparent, die Nutzung komplizierter als nötig, wie das folgende Beispiel zeigt.

Beispiel: Verwendung der drei Klassen ohne abstrakte Fabrik

(:source lang=php linenum:)
<?php
switch ($type) {
  case 'HTTP': {
    include_once 'partner_interface/http.php';
    $interface = new PartnerInterface_HTTP;
  }
  break;

  case 'SOAP': {
    include_once 'partner_interface/soap.php';
    $interface = new PartnerInterface_SOAP;
  }
  break;

  case 'XML-RPC': {
    include_once 'partner_interface/xml-rpc.php';
    $interface = new PartnerInterface_XMLRPC;
  }
  break;
}
?>

Nachdem die Auswahl des zu erzeugenden Objektes in der Methode factory() der Klasse PartnerInterface an zentraler Stelle gekapselt wurde, gestaltet sich die Erzeugung des passenden Objektes im Kontext des Programmes einfach und flexibel, wie das folgende Beispiel zeigt.

Beispiel: Verwendung der drei Klassen mit abstrakter Fabrik

(:source lang=php linenum:)
<?php
$interface = PartnerInterface::factory($type);
?>

Das nächste Beispiel zeigt die Implementierung einer abstrakten Fabrik in PHP, das darauf folgende Beispiel das Grundgerüst einer konkreten Kindklasse, für die die Fabrik Objekte erzeugen kann.

Beispiel: Die abstrakte Klasse PartnerInterface

(:source lang=php linenum:)
<?php
abstract class PartnerInterface {
  protected function __construct() {}

  public static function factory($type) {
    $source = 'PartnerInterface/' . $type . '.php';

    if (@require_once($source)) {
      $class  = 'PartnerInterface_' . $type;
      $object = new $class;

      return $object;
    } else {
      throw new Exception(
        sprintf(
          'Konnte kein Objekt vom Typ %s erzeugen.',
          'PartnerInterface_' . $type
        )
      );
    }
  }

  public abstract function import($data);
  public abstract function export();
}
?>

Beispiel: Die konkrete Klasse PartnerInterface_HTTP

(:source lang=php linenum:)
<?php
require_once 'PartnerInterface.php';

class PartnerInterface_HTTP extends PartnerInterface {
  public function import($data) {
    // ...
  }

  public function export() {
    // ...
  }
}
?>

(Quelle: Factory-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

Das Singleton-Muster

Problem

Die Anzahl der Objekte einer Klasse soll beschränkt werden.

Motivation

Oft ist nur ein Objekt oder ein Pool mit einer festen Anzahl von Objekten einer Klasse sinnvoll, beispielsweise bei der Kapselung von externen Ressourcen.

Lösung

Die Erzeugung von Objekten durch den new-Operator wird durch Deklaration des Konstruktors als protected oder private unterbunden. Das Klonen des Objektes wird durch die Deklaration der Methode __clone() als private final unterbunden.

Für die Objekterzeugung wird eine statische Methode, meist getInstance() oder singleton() genannt, bereitgestellt. In dieser Methode kann nun entschieden werden, ob ein neues Objekt der Klasse erzeugt wird oder ob eine Referenz auf ein bereits erzeugtes Objekt zurückgegeben werden soll.

Abbildung: Struktur des Singleton-Patterns

Beispiel: Singleton

(:source lang=php linenum:)
<?php
class Singleton {
  private static $uniqueInstance = NULL;

  protected function __construct() {
    print "Neues Objekt wird erzeugt.\n";
  }

  private final function __clone() {}

  public static function getInstance() {
    if (self::$uniqueInstance === NULL) {
      self::$uniqueInstance = new Singleton;
    }

    return self::$uniqueInstance;
  }
}

$a = Singleton::getInstance();
$b = Singleton::getInstance();

if ($a === $b) {
    print '$a und $b referenzieren dasselbe Objekt.'."\n";
}
?>
Neues Objekt wird erzeugt.
$a und $b referenzieren dasselbe Objekt.

Anwendungsbeispiele

Klassen, die externe Ressourcen wie beispielsweise Datenbankverbindungen kapseln, sind in der Regel gute Kandidaten für die Verwendung des Singleton-Musters. Mit seiner Methode getInstance() bietet dieses einen globalen Zugriffspunkt auf das (einzige) Objekt einer Klasse. Ohne diese Möglichkeit müsste beispielsweise in jeder Methode einer Anwendung, in der auf die Datenbank zugegriffen wird, ein neues Objekt der entsprechenden Klasse erzeugt werden oder aber ein solches Objekt als Parameter übergeben werden. Beide Alternativen zur Anwendung des Singleton-Musters sind "unschön", Erstere unter dem Aspekt der Performanz sogar ein großer Fehler.

(Quelle: Singleton-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

Das Singleton-Muster wird in Situationen angewandt, in denen nur eine einzige Instanz einer Klasse benötigt wird. Das bekannteste Beispiel dafür ist eine Datenbankverbindung. Mit diesem Muster implementiert macht der Entwickler diese eine Instanz einfach erreichbar von vielen anderen Objekten.

Example: Singleton Function

(:source lang=php linenum:)
<?php
class Example
{
   // Hold an instance of the class
   private static $instance;

   // A private constructor; prevents direct creation of object
   private function __construct() 
   {
       echo 'I am constructed';
   }

   // The singleton method
   public static function singleton() 
   {
       if (!isset(self::$instance)) {
           $c = __CLASS__;
           self::$instance = new $c;
       }

       return self::$instance;
   }

   // Example method
   public function bark()
   {
       echo 'Woof!';
   }

   // Prevent users to clone the instance
   public function __clone()
   {
       trigger_error('Clone is not allowed.', E_USER_ERROR);
   }

}

?>

Dies erlaubt eine einzige Instanz der Klasse Example aufzurufen.

(:source lang=php linenum:)
<?php
// This would fail because the constructor is private
$test = new Example;

// This will always retrieve a single instance of the class
$test = Example::singleton();
$test->bark();

// This will issue an E_USER_ERROR.
$test_clone = clone($test);

?>

(Quelle: Singleton, 01.03.2008, 16:30 GMT+1)

Abstract Singleton

Ab PHP 5.3 möglich, da get_called_class() erst dort verfügbar:

(:source lang=php linenum:)
error_reporting(E_ALL|E_STRICT);  
ini_set('display_errors', true); 

abstract class Singleton 
{  
    private static $instances = array(); 

    final public static function getInstance()  
    {  
        $class = get_called_class(); 
        if (empty(self::$instances[$class])) { 
            $rc = new ReflectionClass($class);
            self::$instances[$class] = $rc->newInstanceArgs(func_get_args()); 
        } 
        return self::$instances[$class]; 
    }  

    protected function __construct() 
    {} 

    final private function __clone() 
    {} 
}  

class ConcreteSingleton extends Singleton  
{ 
    protected function __construct($string, Array $array) 
    { 
        echo __METHOD__ . '(' . $string . ', ' . print_r($array, true) . ')'; 
    } 
}  

$test = ConcreteSingleton::getInstance('Hello World', array(1,2,3));

(Quelle: Abstract Singleton, 01.03.2008, 16:30 GMT+1)

Das Command-Muster

Das Adapter & Facade Muster

Das Template Method-Muster

Problem

Unterklassen soll es ermöglicht werden, bestimmte Schritte einer Operation zu überschreiben, ohne deren Struktur zu verändern.

Motivation

Lässt sich eine Aufgabe in Einzeloperationen zerlegen, von denen eine oder mehrere unterschiedlich implementiert werden können, so bietet sich das Zusammenfassen der gemeinsamen Operationen in einer Basisklasse an. Die Unterklassen dieser Klassen implementieren ihrerseits nur die Einzelschritte, in denen sie sich voneinander unterscheiden.

Lösung

In der Basisklasse wird das Grundgerüst der Operation in einer als public final deklarierten Methode implementiert.

Muss ein Teilschritt, dessen Implementierung an eine Unterklasse delegiert werden soll, implementiert werden, so wird die entsprechende Methode in der Basisklasse als abstract protected deklariert. Soll die Implementierung hingegen optional sein, so wird die Methode lediglich als protected deklariert und verfügt in der Basisklasse nur über einen leeren Methodenrumpf.

Anwendungsbeispiele

Eine Schablonenmethode wird gerne eingesetzt, um eine Ausgabe in unterschiedlichen Formaten darstellen zu können. Die Klasse PHPUnit2_Extensions_CodeCoverage_Renderer des PHPUnit-Paketes kombiniert das Entwurfsmuster der abstrakten Fabrik (siehe „Abstrakte Fabrik“) mit einer Schablonenmethode und ermöglicht so die Darstellung von Code-Coverage-Informationen in unterschiedlichen Formaten durch entsprechende Unterklassen. Die Methode render() stellt hierbei die Schablone dar und ruft die den Einzelschritten entsprechenden Methoden in der vorgegebenen Reihenfolge auf.

Beispiel: Die abstrakte Klasse PHPUnit2_Extensions_CodeCoverage_Renderer

(:source lang=php linenum:)
<?php
abstract class PHPUnit2_Extensions_CodeCoverage_Renderer {
  protected $codeCoverageInformation;

  protected function __construct($codeCoverageInformation) {
    $this->codeCoverageInformation = $codeCoverageInformation;
  }

  public function factory($type, $codeCoverageInformation) {
    $class = 'PHPUnit2_Extensions_CodeCoverage_Renderer_' .
             $type;

    $source = 'PHPUnit2/Extensions/CodeCoverage/Renderer/' .
              $type . '.php';

    if (@require_once($source)) {
        $object = new $class($codeCoverageInformation);

      return $object;
    } else {
      throw new Exception(
        sprintf(
          'Could not load class %s.',
          $class
        )
      );
    }
  }

  public final function render() {
    $buffer = '';

    foreach ($this->codeCoverageInformation as
             $testCaseName => $sourceFiles) {
      $buffer .= $this->startTestCase($testCaseName);

      foreach ($sourceFiles as
               $sourceFile => $executedLines) {
        $buffer .= $this->startSourceFile($sourceFile);

        $buffer .= $this->renderSourceFile(
          file($sourceFile),
          $executedLines
        );

        $buffer .= $this->endSourceFile($sourceFile);
      }

      $buffer .= $this->endTestCase($testCaseName);
    }

    return $buffer;
  }

  protected function startTestCase($testCaseName) {
  }

  protected function endTestCase($testCaseName) {
  }

  protected function startSourceFile($sourceFile) {
  }

  protected function endSourceFile($sourceFile) {
  }

  abstract protected function
  renderSourceFile($codeLines, $executedLines);
}
?>

Beispiel 7.9: Die Klasse PHPUnit2_Extensions_CodeCoverage_Renderer_Text

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Util/CodeCoverage/Renderer.php';

class PHPUnit2_Extensions_CodeCoverage_Renderer_Text
extends PHPUnit2_Extensions_CodeCoverage_Renderer {
  protected function startTestCase($testCaseName) {
    return $testCaseName . "\n\n";
  }

  protected function endTestCase($testCaseName) {
    return "\n";
  }

  protected function startSourceFile($sourceFile) {
    return '  ' . $sourceFile . "\n\n";
  }

  protected function endSourceFile($sourceFile) {
    return "\n";
  }

  protected function renderSourceFile($codeLines, $executedLines) {
    $buffer = '';
    $line   = 1;

    foreach ($codeLines as $codeLine) {
      $buffer .= sprintf(
        '    %4u|%4s| %s',

        $line,
        (isset($executedLines[$line])) ? $executedLines[$line] . 'x' : '',
        $codeLine
      );

      $line++;
    }

    return $buffer;
  }
}
?>

(Quelle: Template Method-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

Das Iterator Muster

Problem

Der generische Zugriff auf die Elemente einer Sammlung soll ermöglicht werden, ohne dass die Struktur der Sammlung oder die Implementierung der einzelnen Elemente bekannt sein muss.

Motivation

Die Elemente unterschiedlicher Sammlungen, bei denen es sich beispielsweise um die Zeilen einer Textdatei, die Elemente eines XML-Dokumentes oder die Ergebniszeilen einer Datenbankabfrage handeln kann, sollen einheitlich verarbeitet werden können.

Lösung wird in den folgenden Abschnitten beschrieben.

(Quelle: Iterator-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

Einleitung

Das Aufzählen oder Durchlaufen der Elemente einer Menge ist ein wiederkehrendes Problem. Beispiele für solche Mengen sind Arrays, die Zeilen einer Textdatei, die Elemente eines XML-Dokumentes oder die Ergebniszeilen einer Datenbankabfrage.

In PHP 3 erfolgte das Durchlaufen eines numerisch indizierten Arrays mit Hilfe einer for()-Schleife. Hierbei musste der Programmierer auf die Indexgrenzen achten.

Beispiel: Iterieren von Arrays in PHP 3

(:source lang=php linenum:)
<?php
$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

for ($i = 0; $i < 10; $i++) {
  print $a[$i] . ' ';
}
?>

1 2 3 4 5 6 7 8 9 10

Assoziative Arrays mussten in PHP 3 mit reset(), while(), list() und each() verarbeitet werden.

Beispiel: Iterieren von assoziativen Arrays in PHP 3

(:source lang=php linenum:)
<?php
$a = array('key' => 'value');

reset($a);

while (list($k, $v) = each($a)) {
  print $k . ': ' . $v;
}
?>

key: value

In PHP 4 wurde mit foreach ein neuer Operator eingeführt, der die Arbeit mit assoziativen und numerisch indizierten Arrays vereinfachte und vereinheitlichte. Nun war es nicht mehr Aufgabe des Programmierers, sich um Dinge wie Indexgrenzen oder das Fortschreiten zum nächsten Element zu kümmern.

Beispiel: Iterieren von Arrays in PHP 4

(:source lang=php linenum:)
<?php
$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$b = array('key' => 'value');

foreach ($a as $v) {
  print $v . ' ';
}

foreach ($b as $k => $v) {
  print $k . ': ' . $v;
}
?>
1 2 3 4 5 6 7 8 9 10
key: value

Bereits in PHP 4 war es möglich, den foreach-Operator auf ein Objekt anzuwenden. In diesem Fall wurden die Instanzvariablen des Objektes wie ein assoziatives Array durchlaufen (nächstes Beispiel). Dies ist in PHP 5 weiterhin möglich, jedoch werden hierbei nur die öffentlichen Instanzvariablen berücksichtigt.

Beispiel: Verwendung von foreach() mit Objekten

(:source lang=php linenum:)
<?php
class Test {
  var $a = 1;
  var $b = 2;
}

$test = new Test;

foreach ($test as $name => $value) {
  print "$name: $value\n";
}
?>
a: 1
b: 2

An dieser Stelle wünschen wir uns ein Konzept, das die Vereinheitlichung von assoziativen und numerisch indizierten Arrays verallgemeinert und auf Objekte erweitert. Dieses Konzept ist das Iterator Entwurfsmuster, das wir im Folgenden diskutieren wollen.

Ein Iterator ermöglicht den generischen Zugriff auf die Elemente einer Menge, ohne dass die Struktur der Menge oder die Implementierung der einzelnen Elemente bekannt sein muss. Bei den Elementen einer solchen Menge kann es sich beispielsweise um die Zeilen einer Textdatei, die Elemente eines XML-Dokumentes oder die Ergebniszeilen einer Datenbankabfrage handeln.

(Quelle: Aufzählen in PHP 3 & 4, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

Die Iterator-Schnittstellen von PHP 5

PHP bietet mit den Schnittstellen Iterator und IteratorAggregate eine Integration des Iterator-Entwurfsmusters in die Programmiersprache selbst: Objekte, die die Schnittstelle Iterator anbieten, können mit dem foreach-Operator verwendet werden.

Abbildung: Die Schnittstellen Iterator, IteratorAggregate und Traversable

Wendet man den foreach-Operator auf ein Objekt an, das die Schnittstelle Iterator anbietet, so bedient sich der foreach-Operator der folgenden Methoden, um an die Daten des Objektes zu gelangen:

  • rewind() setzt den Iterator zurück auf das erste Element der Menge.
  • valid() prüft, ob nach einem Aufruf von rewind() oder next() ein aktuelles Element existiert.
  • key() liefert den Schlüssel des aktuellen Elementes.
  • current() liefert den Wert des aktuellen Elementes.
  • next() setzt den Iterator auf das nächste Element der Menge.

Das folgende Beispiel zeigt eine Implementierung der Schnittstelle Iterator, die über die (durch Leerzeichen getrennten) Teile eines Strings iteriert.

Beispiel: Eine Implementierung der Schnittstelle Iterator

(:source lang=php linenum:)
<?php
class StringIterator implements Iterator {
  private $string;
  private $position;

  public function __construct($string) {
    $this->string = explode(' ', $string);
  }

  public function rewind() {
    $this->position = 0;
  }

  public function valid() {
    return $this->position < sizeof($this->string);
  }

  public function key() {
    return $this->position;
  }

  public function current() {
    return $this->string[$this->position];
  }

  public function next() {
    $this->position++;
  }
}
?>

Beispiel: Implizite Verwendung von Iteratoren mit dem foreach-Operator

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

$iterator = new StringIterator('Dies ist ein String.');

foreach ($iterator as $key => $value) {
  print $value . ' ';
}
?>

Dies ist ein String.

Das Iterieren eines Objektes, dessen Klasse die Schnittstelle Iterator implementiert, kann neben der impliziten Verwendung durch die Benutzung des foreach-Operators (s. letztes Beispiel) auch explizit durch die Verwendung der in der Schnittstelle vereinbarten Methoden erfolgen (nächstes Beispiel). Hierbei wird deutlich, dass die Methoden der Schnittstelle Iterator den aus PHP 3 bekannten Funktionen für das Durchlaufen von assoziativen Arrays entsprechen.

Beispiel: Explizite Verwendung von Iteratoren

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

$iterator = new StringIterator('Dies ist ein String.');

for ($iterator->rewind();
     $iterator->valid();
     $iterator->next()) {
  $key   = $iterator->key();
  $value = $iterator->current();

  print $value . ' ';
}
?>

Dies ist ein String.

Bislang müssen wir für eine Klasse, für die eine entsprechende Iterator-Klasse zur Verfügung steht, "von Hand" ein Iterator-Objekt erzeugen. Hier hilft die Implementierung der Schnittstelle IteratorAggregate durch die Klasse. Deren Methode getIterator liefert ein Objekt der entsprechenden Iterator-Klasse. Verwendet man ein Objekt, das die Schnittstelle IteratorAggregate anbietet, mit dem foreach-Operator, so ruft der PHP-Interpreter automatisch getIterator auf und verwendet das zurückgegebene Iterator-Objekt.

Das nächste Beispiel zeigt eine Klasse String, die die Schnittstelle IteratorAggregate implementiert. Wird ein Objekt dieser Klasse zusammen mit dem foreach-Operator verwendet, so wird das String-Objekt unter Verwendung eines StringIterator-Objektes durchlaufen, ohne dass dieses von Hand erzeugt werden muss.

Beispiel: Eine Implementierung der Schnittstelle IteratorAggregate

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

class String implements IteratorAggregate {
  private $string;

  public function __construct($string = '') {
    $this->string = $string;
  }

  public function getIterator() {
    return new StringIterator($this->string);
  }

  // ...
}

$string = new String('Dies ist ein String.');

foreach ($string as $key => $value) {
  print $value . ' ';
}
?>

Dies ist ein String.

(Quelle: Iterator-Schnittstelle von PHP 5, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

Die Standard PHP Library (SPL)

Die Standard PHP Library (SPL) ist fester Bestandteil von PHP 5 und baut auf den Iterator-Schnittstellen auf. Sie tritt an, ein PHP-Pendant zu der von C++ bekannten Standard Template Library (STL) zu werden.

In PHP 5.0 umfasst die Standard PHP Library die folgenden Klassen und Schnittstellen:

  • FilterIterator, LimitIterator und SeekableIterator bieten Funktionen für das Filtern, Einschränken und Suchen von Elementen einer Menge. Sie bilden den Kern der Standard PHP Library.
  • ArrayObject und ArrayIterator bieten eine alternative Möglichkeit für die Arbeit mit Arrays an.
  • RecursiveIterator, RecursiveIteratorIterator und ParentIterator stellen die Funktionalität zur Verfügung, um rekursiv mit verschachtelten Iteratoren arbeiten zu können.
  • RecursiveIterator ist hierbei eine Schnittstelle, die von einer Iterator-Klasse implementiert werden muss, wenn ihre Objekte von einem RecursiveIteratorIterator rekursiv verarbeitet werden sollen. Ein Beispiel für eine Klasse, die RecursiveIterator implementiert, ist SimpleXMLIterator.
  • DirectoryIterator und RecursiveDirectoryIterator bieten eine einfache und effiziente Möglichkeit, über die Dateien und Unterverzeichnisse eines Verzeichnisses im Dateisystem zu iterieren.
  • CachingIterator und CachingRecursiveIterator erweitern das Standardverhalten von Iterator-Implementierungen, indem sie immer ein Element im Voraus lesen.

Betrachten wir einmal die Aufgabe, die Elemente einer Menge gefiltert zu verarbeiten. Mit Hilfe eines Iterators sind wir bereits in der Lage, die Elemente einer beliebigen Menge zu durchlaufen. Eine einfache Möglichkeit, aus einer Menge von Teilstrings nur diejenigen auszugeben, die mit "Bar" beginnen, sehen wir im folgenden Beispiel.

Beispiel: Filtern einer Menge von Teilstrings

(:source lang=php linenum:)
<?php
$string = new String('Foo Bar Barbara');

foreach ($string as $key => $value) {
  if (strpos($value, 'Bar') === 0) {
    print $value . "\n";
  }
}
?>
Bar
Barbara

Im letzten Beispiel wenden wir die Filterregel direkt in der Schleife an, mit der wir die Elemente der Menge durchlaufen. Dies wird jedoch zum einen bei komplexeren Filteroperationen schnell unübersichtlich, zum anderen stößt diese Methode an Grenzen, wenn es darum geht, Filterregeln dynamisch auszutauschen oder zu kombinieren.

Die Standard PHP Library bietet für dieses Problem die Möglichkeit an, Iterator-Objekte zu kombinieren. Hierbei kontrolliert ein äußerer Iterator einen inneren Iterator. Der innere Iterator arbeitet auf der eigentlichen Menge von Elementen, die verarbeitet werden soll. Der äußere Iterator entscheidet jedoch für jedes dieser Elemente, ob und in welcher Weise es an den Verwender zurückgegeben werden soll. Hinter diesem Konzept steht mit dem Dekorierer ein weiteres Entwurfsmuster, das wir später noch genauer behandeln werden.

Abbildung: FilterIterator, LimitIterator und SeekableIterator

Die abstrakte Klasse FilterIterator erlaubt das Schreiben einer Klasse für die Verwendung als äußeren Iterator. Dieser filtert die Elemente des inneren Iterators, der als Objekt dem Konstruktor zu übergeben ist. Die Filterkriterien sind hierbei in der Methode accept() zu implementieren. In dieser Methode kann über $this->getInnerIterator()->current() auf das aktuelle Element des inneren Iterators zugegriffen werden. Soll dieses akzeptiert (also an den Verwender des äußeren Iterators weitergereicht) werden, so muss die Methode TRUE als Ergebnis liefern.

Das nächste Beispiel zeigt eine von FilterIterator abgeleitete Klasse, die den aus dem letzten Beispiel bekannten Filter implementiert.

Beispiel: Eine FilterIterator-Implementierung

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

class StringFilterIterator extends FilterIterator {
  private $prefix;

  public function
  __construct(StringIterator $stringIterator, $prefix) {
    parent::__construct($stringIterator);
    $this->prefix = $prefix;
  }

  public function accept() {
    $current = $this->getInnerIterator()->current();

    if (strpos($current, $this->prefix) === 0) {
      return TRUE;
    }

    return FALSE;
  }
}

$stringIterator = new StringIterator('Foo Bar Barbara');
$filterIterator = new StringFilterIterator(
  $stringIterator,
  'Bar'
);

foreach ($filterIterator as $key => $value) {
    print "$value\n";
}
?>
Bar
Barbara

Ein Objekt der Klasse LimitIterator erlaubt das Limitieren der Elemente des inneren Iterators, für den es als äußerer Iterator in Aktion tritt. Der Konstruktor erwartet neben dem inneren Iterator zwei optionale Parameter $offset und $count. Der Erste gibt die Nummer des ersten Elements an (beginnend bei 0), das akzeptiert werden soll. Der Zweite die maximale Anzahl an Elementen. Im folgenden Beispiel werden so alle Elemente ab einschließlich des Dritten ausgegeben.

Beispiel: Verwendung der Klasse LimitIterator

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

$stringIterator = new StringIterator('Foo Bar Barbara');
$limitIterator  = new LimitIterator($stringIterator, 2);

foreach ($limitIterator as $key => $value) {
  print "$value\n";
}
?>

Barbara

Der äußere Iterator eines inneren Iterators kann seinerseits als innerer Iterator für einen weiteren Iterator dienen. Im nächsten Beispiel ist der StringFilterIterator einerseits äußerer Iterator für den StringIterator, andererseits aber auch innerer Iterator für den LimitIterator.

Beispiel: Kombination von FilterIterator und LimitIterator

(:source lang=php linenum:)
<?php
require_once 'StringFilterIterator.php';

$stringIterator = new StringIterator('Foo Bar Barbara');
$filterIterator = new StringFilterIterator($stringIterator, 'Bar');
$limitIterator  = new LimitIterator($filterIterator, 1, 1);

foreach ($limitIterator as $key => $value) {
  print $value . "\n";
}
?>@

[@Barbara

Im Standardfall nutzt ein LimitIterator-Objekt wiederholte Aufrufe der Methode next(), die von der Schnittstelle Iterator vereinbart wird, um den durch die Parameter $offset und $count angegebenen Elementebereich zu erreichen. Bei der Verarbeitung der Ergebniszeilen einer Datenbankabfrage hat dieses Vorgehen jedoch einen Nachteil: Die vor dem gewünschten Bereich auftretenden Ergebniszeilen werden beispielsweise mit mysql_fetch_assoc() über die Verbindung zum Datenbankserver in den Speicher des PHP-Interpreters geladen, nur um gleich darauf unverarbeitet wieder überschrieben zu werden. Sinnvoller wäre an dieser Stelle die Verwendung der Funktion mysql_data_seek(), um direkt an die richtige Stelle der Ergebnismenge zu springen.

Für Mengen, bei denen die Position auf ein bestimmtes Element direkt gesetzt werden kann, bietet sich die Erweiterung der Klasse LimitIterator durch eine Kindklasse an, die zusätzlich die Schnittstelle SeekableIterator implementiert. Als einzige Methode dieser Schnittstelle ist seek($position) zu implementieren. Ein Objekt einer solchen Klasse kann nun effizient an die gewünschte Position springen.

(Quelle: Standard PHP Library, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

Die Schnittstelle ArrayAccess

Objekte von Klassen, die die Schnittstelle ArrayAccess (s. Abbildung) implementieren, können wie normale PHP-Arrays verwendet werden. Der Unterschied zu diesen ist jedoch, dass der Programmierer festlegen kann, was beispielsweise bei der Ausführung von $array[$offset] = $value passieren soll.

Abbildung: Die Schnittstelle ArrayAccess

Die vier zu implementierenden Methoden sind:

  • offsetExists($offset) wird für isset($array[$offset]) aufgerufen.
  • offsetGet($offset) wird für $value = $array[$offset] aufgerufen.
  • offsetSet($offset, $value) wird für $array[$offset] = $value aufgerufen. Hierbei ist zu beachten, dass bei Verwendung der $array[] = $value Syntax $offset den Wert NULL enthält.
  • offsetUnset($offset) wird für unset($array[$offset]) aufgerufen.

Das nächste Beispiel zeigt eine Implementierung der normalen PHP-Array-Datenstruktur als Klasse unter Verwendung der Schnittstelle ArrayAccess.

Beispiel: Eine Implementierung der ArrayAccess-Schnittstelle

(:source lang=php linenum:)
<?php
class PHPArray implements ArrayAccess {
  private $array = array();

  public function offsetExists($offset) {
    return array_key_exists($this->array, $offset);
  }

  public function offsetGet($offset) {
    return $this->array[$offset];
  }

  public function offsetSet($offset, $value) {
    if (is_null($offset)) {
      $this->array[] = $value;
    } else {
      $this->array[$offset] = $value;
    }
  }

  public function offsetUnset($offset) {
    unset($this->array[$offset]);
  }
}

$test = new PHPArray;

$test[] = 'Hello'; $test[] = ' World!';
print $test[0] . $test[1];
?>

Hello World!

Nachdem wir uns mit der grundsätzlichen Verwendung der Schnittstelle ArrayAccess vertraut gemacht haben, kommen wir zu einer möglichen Verwendung.

Im folgenden Beispiel benutzen wir eine Implementierung der Schnittstelle ArrayAccess, um ein Array $_SHARED zu erzeugen, auf dessen Inhalt von unterschiedlichen PHP-Instanzen aus zugegriffen werden kann. Für die notwendige Datenhaltung benutzen wir die in PHP 5 eingebettete Datenbank SQLite. Mit Hilfe der ArrayAccess-Implementierung "verbergen" wir die Datenhaltung vor dem Verwender des Arrays.

Beispiel: Die Klasse SharedArray

(:source lang=php linenum:)
<?php
class SharedArray implements ArrayAccess {
  private $db = NULL;

  public function __construct() {
    if ($this->db === NULL &&
        $this->db = sqlite_open('shared_array.db')) {
      @sqlite_query(
        $this->db,
        'CREATE TABLE shared_array
         (offset varchar(32) PRIMARY KEY,
          value  varchar(32));'
      );
    }
  }

  public function __destruct() {
    if ($this->db !== NULL) {
      sqlite_close($this->db);
    }

    $this->db = NULL;
  }

  public function offsetExists($offset) {
    $result = sqlite_query(
      $this->db,
      "SELECT offset
         FROM shared_array
        WHERE offset = '$offset';"
    );

    if ($result === FALSE) {
      return FALSE;
    } else {
      return TRUE;
    }
  }

  public function offsetGet($offset) {
    return sqlite_fetch_single(
      sqlite_query(
        $this->db,
        "SELECT value
           FROM shared_array
          WHERE offset = '$offset';"
      )
    );
  }

  public function offsetSet($offset, $value) {
    sqlite_query(
      $this->db,
      "REPLACE INTO shared_array
               (offset, value)
        VALUES ('$offset', '$value');"
    );
  }

  public function offsetUnset($offset) {
    sqlite_query(
      $this->db,
      "DELETE FROM shared_array
        WHERE offset = '$offset'"
    );
  }
}

$_SHARED = new SharedArray;
?>

(Quelle: ArrayAccess Schnittstelle, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

Das Proxy-Muster

Problem

Der Zugriff auf ein Objekt soll durch ein vorgelagertes Stellvertreterobjekt kontrolliert werden.

Motivation

Funktionalität wie beispielsweise Zugriffskontrolle, die Verzögerung von Berechnungen, oder das Vorhalten von bereits berechneten Ergebnissen möchte man in einer eigenen Klasse modellieren. Ein Objekt dieser Klasse wird als Stellvertreter für ein anderes Objekt verwendet.

Lösung

Die beiden Klassen, von denen die Objekte der einen Objekte der anderen vertreten sollen, implementieren dieselbe Schnittstelle. Über den Konstruktor erhält das Stellvertreterobjekt eine Referenz auf das zu vertretende Objekt, um Methodenaufrufe gegebenfalls an dieses delegieren zu können.

Anwendungsbeispiele

Durch die Vorlagerung eines Stellvertreterobjektes kann bei einem Methodenaufruf entschieden werden, ob der unter Umständen teure Aufruf der Methode des eigentlichen Objektes überhaupt ausgeführt werden muss. Wird die Methode zum wiederholten Male mit demselben Parameter aufgerufen, und liefert die Methode für identische Parameter immer dasselbe Ergebnis, so kann die wiederholte Ausführung durch Speicherung des Ergebnisses vermieden werden.

Die beiden folgenden Beispiele zeigen eine Schnittstelle TeureBerechnung und mit Beispiel eine entsprechende Implementierung.

Beispiel: Die Schnittstelle TeureBerechnung

(:source lang=php linenum:)
<?php
interface TeureBerechnung {
  public function rechne($a);
}
?>

Beispiel: Die Klasse Beispiel

(:source lang=php linenum:)
<?php
class Beispiel implements TeureBerechnung {
  public function rechne($a) {
    // ...
  }
}
?>

Die Klasse BeispielStellvertreter implementiert ebenfalls die Schnittstelle TeureBerechnung. Darüber hinaus bietet sie jedoch einen Caching-Mechanismus, der das Ergebnis der Methodenaufrufe von rechne($a) speichert und so die wiederholte Berechnung für gleiche Parameter $a verhindert.

Beispiel: Die Klasse BeispielStellvertreter

(:source lang=php linenum:)
<?php
class BeispielStellvertreter implements TeureBerechnung {
  private $cache = array();
  private $objekt;

  public function __construct(TeureBerechnung $objekt) {
    $this->objekt = $objekt;
  }

  public function rechne($a) {
    if (!isset($this->cache[$a])) {
      $this->cache[$a] = $this->objekt->rechne($a);
    }

    return $this->cache[$a];
  }
}
?>

Das Stellvertreter-Muster spielt in der verteilten Programmierung ebenfalls eine Rolle. So werden beispielsweise die über einen Webdienst angebotenen Methoden auf der Client-Seite von einem Stellvertreter-Objekt repräsentiert.

In ihrer Implementierung ähneln sich die Entwurfsmuster Dekorierer und Stellvertreter. Der Unterschied zwischen diesen beiden Mustern liegt im Ziel, das sie verfolgen. Ein Dekorierer wird genutzt, um die Funktionalität eines bestehenden Objektes zu erweitern oder zu verändern, während ein Stellvertreter den Zugriff auf das bestehende Objekt kontrolliert.

(Quelle: Proxy-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

Anti-Pattern

Außerirdische Spinnen

Ein Design, das den Namen nicht verdient. Sehr gesprächige, kommunikative Objekte, die sich alle gegenseitig kennen. Überhaupt keine Nutzung von Entwurfsmustern. Bei n Objekten gibt es n*(n-1)/2 Kommunikationspaare (englisch Alien spiders).

Gasfabrik

Eine Gasfabrik ist ein unnötig komplexes Systemdesign für eine recht einfache Aufgabenstellung. Beispielhafte Nutzung: „Ich wollte eine Softwarelösung haben, keine Gasfabrik.“ (englisch Gas factory)

Gottobjekt

Ein Objekt, das zu viel weiß respektive zu viel macht. Aufteilung nach Verantwortlichkeiten, Kapselung und Einhaltung von Entwurfsmustern helfen diesem Muster zu begegnen. Auch als Gottklasse (God class) und Blob bekannt (englisch God object).

Innere Plattform-Effekt

Ein System besitzt derartig weitreichende Konfigurationsmöglichkeiten, dass es letztlich zu einer schwachen Kopie der Plattform wird, mittels der es gebaut wurde. Ein Beispiel sind "flexible" Datenmodelle, die auf konkrete (anwendungsbezogene) Datenbanktabellen verzichten und statt dessen mittels allgemeiner Tabellen eine eigene Verwaltungsschicht für die Datenstruktur implementieren. Derartige Systeme sind typischerweise schwer zu beherrschen und leiden unter erheblichen Performanceproblemen. (englisch Inner platform effect)

Spaghetti-Code

Eine sehr kompakte Systemstruktur, deren Kontrollfluss einem Topf Spaghetti ähnelt.

Sumo-Hochzeit

Ein Fat Client ist unnatürlich stark abhängig von der Datenbank. In der Datenbank ist sehr viel Logik in Form der datenbankeigenen Programmiersprache positioniert, in Oracle zum Beispiel mit PL/SQL. Die ganze Architektur ist sehr unflexibel. Soll die Anwendung zu einer Internet-Anwendung migriert oder die Datenbank gewechselt werden, so müssen auf beiden Schichten (Client und Datenhaltung) viele Bereiche neu entwickelt werden. Die Systeme sind nicht entkoppelt (englisch Sumo Marriage).

Zwiebel

Neue Funktionalität wird um (oder über) die alte gelegt. Häufig zu beobachten, wenn ein Entwickler ein Programm erweitern soll, welches er nicht geschrieben hat. Der Entwickler möchte oder kann die bereits existente Lösung nicht komplett verstehen, und setzt seine neue Lösung einfach drüber. Dies führt mit einer Vielzahl von Versionen und unterschiedlichen Entwicklern über die Jahre zu einem Zwiebel-System (englisch Onion).

Programmierung mittels Copy & Paste

Der Programmierer entwickelt den Code nicht neu, sondern bedient sich bereits existenter Quelltexte, aus denen er Passagen herauskopiert. Die Gefahr ist sehr groß, dass er Fehler mitkopiert oder die Kopie für den neuen Bereich nicht optimal einsatzbereit ist. Der Entwickler reflektiert weniger über sein Programm, als wenn er jede Zeile selbst entwickeln würde. Fehleranfälliges Vorgehen, wenn der Entwickler nicht weiß, was er eigentlich macht. Weit verbreitet (englisch Copy And Paste Programming). Dieses Vorgehen ist auch unter dem Begriff „Verteile und beherrsche nicht“ (als zynische Anspielung auf das informatische Konzept des "divide and conquer" / divide et impera / Teile und herrsche) bekannt.

Lavafluss

Beschreibt den Umstand, dass in einer Anwendung immer mehr „toter Quelltext“ herumliegt. Dieser wird nicht mehr genutzt. Statt ihn zu löschen, werden im Programm immer mehr Verzweigungen eingebaut, die um den besagten Quelltext herumlaufen oder auf ihn aufbauen (englisch Lava flow).

Switch-Statement

Verhalten von Objekten gemäß ihrem Status (state) sollten mit Statusobjekten und dem state-Pattern gesteuert werden, nicht mit konditionalem Code.

Magic Values

Hierbei handelt es sich um Daten (Literale) mit besonderer Bedeutung. Sie sind hartkodiert (hardcoded) und nur mit besonderem Wissen über die konkrete Verwendung zu verstehen. Werte sollten zentral als Variable definiert werden, optimalerweise als typsicheres Objekt (typesafe).

Reservierte Wörter

Die Verwendung von reservierten Wörtern in SQL-Anweisungen kann zu schwer zu findenden Fehlern führen. Ein Austausch der Datenbank eines Herstellers gegen ein anderes Produkt kann dazu führen, dass weitere Namen als reserviert betrachtet werden müssen.

(Quelle: AntiPattern, 01.03.2008, 16:30 GMT+1)

 
 
March 02, 2008, at 10:26 PM by chueser -
Changed line 2069 from:

(Quelle: AntiPattern, 01.03.2008, 16:30 GMT+1)-]

to:

(Quelle: AntiPattern, 01.03.2008, 16:30 GMT+1)

 
 
March 02, 2008, at 09:30 PM by chueser -
Deleted line 23:
Changed lines 1540-1543 from:

Bereits in PHP 4 war es möglich, den foreach-Operator auf ein Objekt anzuwenden. In diesem Fall wurden die Instanzvariablen des Objektes wie ein assoziatives Array durchlaufen (Beispiel 3.4). Dies ist in PHP 5 weiterhin möglich, jedoch werden hierbei nur die öffentlichen Instanzvariablen berücksichtigt.

Beispiel: Verwendung von foreach() mit Objekten

to:

Bereits in PHP 4 war es möglich, den foreach-Operator auf ein Objekt anzuwenden. In diesem Fall wurden die Instanzvariablen des Objektes wie ein assoziatives Array durchlaufen (nächstes Beispiel). Dies ist in PHP 5 weiterhin möglich, jedoch werden hierbei nur die öffentlichen Instanzvariablen berücksichtigt.

Beispiel: Verwendung von foreach() mit Objekten

Changed lines 1560-1561 from:

An dieser Stelle wünschen wir uns ein Konzept, das die Vereinheitlichung von assoziativen und numerisch indizierten Arrays verallgemeinert und auf Objekte erweitert. Dieses Konzept finden wir im Entwurfsmuster (siehe Teil II. Entwurfsmuster in PHP anwenden) des Iterators, das wir im Folgenden diskutieren wollen.

to:

An dieser Stelle wünschen wir uns ein Konzept, das die Vereinheitlichung von assoziativen und numerisch indizierten Arrays verallgemeinert und auf Objekte erweitert. Dieses Konzept ist das Iterator Entwurfsmuster, das wir im Folgenden diskutieren wollen.

Changed lines 1569-1570 from:

PHP bietet mit den Schnittstellen Iterator und IteratorAggregate eine Integration des Iterator-Entwurfsmusters in die Programmiersprache selbst: Objekte, die die Schnittstelle Iterator anbieten, können mit dem foreach-Operator verwendet werden. Aufgrund dieser Integration wollen wir das Iterator-Entwurfsmuster im Rahmen der Sprachmerkmale diskutieren und nicht im eigentlichen Teil über Entwurfsmuster.

to:

PHP bietet mit den Schnittstellen Iterator und IteratorAggregate eine Integration des Iterator-Entwurfsmusters in die Programmiersprache selbst: Objekte, die die Schnittstelle Iterator anbieten, können mit dem foreach-Operator verwendet werden.

Changed lines 1573-1584 from:

Wendet man den foreach-Operator auf ein Objekt an, das die Schnittstelle Iterator anbietet, so bedient sich der foreach-Operator der folgenden Methoden, um an die Daten des Objektes zu gelangen:

rewind() setzt den Iterator zurück auf das erste Element der Menge.

valid() prüft, ob nach einem Aufruf von rewind() oder next() ein aktuelles Element existiert.

key() liefert den Schlüssel des aktuellen Elementes.

current() liefert den Wert des aktuellen Elementes.

next() setzt den Iterator auf das nächste Element der Menge.

to:

Wendet man den foreach-Operator auf ein Objekt an, das die Schnittstelle Iterator anbietet, so bedient sich der foreach-Operator der folgenden Methoden, um an die Daten des Objektes zu gelangen:

  • rewind() setzt den Iterator zurück auf das erste Element der Menge.
  • valid() prüft, ob nach einem Aufruf von rewind() oder next() ein aktuelles Element existiert.
  • key() liefert den Schlüssel des aktuellen Elementes.
  • current() liefert den Wert des aktuellen Elementes.
  • next() setzt den Iterator auf das nächste Element der Menge.
Changed lines 1653-1654 from:

Das nächste Beispiel zeigt eine Klasse String, die die Schnittstelle IteratorAggregate implementiert. Wird ein Objekt dieser Klasse zusammen mit dem foreach-Operator verwendet, so wird das String-Objekt unter Verwendung eines StringIterator-Objektes durchlaufen, ohne dass dieses von Hand erzeugt werden muss.

to:

Das nächste Beispiel zeigt eine Klasse String, die die Schnittstelle IteratorAggregate implementiert. Wird ein Objekt dieser Klasse zusammen mit dem foreach-Operator verwendet, so wird das String-Objekt unter Verwendung eines StringIterator-Objektes durchlaufen, ohne dass dieses von Hand erzeugt werden muss.

Changed lines 1692-1703 from:

FilterIterator, LimitIterator und SeekableIterator bieten Funktionen für das Filtern, Einschränken und Suchen von Elementen einer Menge. Sie bilden den Kern der Standard PHP Library.

ArrayObject und ArrayIterator bieten eine alternative Möglichkeit für die Arbeit mit Arrays an.

RecursiveIterator, RecursiveIteratorIterator und ParentIterator stellen die Funktionalität zur Verfügung, um rekursiv mit verschachtelten Iteratoren arbeiten zu können.

RecursiveIterator ist hierbei eine Schnittstelle, die von einer Iterator-Klasse implementiert werden muss, wenn ihre Objekte von einem RecursiveIteratorIterator rekursiv verarbeitet werden sollen. Ein Beispiel für eine Klasse, die RecursiveIterator implementiert, ist SimpleXMLIterator.

DirectoryIterator und RecursiveDirectoryIterator bieten eine einfache und effiziente Möglichkeit, über die Dateien und Unterverzeichnisse eines Verzeichnisses im Dateisystem zu iterieren.

CachingIterator und CachingRecursiveIterator erweitern das Standardverhalten von Iterator-Implementierungen, indem sie immer ein Element im Voraus lesen.

to:
  • FilterIterator, LimitIterator und SeekableIterator bieten Funktionen für das Filtern, Einschränken und Suchen von Elementen einer Menge. Sie bilden den Kern der Standard PHP Library.
  • ArrayObject und ArrayIterator bieten eine alternative Möglichkeit für die Arbeit mit Arrays an.
  • RecursiveIterator, RecursiveIteratorIterator und ParentIterator stellen die Funktionalität zur Verfügung, um rekursiv mit verschachtelten Iteratoren arbeiten zu können.
  • RecursiveIterator ist hierbei eine Schnittstelle, die von einer Iterator-Klasse implementiert werden muss, wenn ihre Objekte von einem RecursiveIteratorIterator rekursiv verarbeitet werden sollen. Ein Beispiel für eine Klasse, die RecursiveIterator implementiert, ist SimpleXMLIterator.
  • DirectoryIterator und RecursiveDirectoryIterator bieten eine einfache und effiziente Möglichkeit, über die Dateien und Unterverzeichnisse eines Verzeichnisses im Dateisystem zu iterieren.
  • CachingIterator und CachingRecursiveIterator erweitern das Standardverhalten von Iterator-Implementierungen, indem sie immer ein Element im Voraus lesen.
Changed lines 1718-1719 from:

Die Standard PHP Library bietet für dieses Problem die Möglichkeit an, Iterator-Objekte zu kombinieren. Hierbei kontrolliert ein äußerer Iterator einen inneren Iterator. Der innere Iterator arbeitet auf der eigentlichen Menge von Elementen, die verarbeitet werden soll. Der äußere Iterator entscheidet jedoch für jedes dieser Elemente, ob und in welcher Weise es an den Verwender zurückgegeben werden soll. Hinter diesem Konzept steht mit dem Dekorierer (siehe Kapitel 6) ein weiteres Entwurfsmuster, das wir später noch genauer behandeln werden.

to:

Die Standard PHP Library bietet für dieses Problem die Möglichkeit an, Iterator-Objekte zu kombinieren. Hierbei kontrolliert ein äußerer Iterator einen inneren Iterator. Der innere Iterator arbeitet auf der eigentlichen Menge von Elementen, die verarbeitet werden soll. Der äußere Iterator entscheidet jedoch für jedes dieser Elemente, ob und in welcher Weise es an den Verwender zurückgegeben werden soll. Hinter diesem Konzept steht mit dem Dekorierer ein weiteres Entwurfsmuster, das wir später noch genauer behandeln werden.

Changed lines 1722-1727 from:

Die abstrakte Klasse FilterIterator erlaubt das Schreiben einer Klasse für die Verwendung als äußeren Iterator. Dieser filtert die Elemente des inneren Iterators, der als Objekt dem Konstruktor zu übergeben ist. Die Filterkriterien sind hierbei in der Methode accept() zu implementieren. In dieser Methode kann über $this->getInnerIterator()->current() auf das aktuelle Element des inneren Iterators zugegriffen werden. Soll dieses akzeptiert (also an den Verwender des äußeren Iterators weitergereicht) werden, so muss die Methode TRUE als Ergebnis liefern.

Das nächste Beispiel zeigt eine von FilterIterator abgeleitete Klasse, die den aus dem letzten Beispiel bekannten Filter implementiert.

Beispiel: Eine FilterIterator-Implementierung

to:

Die abstrakte Klasse FilterIterator erlaubt das Schreiben einer Klasse für die Verwendung als äußeren Iterator. Dieser filtert die Elemente des inneren Iterators, der als Objekt dem Konstruktor zu übergeben ist. Die Filterkriterien sind hierbei in der Methode accept() zu implementieren. In dieser Methode kann über $this->getInnerIterator()->current() auf das aktuelle Element des inneren Iterators zugegriffen werden. Soll dieses akzeptiert (also an den Verwender des äußeren Iterators weitergereicht) werden, so muss die Methode TRUE als Ergebnis liefern.

Das nächste Beispiel zeigt eine von FilterIterator abgeleitete Klasse, die den aus dem letzten Beispiel bekannten Filter implementiert.

Beispiel: Eine FilterIterator-Implementierung

Changed lines 1767-1768 from:

Beispiel: Verwendung der Klasse LimitIterator

to:

Beispiel: Verwendung der Klasse LimitIterator

Changed lines 1784-1785 from:

Beispiel: Kombination von FilterIterator und LimitIterator

to:

Beispiel: Kombination von FilterIterator und LimitIterator

Changed lines 1800-1802 from:

Im Standardfall nutzt ein LimitIterator-Objekt wiederholte Aufrufe der Methode next(), die von der Schnittstelle Iterator vereinbart wird, um den durch die Parameter $offset und $count angegebenen Elementebereich zu erreichen. Bei der Verarbeitung der Ergebniszeilen einer Datenbankabfrage hat dieses Vorgehen jedoch einen Nachteil: Die vor dem gewünschten Bereich auftretenden Ergebniszeilen werden beispielsweise mit mysql_fetch_assoc() über die Verbindung zum Datenbankserver in den Speicher des PHP-Interpreters geladen, nur um gleich darauf unverarbeitet wieder überschrieben zu werden. Sinnvoller wäre an dieser Stelle die Verwendung der Funktion mysql_data_seek(), um direkt an die richtige Stelle der Ergebnismenge zu springen.

Für Mengen, bei denen die Position auf ein bestimmtes Element direkt gesetzt werden kann, bietet sich die Erweiterung der Klasse LimitIterator durch eine Kindklasse an, die zusätzlich die Schnittstelle SeekableIterator implementiert. Als einzige Methode dieser Schnittstelle ist seek($position) zu implementieren. Ein Objekt einer solchen Klasse kann nun effizient an die gewünschte Position springen.

to:

Im Standardfall nutzt ein LimitIterator-Objekt wiederholte Aufrufe der Methode next(), die von der Schnittstelle Iterator vereinbart wird, um den durch die Parameter $offset und $count angegebenen Elementebereich zu erreichen. Bei der Verarbeitung der Ergebniszeilen einer Datenbankabfrage hat dieses Vorgehen jedoch einen Nachteil: Die vor dem gewünschten Bereich auftretenden Ergebniszeilen werden beispielsweise mit mysql_fetch_assoc() über die Verbindung zum Datenbankserver in den Speicher des PHP-Interpreters geladen, nur um gleich darauf unverarbeitet wieder überschrieben zu werden. Sinnvoller wäre an dieser Stelle die Verwendung der Funktion mysql_data_seek(), um direkt an die richtige Stelle der Ergebnismenge zu springen.

Für Mengen, bei denen die Position auf ein bestimmtes Element direkt gesetzt werden kann, bietet sich die Erweiterung der Klasse LimitIterator durch eine Kindklasse an, die zusätzlich die Schnittstelle SeekableIterator implementiert. Als einzige Methode dieser Schnittstelle ist seek($position) zu implementieren. Ein Objekt einer solchen Klasse kann nun effizient an die gewünschte Position springen.

 
 
March 02, 2008, at 09:10 PM by chueser - + iterator
Deleted line 21:
Changed line 23 from:
to:
Deleted line 25:
Changed lines 1472-1479 from:

Das Iterator & Composite Muster

Das State-Muster

Das Proxy-Muster

to:

Das Iterator Muster

Changed lines 1476-1477 from:

Der Zugriff auf ein Objekt soll durch ein vorgelagertes Stellvertreterobjekt kontrolliert werden.

to:

Der generische Zugriff auf die Elemente einer Sammlung soll ermöglicht werden, ohne dass die Struktur der Sammlung oder die Implementierung der einzelnen Elemente bekannt sein muss.

Changed lines 1480-1493 from:

Funktionalität wie beispielsweise Zugriffskontrolle, die Verzögerung von Berechnungen, oder das Vorhalten von bereits berechneten Ergebnissen möchte man in einer eigenen Klasse modellieren. Ein Objekt dieser Klasse wird als Stellvertreter für ein anderes Objekt verwendet.

Lösung

Die beiden Klassen, von denen die Objekte der einen Objekte der anderen vertreten sollen, implementieren dieselbe Schnittstelle. Über den Konstruktor erhält das Stellvertreterobjekt eine Referenz auf das zu vertretende Objekt, um Methodenaufrufe gegebenfalls an dieses delegieren zu können.

Anwendungsbeispiele

Durch die Vorlagerung eines Stellvertreterobjektes kann bei einem Methodenaufruf entschieden werden, ob der unter Umständen teure Aufruf der Methode des eigentlichen Objektes überhaupt ausgeführt werden muss. Wird die Methode zum wiederholten Male mit demselben Parameter aufgerufen, und liefert die Methode für identische Parameter immer dasselbe Ergebnis, so kann die wiederholte Ausführung durch Speicherung des Ergebnisses vermieden werden.

Die beiden folgenden Beispiele zeigen eine Schnittstelle TeureBerechnung und mit Beispiel eine entsprechende Implementierung.

Beispiel: Die Schnittstelle TeureBerechnung

to:

Die Elemente unterschiedlicher Sammlungen, bei denen es sich beispielsweise um die Zeilen einer Textdatei, die Elemente eines XML-Dokumentes oder die Ergebniszeilen einer Datenbankabfrage handeln kann, sollen einheitlich verarbeitet werden können.

Lösung wird in den folgenden Abschnitten beschrieben.

(Quelle: Iterator-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

Einleitung

Das Aufzählen oder Durchlaufen der Elemente einer Menge ist ein wiederkehrendes Problem. Beispiele für solche Mengen sind Arrays, die Zeilen einer Textdatei, die Elemente eines XML-Dokumentes oder die Ergebniszeilen einer Datenbankabfrage.

In PHP 3 erfolgte das Durchlaufen eines numerisch indizierten Arrays mit Hilfe einer for()-Schleife. Hierbei musste der Programmierer auf die Indexgrenzen achten.

Beispiel: Iterieren von Arrays in PHP 3

Changed lines 1496-1497 from:

interface TeureBerechnung {

  public function rechne($a);
to:

$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

for ($i = 0; $i < 10; $i++) {

  print $a[$i] . ' ';
Changed lines 1503-1504 from:

Beispiel: Die Klasse Beispiel

to:

1 2 3 4 5 6 7 8 9 10

Assoziative Arrays mussten in PHP 3 mit reset(), while(), list() und each() verarbeitet werden.

Beispiel: Iterieren von assoziativen Arrays in PHP 3

Changed lines 1510-1513 from:

class Beispiel implements TeureBerechnung {

  public function rechne($a) {
    // …
  }
to:

$a = array('key' => 'value');

reset($a);

while (list($k, $v) = each($a)) {

  print $k . ': ' . $v;
Changed lines 1519-1522 from:

Die Klasse BeispielStellvertreter implementiert ebenfalls die Schnittstelle TeureBerechnung. Darüber hinaus bietet sie jedoch einen Caching-Mechanismus, der das Ergebnis der Methodenaufrufe von rechne($a) speichert und so die wiederholte Berechnung für gleiche Parameter $a verhindert.

Beispiel: Die Klasse BeispielStellvertreter

to:

key: value

In PHP 4 wurde mit foreach ein neuer Operator eingeführt, der die Arbeit mit assoziativen und numerisch indizierten Arrays vereinfachte und vereinheitlichte. Nun war es nicht mehr Aufgabe des Programmierers, sich um Dinge wie Indexgrenzen oder das Fortschreiten zum nächsten Element zu kümmern.

Beispiel: Iterieren von Arrays in PHP 4

Changed lines 1526-1528 from:

class BeispielStellvertreter implements TeureBerechnung {

  private $cache = array();
  private $objekt;
to:

$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); $b = array('key' => 'value');

Changed lines 1529-1531 from:
  public function __construct(TeureBerechnung $objekt) {
    $this->objekt = $objekt;
  }
to:

foreach ($a as $v) {

  print $v . ' ';

}

Changed lines 1533-1539 from:
  public function rechne($a) {
    if (!isset($this->cache[$a])) {
      $this->cache[$a] = $this->objekt->rechne($a);
    }

    return $this->cache[$a];
  }
to:

foreach ($b as $k => $v) {

  print $k . ': ' . $v;
Added lines 1538-2008:
1 2 3 4 5 6 7 8 9 10
key: value

Bereits in PHP 4 war es möglich, den foreach-Operator auf ein Objekt anzuwenden. In diesem Fall wurden die Instanzvariablen des Objektes wie ein assoziatives Array durchlaufen (Beispiel 3.4). Dies ist in PHP 5 weiterhin möglich, jedoch werden hierbei nur die öffentlichen Instanzvariablen berücksichtigt.

Beispiel: Verwendung von foreach() mit Objekten

(:source lang=php linenum:)
<?php
class Test {
  var $a = 1;
  var $b = 2;
}

$test = new Test;

foreach ($test as $name => $value) {
  print "$name: $value\n";
}
?>
a: 1
b: 2

An dieser Stelle wünschen wir uns ein Konzept, das die Vereinheitlichung von assoziativen und numerisch indizierten Arrays verallgemeinert und auf Objekte erweitert. Dieses Konzept finden wir im Entwurfsmuster (siehe Teil II. Entwurfsmuster in PHP anwenden) des Iterators, das wir im Folgenden diskutieren wollen.

Ein Iterator ermöglicht den generischen Zugriff auf die Elemente einer Menge, ohne dass die Struktur der Menge oder die Implementierung der einzelnen Elemente bekannt sein muss. Bei den Elementen einer solchen Menge kann es sich beispielsweise um die Zeilen einer Textdatei, die Elemente eines XML-Dokumentes oder die Ergebniszeilen einer Datenbankabfrage handeln.

(Quelle: Aufzählen in PHP 3 & 4, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

Die Iterator-Schnittstellen von PHP 5

PHP bietet mit den Schnittstellen Iterator und IteratorAggregate eine Integration des Iterator-Entwurfsmusters in die Programmiersprache selbst: Objekte, die die Schnittstelle Iterator anbieten, können mit dem foreach-Operator verwendet werden. Aufgrund dieser Integration wollen wir das Iterator-Entwurfsmuster im Rahmen der Sprachmerkmale diskutieren und nicht im eigentlichen Teil über Entwurfsmuster.

Abbildung: Die Schnittstellen Iterator, IteratorAggregate und Traversable

Wendet man den foreach-Operator auf ein Objekt an, das die Schnittstelle Iterator anbietet, so bedient sich der foreach-Operator der folgenden Methoden, um an die Daten des Objektes zu gelangen:

rewind() setzt den Iterator zurück auf das erste Element der Menge.

valid() prüft, ob nach einem Aufruf von rewind() oder next() ein aktuelles Element existiert.

key() liefert den Schlüssel des aktuellen Elementes.

current() liefert den Wert des aktuellen Elementes.

next() setzt den Iterator auf das nächste Element der Menge.

Das folgende Beispiel zeigt eine Implementierung der Schnittstelle Iterator, die über die (durch Leerzeichen getrennten) Teile eines Strings iteriert.

Beispiel: Eine Implementierung der Schnittstelle Iterator

(:source lang=php linenum:)
<?php
class StringIterator implements Iterator {
  private $string;
  private $position;

  public function __construct($string) {
    $this->string = explode(' ', $string);
  }

  public function rewind() {
    $this->position = 0;
  }

  public function valid() {
    return $this->position < sizeof($this->string);
  }

  public function key() {
    return $this->position;
  }

  public function current() {
    return $this->string[$this->position];
  }

  public function next() {
    $this->position++;
  }
}
?>

Beispiel: Implizite Verwendung von Iteratoren mit dem foreach-Operator

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

$iterator = new StringIterator('Dies ist ein String.');

foreach ($iterator as $key => $value) {
  print $value . ' ';
}
?>

Dies ist ein String.

Das Iterieren eines Objektes, dessen Klasse die Schnittstelle Iterator implementiert, kann neben der impliziten Verwendung durch die Benutzung des foreach-Operators (s. letztes Beispiel) auch explizit durch die Verwendung der in der Schnittstelle vereinbarten Methoden erfolgen (nächstes Beispiel). Hierbei wird deutlich, dass die Methoden der Schnittstelle Iterator den aus PHP 3 bekannten Funktionen für das Durchlaufen von assoziativen Arrays entsprechen.

Beispiel: Explizite Verwendung von Iteratoren

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

$iterator = new StringIterator('Dies ist ein String.');

for ($iterator->rewind();
     $iterator->valid();
     $iterator->next()) {
  $key   = $iterator->key();
  $value = $iterator->current();

  print $value . ' ';
}
?>

Dies ist ein String.

Bislang müssen wir für eine Klasse, für die eine entsprechende Iterator-Klasse zur Verfügung steht, "von Hand" ein Iterator-Objekt erzeugen. Hier hilft die Implementierung der Schnittstelle IteratorAggregate durch die Klasse. Deren Methode getIterator liefert ein Objekt der entsprechenden Iterator-Klasse. Verwendet man ein Objekt, das die Schnittstelle IteratorAggregate anbietet, mit dem foreach-Operator, so ruft der PHP-Interpreter automatisch getIterator auf und verwendet das zurückgegebene Iterator-Objekt.

Das nächste Beispiel zeigt eine Klasse String, die die Schnittstelle IteratorAggregate implementiert. Wird ein Objekt dieser Klasse zusammen mit dem foreach-Operator verwendet, so wird das String-Objekt unter Verwendung eines StringIterator-Objektes durchlaufen, ohne dass dieses von Hand erzeugt werden muss.

Beispiel: Eine Implementierung der Schnittstelle IteratorAggregate

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

class String implements IteratorAggregate {
  private $string;

  public function __construct($string = '') {
    $this->string = $string;
  }

  public function getIterator() {
    return new StringIterator($this->string);
  }

  // ...
}

$string = new String('Dies ist ein String.');

foreach ($string as $key => $value) {
  print $value . ' ';
}
?>

Dies ist ein String.

(Quelle: Iterator-Schnittstelle von PHP 5, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

Die Standard PHP Library (SPL)

Die Standard PHP Library (SPL) ist fester Bestandteil von PHP 5 und baut auf den Iterator-Schnittstellen auf. Sie tritt an, ein PHP-Pendant zu der von C++ bekannten Standard Template Library (STL) zu werden.

In PHP 5.0 umfasst die Standard PHP Library die folgenden Klassen und Schnittstellen:

FilterIterator, LimitIterator und SeekableIterator bieten Funktionen für das Filtern, Einschränken und Suchen von Elementen einer Menge. Sie bilden den Kern der Standard PHP Library.

ArrayObject und ArrayIterator bieten eine alternative Möglichkeit für die Arbeit mit Arrays an.

RecursiveIterator, RecursiveIteratorIterator und ParentIterator stellen die Funktionalität zur Verfügung, um rekursiv mit verschachtelten Iteratoren arbeiten zu können.

RecursiveIterator ist hierbei eine Schnittstelle, die von einer Iterator-Klasse implementiert werden muss, wenn ihre Objekte von einem RecursiveIteratorIterator rekursiv verarbeitet werden sollen. Ein Beispiel für eine Klasse, die RecursiveIterator implementiert, ist SimpleXMLIterator.

DirectoryIterator und RecursiveDirectoryIterator bieten eine einfache und effiziente Möglichkeit, über die Dateien und Unterverzeichnisse eines Verzeichnisses im Dateisystem zu iterieren.

CachingIterator und CachingRecursiveIterator erweitern das Standardverhalten von Iterator-Implementierungen, indem sie immer ein Element im Voraus lesen.

Betrachten wir einmal die Aufgabe, die Elemente einer Menge gefiltert zu verarbeiten. Mit Hilfe eines Iterators sind wir bereits in der Lage, die Elemente einer beliebigen Menge zu durchlaufen. Eine einfache Möglichkeit, aus einer Menge von Teilstrings nur diejenigen auszugeben, die mit "Bar" beginnen, sehen wir im folgenden Beispiel.

Beispiel: Filtern einer Menge von Teilstrings

(:source lang=php linenum:)
<?php
$string = new String('Foo Bar Barbara');

foreach ($string as $key => $value) {
  if (strpos($value, 'Bar') === 0) {
    print $value . "\n";
  }
}
?>
Bar
Barbara

Im letzten Beispiel wenden wir die Filterregel direkt in der Schleife an, mit der wir die Elemente der Menge durchlaufen. Dies wird jedoch zum einen bei komplexeren Filteroperationen schnell unübersichtlich, zum anderen stößt diese Methode an Grenzen, wenn es darum geht, Filterregeln dynamisch auszutauschen oder zu kombinieren.

Die Standard PHP Library bietet für dieses Problem die Möglichkeit an, Iterator-Objekte zu kombinieren. Hierbei kontrolliert ein äußerer Iterator einen inneren Iterator. Der innere Iterator arbeitet auf der eigentlichen Menge von Elementen, die verarbeitet werden soll. Der äußere Iterator entscheidet jedoch für jedes dieser Elemente, ob und in welcher Weise es an den Verwender zurückgegeben werden soll. Hinter diesem Konzept steht mit dem Dekorierer (siehe Kapitel 6) ein weiteres Entwurfsmuster, das wir später noch genauer behandeln werden.

Abbildung: FilterIterator, LimitIterator und SeekableIterator

Die abstrakte Klasse FilterIterator erlaubt das Schreiben einer Klasse für die Verwendung als äußeren Iterator. Dieser filtert die Elemente des inneren Iterators, der als Objekt dem Konstruktor zu übergeben ist. Die Filterkriterien sind hierbei in der Methode accept() zu implementieren. In dieser Methode kann über $this->getInnerIterator()->current() auf das aktuelle Element des inneren Iterators zugegriffen werden. Soll dieses akzeptiert (also an den Verwender des äußeren Iterators weitergereicht) werden, so muss die Methode TRUE als Ergebnis liefern.

Das nächste Beispiel zeigt eine von FilterIterator abgeleitete Klasse, die den aus dem letzten Beispiel bekannten Filter implementiert.

Beispiel: Eine FilterIterator-Implementierung

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

class StringFilterIterator extends FilterIterator {
  private $prefix;

  public function
  __construct(StringIterator $stringIterator, $prefix) {
    parent::__construct($stringIterator);
    $this->prefix = $prefix;
  }

  public function accept() {
    $current = $this->getInnerIterator()->current();

    if (strpos($current, $this->prefix) === 0) {
      return TRUE;
    }

    return FALSE;
  }
}

$stringIterator = new StringIterator('Foo Bar Barbara');
$filterIterator = new StringFilterIterator(
  $stringIterator,
  'Bar'
);

foreach ($filterIterator as $key => $value) {
    print "$value\n";
}
?>
Bar
Barbara

Ein Objekt der Klasse LimitIterator erlaubt das Limitieren der Elemente des inneren Iterators, für den es als äußerer Iterator in Aktion tritt. Der Konstruktor erwartet neben dem inneren Iterator zwei optionale Parameter $offset und $count. Der Erste gibt die Nummer des ersten Elements an (beginnend bei 0), das akzeptiert werden soll. Der Zweite die maximale Anzahl an Elementen. Im folgenden Beispiel werden so alle Elemente ab einschließlich des Dritten ausgegeben.

Beispiel: Verwendung der Klasse LimitIterator

(:source lang=php linenum:)
<?php
require_once 'StringIterator.php';

$stringIterator = new StringIterator('Foo Bar Barbara');
$limitIterator  = new LimitIterator($stringIterator, 2);

foreach ($limitIterator as $key => $value) {
  print "$value\n";
}
?>

Barbara

Der äußere Iterator eines inneren Iterators kann seinerseits als innerer Iterator für einen weiteren Iterator dienen. Im nächsten Beispiel ist der StringFilterIterator einerseits äußerer Iterator für den StringIterator, andererseits aber auch innerer Iterator für den LimitIterator.

Beispiel: Kombination von FilterIterator und LimitIterator

(:source lang=php linenum:)
<?php
require_once 'StringFilterIterator.php';

$stringIterator = new StringIterator('Foo Bar Barbara');
$filterIterator = new StringFilterIterator($stringIterator, 'Bar');
$limitIterator  = new LimitIterator($filterIterator, 1, 1);

foreach ($limitIterator as $key => $value) {
  print $value . "\n";
}
?>@

[@Barbara

Im Standardfall nutzt ein LimitIterator-Objekt wiederholte Aufrufe der Methode next(), die von der Schnittstelle Iterator vereinbart wird, um den durch die Parameter $offset und $count angegebenen Elementebereich zu erreichen. Bei der Verarbeitung der Ergebniszeilen einer Datenbankabfrage hat dieses Vorgehen jedoch einen Nachteil: Die vor dem gewünschten Bereich auftretenden Ergebniszeilen werden beispielsweise mit mysql_fetch_assoc() über die Verbindung zum Datenbankserver in den Speicher des PHP-Interpreters geladen, nur um gleich darauf unverarbeitet wieder überschrieben zu werden. Sinnvoller wäre an dieser Stelle die Verwendung der Funktion mysql_data_seek(), um direkt an die richtige Stelle der Ergebnismenge zu springen.

Für Mengen, bei denen die Position auf ein bestimmtes Element direkt gesetzt werden kann, bietet sich die Erweiterung der Klasse LimitIterator durch eine Kindklasse an, die zusätzlich die Schnittstelle SeekableIterator implementiert. Als einzige Methode dieser Schnittstelle ist seek($position) zu implementieren. Ein Objekt einer solchen Klasse kann nun effizient an die gewünschte Position springen.

(Quelle: Standard PHP Library, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

Die Schnittstelle ArrayAccess

Objekte von Klassen, die die Schnittstelle ArrayAccess (s. Abbildung) implementieren, können wie normale PHP-Arrays verwendet werden. Der Unterschied zu diesen ist jedoch, dass der Programmierer festlegen kann, was beispielsweise bei der Ausführung von $array[$offset] = $value passieren soll.

Abbildung: Die Schnittstelle ArrayAccess

Die vier zu implementierenden Methoden sind:

  • offsetExists($offset) wird für isset($array[$offset]) aufgerufen.
  • offsetGet($offset) wird für $value = $array[$offset] aufgerufen.
  • offsetSet($offset, $value) wird für $array[$offset] = $value aufgerufen. Hierbei ist zu beachten, dass bei Verwendung der $array[] = $value Syntax $offset den Wert NULL enthält.
  • offsetUnset($offset) wird für unset($array[$offset]) aufgerufen.

Das nächste Beispiel zeigt eine Implementierung der normalen PHP-Array-Datenstruktur als Klasse unter Verwendung der Schnittstelle ArrayAccess.

Beispiel: Eine Implementierung der ArrayAccess-Schnittstelle

(:source lang=php linenum:)
<?php
class PHPArray implements ArrayAccess {
  private $array = array();

  public function offsetExists($offset) {
    return array_key_exists($this->array, $offset);
  }

  public function offsetGet($offset) {
    return $this->array[$offset];
  }

  public function offsetSet($offset, $value) {
    if (is_null($offset)) {
      $this->array[] = $value;
    } else {
      $this->array[$offset] = $value;
    }
  }

  public function offsetUnset($offset) {
    unset($this->array[$offset]);
  }
}

$test = new PHPArray;

$test[] = 'Hello'; $test[] = ' World!';
print $test[0] . $test[1];
?>

Hello World!

Nachdem wir uns mit der grundsätzlichen Verwendung der Schnittstelle ArrayAccess vertraut gemacht haben, kommen wir zu einer möglichen Verwendung.

Im folgenden Beispiel benutzen wir eine Implementierung der Schnittstelle ArrayAccess, um ein Array $_SHARED zu erzeugen, auf dessen Inhalt von unterschiedlichen PHP-Instanzen aus zugegriffen werden kann. Für die notwendige Datenhaltung benutzen wir die in PHP 5 eingebettete Datenbank SQLite. Mit Hilfe der ArrayAccess-Implementierung "verbergen" wir die Datenhaltung vor dem Verwender des Arrays.

Beispiel: Die Klasse SharedArray

(:source lang=php linenum:)
<?php
class SharedArray implements ArrayAccess {
  private $db = NULL;

  public function __construct() {
    if ($this->db === NULL &&
        $this->db = sqlite_open('shared_array.db')) {
      @sqlite_query(
        $this->db,
        'CREATE TABLE shared_array
         (offset varchar(32) PRIMARY KEY,
          value  varchar(32));'
      );
    }
  }

  public function __destruct() {
    if ($this->db !== NULL) {
      sqlite_close($this->db);
    }

    $this->db = NULL;
  }

  public function offsetExists($offset) {
    $result = sqlite_query(
      $this->db,
      "SELECT offset
         FROM shared_array
        WHERE offset = '$offset';"
    );

    if ($result === FALSE) {
      return FALSE;
    } else {
      return TRUE;
    }
  }

  public function offsetGet($offset) {
    return sqlite_fetch_single(
      sqlite_query(
        $this->db,
        "SELECT value
           FROM shared_array
          WHERE offset = '$offset';"
      )
    );
  }

  public function offsetSet($offset, $value) {
    sqlite_query(
      $this->db,
      "REPLACE INTO shared_array
               (offset, value)
        VALUES ('$offset', '$value');"
    );
  }

  public function offsetUnset($offset) {
    sqlite_query(
      $this->db,
      "DELETE FROM shared_array
        WHERE offset = '$offset'"
    );
  }
}

$_SHARED = new SharedArray;
?>

(Quelle: ArrayAccess Schnittstelle, Sebastian Bergmann, 02.03.2008, 21:00 GMT+1)

Das Proxy-Muster

Problem

Der Zugriff auf ein Objekt soll durch ein vorgelagertes Stellvertreterobjekt kontrolliert werden.

Motivation

Funktionalität wie beispielsweise Zugriffskontrolle, die Verzögerung von Berechnungen, oder das Vorhalten von bereits berechneten Ergebnissen möchte man in einer eigenen Klasse modellieren. Ein Objekt dieser Klasse wird als Stellvertreter für ein anderes Objekt verwendet.

Lösung

Die beiden Klassen, von denen die Objekte der einen Objekte der anderen vertreten sollen, implementieren dieselbe Schnittstelle. Über den Konstruktor erhält das Stellvertreterobjekt eine Referenz auf das zu vertretende Objekt, um Methodenaufrufe gegebenfalls an dieses delegieren zu können.

Anwendungsbeispiele

Durch die Vorlagerung eines Stellvertreterobjektes kann bei einem Methodenaufruf entschieden werden, ob der unter Umständen teure Aufruf der Methode des eigentlichen Objektes überhaupt ausgeführt werden muss. Wird die Methode zum wiederholten Male mit demselben Parameter aufgerufen, und liefert die Methode für identische Parameter immer dasselbe Ergebnis, so kann die wiederholte Ausführung durch Speicherung des Ergebnisses vermieden werden.

Die beiden folgenden Beispiele zeigen eine Schnittstelle TeureBerechnung und mit Beispiel eine entsprechende Implementierung.

Beispiel: Die Schnittstelle TeureBerechnung

(:source lang=php linenum:)
<?php
interface TeureBerechnung {
  public function rechne($a);
}
?>

Beispiel: Die Klasse Beispiel

(:source lang=php linenum:)
<?php
class Beispiel implements TeureBerechnung {
  public function rechne($a) {
    // ...
  }
}
?>

Die Klasse BeispielStellvertreter implementiert ebenfalls die Schnittstelle TeureBerechnung. Darüber hinaus bietet sie jedoch einen Caching-Mechanismus, der das Ergebnis der Methodenaufrufe von rechne($a) speichert und so die wiederholte Berechnung für gleiche Parameter $a verhindert.

Beispiel: Die Klasse BeispielStellvertreter

(:source lang=php linenum:)
<?php
class BeispielStellvertreter implements TeureBerechnung {
  private $cache = array();
  private $objekt;

  public function __construct(TeureBerechnung $objekt) {
    $this->objekt = $objekt;
  }

  public function rechne($a) {
    if (!isset($this->cache[$a])) {
      $this->cache[$a] = $this->objekt->rechne($a);
    }

    return $this->cache[$a];
  }
}
?>
Deleted lines 2014-2016:

zusammengesetzte Muster

 
 
March 02, 2008, at 08:40 PM by chueser - + template
Changed line 23 from:
to:
Changed lines 1333-1375 from:

Das Template Methode-Muster

Das Iterator & Composite Muster

Das State-Muster

Das Proxy-Muster

Problem

Der Zugriff auf ein Objekt soll durch ein vorgelagertes Stellvertreterobjekt kontrolliert werden.

Motivation

Funktionalität wie beispielsweise Zugriffskontrolle, die Verzögerung von Berechnungen, oder das Vorhalten von bereits berechneten Ergebnissen möchte man in einer eigenen Klasse modellieren. Ein Objekt dieser Klasse wird als Stellvertreter für ein anderes Objekt verwendet.

Lösung

Die beiden Klassen, von denen die Objekte der einen Objekte der anderen vertreten sollen, implementieren dieselbe Schnittstelle. Über den Konstruktor erhält das Stellvertreterobjekt eine Referenz auf das zu vertretende Objekt, um Methodenaufrufe gegebenfalls an dieses delegieren zu können.

Anwendungsbeispiele

Durch die Vorlagerung eines Stellvertreterobjektes kann bei einem Methodenaufruf entschieden werden, ob der unter Umständen teure Aufruf der Methode des eigentlichen Objektes überhaupt ausgeführt werden muss. Wird die Methode zum wiederholten Male mit demselben Parameter aufgerufen, und liefert die Methode für identische Parameter immer dasselbe Ergebnis, so kann die wiederholte Ausführung durch Speicherung des Ergebnisses vermieden werden.

Die beiden folgenden Beispiele zeigen eine Schnittstelle TeureBerechnung und mit Beispiel eine entsprechende Implementierung.

Beispiel: Die Schnittstelle TeureBerechnung

(:source lang=php linenum:)
<?php
interface TeureBerechnung {
  public function rechne($a);
}
?>

Beispiel: Die Klasse Beispiel

(:source lang=php linenum:)[@<?php class Beispiel implements TeureBerechnung {

  public function rechne($a) {
    // …
to:

Das Template Method-Muster

Problem

Unterklassen soll es ermöglicht werden, bestimmte Schritte einer Operation zu überschreiben, ohne deren Struktur zu verändern.

Motivation

Lässt sich eine Aufgabe in Einzeloperationen zerlegen, von denen eine oder mehrere unterschiedlich implementiert werden können, so bietet sich das Zusammenfassen der gemeinsamen Operationen in einer Basisklasse an. Die Unterklassen dieser Klassen implementieren ihrerseits nur die Einzelschritte, in denen sie sich voneinander unterscheiden.

Lösung

In der Basisklasse wird das Grundgerüst der Operation in einer als public final deklarierten Methode implementiert.

Muss ein Teilschritt, dessen Implementierung an eine Unterklasse delegiert werden soll, implementiert werden, so wird die entsprechende Methode in der Basisklasse als abstract protected deklariert. Soll die Implementierung hingegen optional sein, so wird die Methode lediglich als protected deklariert und verfügt in der Basisklasse nur über einen leeren Methodenrumpf.

Anwendungsbeispiele

Eine Schablonenmethode wird gerne eingesetzt, um eine Ausgabe in unterschiedlichen Formaten darstellen zu können. Die Klasse PHPUnit2_Extensions_CodeCoverage_Renderer des PHPUnit-Paketes kombiniert das Entwurfsmuster der abstrakten Fabrik (siehe „Abstrakte Fabrik“) mit einer Schablonenmethode und ermöglicht so die Darstellung von Code-Coverage-Informationen in unterschiedlichen Formaten durch entsprechende Unterklassen. Die Methode render() stellt hierbei die Schablone dar und ruft die den Einzelschritten entsprechenden Methoden in der vorgegebenen Reihenfolge auf.

Beispiel: Die abstrakte Klasse PHPUnit2_Extensions_CodeCoverage_Renderer

(:source lang=php linenum:)
<?php
abstract class PHPUnit2_Extensions_CodeCoverage_Renderer {
  protected $codeCoverageInformation;

  protected function __construct($codeCoverageInformation) {
    $this->codeCoverageInformation = $codeCoverageInformation;
  }

  public function factory($type, $codeCoverageInformation) {
    $class = 'PHPUnit2_Extensions_CodeCoverage_Renderer_' .
             $type;

    $source = 'PHPUnit2/Extensions/CodeCoverage/Renderer/' .
              $type . '.php';

    if (@require_once($source)) {
        $object = new $class($codeCoverageInformation);

      return $object;
    } else {
      throw new Exception(
        sprintf(
          'Could not load class %s.',
          $class
        )
      );
    }
  }

  public final function render() {
    $buffer = '';

    foreach ($this->codeCoverageInformation as
             $testCaseName => $sourceFiles) {
      $buffer .= $this->startTestCase($testCaseName);

      foreach ($sourceFiles as
               $sourceFile => $executedLines) {
        $buffer .= $this->startSourceFile($sourceFile);

        $buffer .= $this->renderSourceFile(
          file($sourceFile),
          $executedLines
        );

        $buffer .= $this->endSourceFile($sourceFile);
      }

      $buffer .= $this->endTestCase($testCaseName);
    }

    return $buffer;
  }

  protected function startTestCase($testCaseName) {
  }

  protected function endTestCase($testCaseName) {
  }

  protected function startSourceFile($sourceFile) {
  }

  protected function endSourceFile($sourceFile) {
  }

  abstract protected function
  renderSourceFile($codeLines, $executedLines);
}
?>

Beispiel 7.9: Die Klasse PHPUnit2_Extensions_CodeCoverage_Renderer_Text

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Util/CodeCoverage/Renderer.php';

class PHPUnit2_Extensions_CodeCoverage_Renderer_Text
extends PHPUnit2_Extensions_CodeCoverage_Renderer {
  protected function startTestCase($testCaseName) {
    return $testCaseName . "\n\n";
  }

  protected function endTestCase($testCaseName) {
    return "\n";
  }

  protected function startSourceFile($sourceFile) {
    return '  ' . $sourceFile . "\n\n";
  }

  protected function endSourceFile($sourceFile) {
    return "\n";
  }

  protected function renderSourceFile($codeLines, $executedLines) {
    $buffer = '';
    $line   = 1;

    foreach ($codeLines as $codeLine) {
      $buffer .= sprintf(
        '    %4u|%4s| %s',

        $line,
        (isset($executedLines[$line])) ? $executedLines[$line] . 'x' : '',
        $codeLine
      );

      $line++;
    }

    return $buffer;
  }
}
?>

(Quelle: Template Method-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

 
 
March 02, 2008, at 08:31 PM by chueser - + proxy
Added lines 1343-1408:

Problem

Der Zugriff auf ein Objekt soll durch ein vorgelagertes Stellvertreterobjekt kontrolliert werden.

Motivation

Funktionalität wie beispielsweise Zugriffskontrolle, die Verzögerung von Berechnungen, oder das Vorhalten von bereits berechneten Ergebnissen möchte man in einer eigenen Klasse modellieren. Ein Objekt dieser Klasse wird als Stellvertreter für ein anderes Objekt verwendet.

Lösung

Die beiden Klassen, von denen die Objekte der einen Objekte der anderen vertreten sollen, implementieren dieselbe Schnittstelle. Über den Konstruktor erhält das Stellvertreterobjekt eine Referenz auf das zu vertretende Objekt, um Methodenaufrufe gegebenfalls an dieses delegieren zu können.

Anwendungsbeispiele

Durch die Vorlagerung eines Stellvertreterobjektes kann bei einem Methodenaufruf entschieden werden, ob der unter Umständen teure Aufruf der Methode des eigentlichen Objektes überhaupt ausgeführt werden muss. Wird die Methode zum wiederholten Male mit demselben Parameter aufgerufen, und liefert die Methode für identische Parameter immer dasselbe Ergebnis, so kann die wiederholte Ausführung durch Speicherung des Ergebnisses vermieden werden.

Die beiden folgenden Beispiele zeigen eine Schnittstelle TeureBerechnung und mit Beispiel eine entsprechende Implementierung.

Beispiel: Die Schnittstelle TeureBerechnung

(:source lang=php linenum:)
<?php
interface TeureBerechnung {
  public function rechne($a);
}
?>

Beispiel: Die Klasse Beispiel

(:source lang=php linenum:)
<?php
class Beispiel implements TeureBerechnung {
  public function rechne($a) {
    // ...
  }
}
?>

Die Klasse BeispielStellvertreter implementiert ebenfalls die Schnittstelle TeureBerechnung. Darüber hinaus bietet sie jedoch einen Caching-Mechanismus, der das Ergebnis der Methodenaufrufe von rechne($a) speichert und so die wiederholte Berechnung für gleiche Parameter $a verhindert.

Beispiel: Die Klasse BeispielStellvertreter

(:source lang=php linenum:)
<?php
class BeispielStellvertreter implements TeureBerechnung {
  private $cache = array();
  private $objekt;

  public function __construct(TeureBerechnung $objekt) {
    $this->objekt = $objekt;
  }

  public function rechne($a) {
    if (!isset($this->cache[$a])) {
      $this->cache[$a] = $this->objekt->rechne($a);
    }

    return $this->cache[$a];
  }
}
?>

Das Stellvertreter-Muster spielt in der verteilten Programmierung ebenfalls eine Rolle. So werden beispielsweise die über einen Webdienst angebotenen Methoden auf der Client-Seite von einem Stellvertreter-Objekt repräsentiert.

In ihrer Implementierung ähneln sich die Entwurfsmuster Dekorierer und Stellvertreter. Der Unterschied zwischen diesen beiden Mustern liegt im Ziel, das sie verfolgen. Ein Dekorierer wird genutzt, um die Funktionalität eines bestehenden Objektes zu erweitern oder zu verändern, während ein Stellvertreter den Zugriff auf das bestehende Objekt kontrolliert.

(Quelle: Proxy-Muster, Sebastian Bergmann, 02.03.2008, 20:00 GMT+1)

 
 
March 02, 2008, at 07:45 PM by chueser -
Added lines 1169-1223:

Problem

Die Anzahl der Objekte einer Klasse soll beschränkt werden.

Motivation

Oft ist nur ein Objekt oder ein Pool mit einer festen Anzahl von Objekten einer Klasse sinnvoll, beispielsweise bei der Kapselung von externen Ressourcen.

Lösung

Die Erzeugung von Objekten durch den new-Operator wird durch Deklaration des Konstruktors als protected oder private unterbunden. Das Klonen des Objektes wird durch die Deklaration der Methode __clone() als private final unterbunden.

Für die Objekterzeugung wird eine statische Methode, meist getInstance() oder singleton() genannt, bereitgestellt. In dieser Methode kann nun entschieden werden, ob ein neues Objekt der Klasse erzeugt wird oder ob eine Referenz auf ein bereits erzeugtes Objekt zurückgegeben werden soll.

Abbildung: Struktur des Singleton-Patterns

Beispiel: Singleton

(:source lang=php linenum:)
<?php
class Singleton {
  private static $uniqueInstance = NULL;

  protected function __construct() {
    print "Neues Objekt wird erzeugt.\n";
  }

  private final function __clone() {}

  public static function getInstance() {
    if (self::$uniqueInstance === NULL) {
      self::$uniqueInstance = new Singleton;
    }

    return self::$uniqueInstance;
  }
}

$a = Singleton::getInstance();
$b = Singleton::getInstance();

if ($a === $b) {
    print '$a und $b referenzieren dasselbe Objekt.'."\n";
}
?>
Neues Objekt wird erzeugt.
$a und $b referenzieren dasselbe Objekt.

Anwendungsbeispiele

Klassen, die externe Ressourcen wie beispielsweise Datenbankverbindungen kapseln, sind in der Regel gute Kandidaten für die Verwendung des Singleton-Musters. Mit seiner Methode getInstance() bietet dieses einen globalen Zugriffspunkt auf das (einzige) Objekt einer Klasse. Ohne diese Möglichkeit müsste beispielsweise in jeder Methode einer Anwendung, in der auf die Datenbank zugegriffen wird, ein neues Objekt der entsprechenden Klasse erzeugt werden oder aber ein solches Objekt als Parameter übergeben werden. Beide Alternativen zur Anwendung des Singleton-Musters sind "unschön", Erstere unter dem Aspekt der Performanz sogar ein großer Fehler.

(Quelle: Singleton-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

 
 
March 02, 2008, at 07:39 PM by chueser -
Changed lines 978-980 from:
to:

(Quelle: Decorator-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

Added lines 1163-1164:

(Quelle: Factory-Muster, Sebastian Bergmann, 02.03.2008, 19:30 GMT+1)

 
 
March 02, 2008, at 07:31 PM by chueser - + factory
Changed lines 650-651 from:

Abbildung: Struktur des Beobachter-Musters

to:

Abbildung: Struktur des Beobachter-Musters

Changed lines 984-985 from:

Beispiel: Factory Method

to:

Beispiel: Factory Method

Added lines 1060-1160:

Abstract Factory

Problem

Objekte verwandter Klassen sollen erzeugt werden, so dass die zu verwendende Klasse erst zur Laufzeit festgelegt werden kann.

Motivation

Eine Aufgabe kann auf unterschiedliche Arten, beispielweise unter Verwendung verschiedener Protokolle oder Verfahren, durchgeführt werden. Die einzelnen Implementierungen sollen zur Laufzeit des Programms austauschbar sein.

Lösung

Die gemeinsame Funktionalität der einzelnen Implementierungen wird in einer abstrakten Basisklasse gekapselt. Die direkte Erzeugung von Objekten der Kindklassen dieser Basisklasse wird durch die Deklaration der entsprechenden Konstruktoren als protected oder private unterbunden. Für die Erzeugung von Objekten bietet die abstrakte Basisklasse eine statische Methode an, die anhand des übergebenen Parameters ein Objekt des gewünschten Typs erzeugt und zur Verfügung stellt.

Anwendungsbeispiele

Betrachten wir als Beispiel einen Online-Shop, der seinen Partnern ein Interface zum Produktkatalog zur Verfügung stellt. Der eine Partner wünscht eine Schnittstelle auf XML-RPC-Basis, ein weiterer würde gerne SOAP nutzen können, während ein dritter Geschäftspartner ein naives Protokoll auf der Basis von HTTP GET und POST verlangt.

Die Programmlogik ist bei den drei Varianten gleich, sie wird also zunächst in einer Basisklasse gekapselt. Von dieser Basisklasse leiten sich drei Klassen, je eine für XML-RPC, SOAP und HTTP GET / POST ab. Diese abgeleiteten Klassen implementieren den jeweiligen Kommunikationsmechanismus.

Bislang haben wir nur mit dem Konzept der Vererbung Coderedundanzen vermieden. Allerdings ist das Anlegen von Instanzen der drei Klassen aus der Applikation heraus noch nicht transparent, die Nutzung komplizierter als nötig, wie das folgende Beispiel zeigt.

Beispiel: Verwendung der drei Klassen ohne abstrakte Fabrik

(:source lang=php linenum:)
<?php
switch ($type) {
  case 'HTTP': {
    include_once 'partner_interface/http.php';
    $interface = new PartnerInterface_HTTP;
  }
  break;

  case 'SOAP': {
    include_once 'partner_interface/soap.php';
    $interface = new PartnerInterface_SOAP;
  }
  break;

  case 'XML-RPC': {
    include_once 'partner_interface/xml-rpc.php';
    $interface = new PartnerInterface_XMLRPC;
  }
  break;
}
?>

Nachdem die Auswahl des zu erzeugenden Objektes in der Methode factory() der Klasse PartnerInterface an zentraler Stelle gekapselt wurde, gestaltet sich die Erzeugung des passenden Objektes im Kontext des Programmes einfach und flexibel, wie das folgende Beispiel zeigt.

Beispiel: Verwendung der drei Klassen mit abstrakter Fabrik

(:source lang=php linenum:)
<?php
$interface = PartnerInterface::factory($type);
?>

Das nächste Beispiel zeigt die Implementierung einer abstrakten Fabrik in PHP, das darauf folgende Beispiel das Grundgerüst einer konkreten Kindklasse, für die die Fabrik Objekte erzeugen kann.

Beispiel: Die abstrakte Klasse PartnerInterface

(:source lang=php linenum:)
<?php
abstract class PartnerInterface {
  protected function __construct() {}

  public static function factory($type) {
    $source = 'PartnerInterface/' . $type . '.php';

    if (@require_once($source)) {
      $class  = 'PartnerInterface_' . $type;
      $object = new $class;

      return $object;
    } else {
      throw new Exception(
        sprintf(
          'Konnte kein Objekt vom Typ %s erzeugen.',
          'PartnerInterface_' . $type
        )
      );
    }
  }

  public abstract function import($data);
  public abstract function export();
}
?>

Beispiel: Die konkrete Klasse PartnerInterface_HTTP

(:source lang=php linenum:)
<?php
require_once 'PartnerInterface.php';

class PartnerInterface_HTTP extends PartnerInterface {
  public function import($data) {
    // ...
  }

  public function export() {
    // ...
  }
}
?>
 
 
March 02, 2008, at 07:04 PM by chueser - + dekorator
Added lines 850-977:

Problem

Objekte sollen dynamisch um Funktionalität erweitert werden können, ohne die zugehörige Klasse durch Unterklassenbildung statisch erweitern zu müssen.

Motivation

Oft kommt es vor, dass man die Funktionalität eines Objektes dynamisch und transparent erweitern oder verändern möchte. Dem statischen Ansatz der Erweiterung der Klassenhierarchie um entsprechende Unterklassen ist meist eine objektbasierte Lösung vorzuziehen. Hierbei kann die Funktionalität verschiedener Objekte durch lose Kopplung zur Laufzeit "zusammengesteckt" werden.

Lösung

Die erweiterte oder veränderte Funktionalität wird in einem so genannten Dekorierer-Objekt modelliert, das dieselbe Schnittstelle wie das zu dekorierende Objekt anbietet. Dies erlaubt die transparente Benutzung des Dekorierer-Objekts durch Verwender des ursprünglichen Objektes. Das Dekorierer-Objekt leitet Methodenaufrufe zur Ausführung an das aggregierte, zu dekorierende Objekt weiter und kann seine zusätzliche Funktionalität vor oder nach diesem Methodenaufruf ausführen.

Anwendungsbeispiele

Die Klassen FilterIterator und LimitIterator sind zwei Dekorierer. Objekte dieser Iterator-Klassen dekorieren ein anderes Iterator-Objekt und filtern oder limitieren dessen Elemente.

Für die Anpassung und Erweiterung der Testausführung bietet PHPUnit die Dekorierung der Klasse PHPUnit2_Framework_TestCase an. Das Grundgerüst des entsprechenden Dekorierers liegt in Form der Klasse PHPUnit2_Extensions_TestDecorator vor. Diese implementiert die vom PHPUnit-Framework für die Testausführung benötigte Schnittstelle PHPUnit2_Framework_Test und stellt ebenso wie die Klasse PHPUnit2_Framework_TestCase die Zusicherungsmethoden der Klasse PHPUnit2_Framework_Assert bereit. Sie kann daher von jedem Verwender, der eigentlich ein Objekt der Klasse PHPUnit2_Framework_TestCase erwartet, verarbeitet werden.

Beispiel: Die Klasse PHPUnit2_Extensions_TestDecorator

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/Assert.php';
require_once 'PHPUnit2/Framework/Test.php';
require_once 'PHPUnit2/Framework/TestResult.php';

class PHPUnit2_Extensions_TestDecorator
extends PHPUnit2_Framework_Assert
implements PHPUnit2_Framework_Test {
  protected $test = NULL;

  public function
  __construct(PHPUnit2_Framework_Test $test) {
    $this->test = $test;
  }

  public function toString() {
    return $this->test->toString();
  }

  public function
  basicRun(PHPUnit2_Framework_TestResult $result) {
    $this->test->run($result);
  }

  public function countTestCases() {
    return $this->test->countTestCases();
  }

  protected function createResult() {
    return new PHPUnit2_Framework_TestResult;
  }

  public function getTest() {
    return $this->test;
  }

  public function
  run(PHPUnit2_Framework_TestResult $result) {
    $this->basicRun($result);
    return $result;
  }
}
?>

Beispiel: Die Klasse PHPUnit2_Extensions_RepeatedTest

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/Test.php';
require_once 'PHPUnit2/Framework/TestResult.php';
require_once 'PHPUnit2/Extensions/TestDecorator.php';

class PHPUnit2_Extensions_RepeatedTest
extends PHPUnit2_Extensions_TestDecorator {
  private $timesRepeat = 1;

  public function
  __construct(PHPUnit2_Framework_Test $test,
              $timesRepeat = 1) {
      parent::__construct($test);

      if (is_integer($timesRepeat) &&
          $timesRepeat >= 0) {
          $this->timesRepeat = $timesRepeat;
      } else {
          throw new Exception('Illegal argument.');
      }
  }

  public function countTestCases() {
      return $this->timesRepeat *
             $this->test->countTestCases();
  }

  public function
  run(PHPUnit2_Framework_TestResult $result) {
    for ($i = 0;
         $i < $this->timesRepeat && !$result->shouldStop();
         $i++) {
      $this->test->run($result);
    }

    return $result;
  }
}
?>

Für die dekorierte Ausführung eines Tests, in unserem Beispiel also die wiederholte Ausführung einer Testfallmethode, wird zunächst ein Objekt der Testfallklasse erzeugt. Dieses wird anschließend dem Konstruktor der Dekorierer-Klasse (hier PHPUnit2_Extensions_RepeatedTest) übergeben, um ein entsprechendes Dekorierer-Objekt zu erzeugen, das dann für die Testausführung mit PHPUnit2_Framework_TestResult verwendet werden kann.

Beispiel: Wiederholte Testausführung mit PHPUnit2_Extensions_RepeatedTest

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/TestResult.php';
require_once 'PHPUnit2/Extensions/RepeatedTest.php';

require_once 'SampleTest.php';

$result = new PHPUnit2_Framework_TestResult;

// Führt SampleTest::testDoSomething() einmal aus.
$test = new SampleTest('testDoSomething');
$result->run($test);

// Führt SampleTest::testDoSomething() einmal aus.
$decoratedTest = new PHPUnit2_Extensions_RepeatedTest($test, 2);
$result->run($decoratedTest);
?>
 
 
March 02, 2008, at 05:14 PM by chueser -
Changed line 634 from:

attach($observer)

to:

attach($observer)

 
 
March 02, 2008, at 05:13 PM by chueser -
Changed line 377 from:

(Quelle: Reflection, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

to:

(Quelle: Reflection, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

 
 
March 02, 2008, at 05:12 PM by chueser - observer
Added lines 621-846:

Problem

Wenn ein Objekt seinen Zustand ändert, sollen davon abhängige Objekte benachrichtigt werden.

Motivation

Eine Änderung an einem Objekt erfordert Änderungen an anderen Objekten, um die Konsistenz des Gesamtsystems zu erhalten. An Stelle einer engen Kopplung wird eine lose Kopplung der Objekte angestrebt, bei der die beteiligten Objekte unabhängig voneinander variiert werden können.

Lösung

Das unter Beobachtung stehende Objekt, Subject genannt, stellt die folgenden Methoden zur Verfügung, über die sich andere Objekte, Observer genannt, für die Benachrichtigung bei Zustandsänderungen an- und abmelden können:

attach($observer)

Registriert das Objekt $observer zur Benachrichtigung bei Änderung des Zustands des Objektes, dessen attach()-Methode aufgerufen wird.

detach($observer)

Hebt die Registrierung des Objektes $observer zur Benachrichtigung bei Änderung des Zustands des Objektes, dessen detach()-Methode aufgerufen wird.

getState()

Liefert den aktuellen Zustand des Objektes. Ein Beobachter-Objekt kann sich so nach der Benachrichtigung über eine Änderung des Zustands des beobachteten Objektes darüber informieren, wie sich dessen Zustand geändert hat.

Die Benachrichtigung der Beobachter-Objekte erfolgt durch den Aufruf der Methode notify() des Subject-Objektes. Diese Methode ruft auf jedem als Beobachter registrierten Objekt dessen update()-Methode auf, die von den Beobachter-Objekten bereitzustellen ist.

Die folgende Abbildung zeigt zwei Klassen Subject und Observer, die die beschriebene Funktionalität zur Verfügung stellen und als Basis für zwei konkrete Klassen, ConcreteSubject und ConcreteObserver, dienen.

Abbildung: Struktur des Beobachter-Musters

Beispiel: Die abstrakte Klasse Subject

(:source lang=php linenum:)
<?php
require_once 'Observer.php';

abstract class Subject {
  protected $observers = array();

  public function attach(Observer $observer) {
      $this->observers[] = $observer;
  }

  public function detach(Observer $observer) {
    for ($i = 0; $i < sizeof($this->observers); $i++) {
      if ($this->observers[$i] === $observer) {
        unset($this->observers[$i]);
      }
    }
  }

  protected function notify() {
    for ($i = 0; $i < sizeof($this->observers); $i++) {
      $this->observers[$i]->update();
    }
  }

  public abstract function getState();
}
?>

Beispiel: Die abstrakte Klasse Observer

(:source lang=php linenum:)
<?php
require_once 'Subject.php';

abstract class Observer {
  protected $subject = NULL;

  public function attach(Subject $subject) {
    $this->subject = $subject;
    $this->subject->attach($this);
  }

  public function detach() {
    if ($this->subject !== NULL) {
      $this->subject->detach($this);
    }
  }

  public abstract function update();
}
?>

Beispiel: Die Klasse ConcreteSubject

(:source lang=php linenum:)
<?php
require_once 'Subject.php';

class ConcreteSubject extends Subject {
  protected $state = NULL;

  public function getState() {
    return $this->state;
  }

  public function doSomething() {
    // ...

    $this->notify();
  }
}
?>

Beispiel: Die Klasse ConcreteObserver

(:source lang=php linenum:)
<?php
require_once 'Observer.php';

class ConcreteObserver extends Observer {
  protected $state = NULL;

  public function __construct(Subject $subject) {
    $this->attach($subject);
  }

  public function update() {
    print 'ConcreteObserver::update()';

    $this->state = $this->subject->getState();
  }
}
?>

Beispiel: Verwendung von Subject und Observer

(:source lang=php linenum:)
<?php
require_once 'ConcreteSubject.php';
require_once 'ConcreteObserver.php';

$subject  = new ConcreteSubject;
$observer = new ConcreteObserver($subject);

$subject->doSomething();
$observer->detach();
$subject->doSomething();
?>

ConcreteObserver::update()

Anwendungsbeispiele

Das Problem, das ursprünglich das Beobachter-Muster motivierte, war die Kommunikation zwischen dem Model-Objekt und dessen View-Objekten in einer Applikation, die dem Model-View-Controller-Prinzip (MVC) folgt. Hierbei wird eine Anwendung in die drei Schichten Datenmodell (Model), Darstellungsschicht (View) und Steuerungsschicht (Controller) unterteilt. Diese Schichten sind voneinander entkoppelt: Das Datenmodell kennt weder Darstellungsschicht noch Steuerungsschicht. Die Darstellungsschicht registriert sich als Beobachter des Datenmodells und stellt dieses dar. Die Steuerungsschicht steuert den Ablauf der Anwendung und nimmt Änderungen am Datenmodell über dessen Programmierschnittstelle vor.

Ein anderes Einsatzgebiet des Beobachter-Musters ist das Protokollieren von Abläufen. Für die Verfolgung, und damit für das Protokollieren, der Testausführung bietet PHPUnit die Schnittstelle PHPUnit2_Framework_TestListener an. Objekte von Klassen, die diese implementieren, können über eine entsprechende Methode (addListener($listener)) an ein Objekt der Klasse PHPUnit2_Framework_TestResult "angehängt" werden, um dieses zu beobachten und so die Testausführung zu protokollieren.

Beispiel: Eine Implementierung der Schnittstelle PHPUnit2_Framework_TestListener

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/TestListener.php';

class SimpleTestListener
implements PHPUnit2_Framework_TestListener {
  public function
  addError(PHPUnit2_Framework_Test $test, Exception $e) {
    printf(
      'Bei der Ausführung des Testfalls "%s"' .
      " trat ein Fehler auf.\n",
      $test->getName()
    );
  }

  public function
  addFailure(PHPUnit2_Framework_Test $test,
             PHPUnit2_Framework_AssertionFailedError $e) {
    printf(
      "Der Testfall \"%s\" schlug fehl.\n",
      $test->getName()
    );
  }

  public function
  addIncompleteTest(PHPUnit2_Framework_Test $test,
                    Exception $e) {
    printf(
      "Der Testfall \"%s\" wurde nicht implementiert.\n",
      $test->getName()
    );
  }

  public function startTest(PHPUnit2_Framework_Test $test) {
    printf(
      "Ausführung des Testfalls \"%s\" wurde gestartet.\n",
      $test->getName()
    );
  }

  public function endTest(PHPUnit2_Framework_Test $test) {
    printf(
      "Ausführung des Testfalls \"%s\" wurde beendet.\n",
      $test->getName()
    );
  }

  public function
  startTestSuite(PHPUnit2_Framework_TestSuite $suite) {
  }

  public function
  endTestSuite(PHPUnit2_Framework_TestSuite $suite) {
  }
}
?>

Im nächsten Beispiel wird zunächst ein neues Objekt der Klasse PHPUnit2_Framework_TestResult erzeugt. Diesem wird im Anschluss ein Objekt der Klasse SimpleTestListener aus dem letzten Beispiel als Listener hinzugefügt. Schließlich wird das PHPUnit2_Framework_TestResult-Objekt genutzt, um den testDoSomething Testfall auszuführen.

Beispiel: Ausführung eines Testfalls unter Beobachtung des SimpleTestListener

(:source lang=php linenum:)
<?php
require_once 'PHPUnit2/Framework/TestResult.php';

require_once 'SampleTest.php';
require_once 'SimpleTestListener.php';

$test = new SampleTest('testDoSomething');

$result = new PHPUnit2_Framework_TestResult;
$result->addListener(new SimpleTestListener);
$result->run($test);
?>
Ausführung des Testfalls "testDoSomething" wurde gestartet.
Ausführung des Testfalls "testDoSomething" wurde beendet.

(Quelle: Observer-Muster, Sebastian Bergmann, 01.03.2008, 17:00 GMT+1)

 
 
March 02, 2008, at 04:35 PM by chueser -
Changed line 616 from:

(Quelle: Strategy-Muster, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

to:

(Quelle: Strategy-Muster, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

 
 
March 02, 2008, at 04:34 PM by chueser -
Changed lines 89-91 from:

(Quelle: Motivation, 28.02.2008)

to:

(Quelle: Motivation, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Changed lines 188-190 from:

(Quelle: OOP, 28.02.2008)

to:

(Quelle: OOP, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Changed lines 236-237 from:

(Quelle: Polymorphie, 28.02.2008)

to:

(Quelle: Polymorphie, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Changed lines 275-277 from:

(Quelle: Serialization, 24.02.2008)

to:

(Quelle: Serialization, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

Changed lines 377-379 from:

(Quelle: Reflection, 28.02.2008)

to:

(Quelle: Reflection, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

Changed lines 432-434 from:

(Quelle: Migration, 28.02.2008)

to:

(Quelle: Migration, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

Changed lines 449-451 from:

(Quelle: Interzeptionsmethoden, 28.02.2008)

to:

(Quelle: Interzeptionsmethoden, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

Changed lines 616-618 from:

(Quelle: Strategy-Muster, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

to:

(Quelle: Strategy-Muster, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)-]

Changed lines 658-660 from:

(Quelle: Factory Method, 24.02.2008)

to:

(Quelle: Factory Method, 01.03.2008, 16:30 GMT+1)

Changed lines 705-707 from:

(Quelle: Factory Method, 24.02.2008)

to:

(Quelle: Factory Method, 01.03.2008, 16:30 GMT+1)

Changed lines 769-771 from:

(Quelle: Singleton, 24.02.2008)

to:

(Quelle: Singleton, 01.03.2008, 16:30 GMT+1)

Changed lines 810-811 from:

(Quelle: Abstract Singleton, 24.02.2008)

to:

(Quelle: Abstract Singleton, 01.03.2008, 16:30 GMT+1)

Changed line 896 from:

(Quelle: AntiPattern, 22.02.2008)

to:

(Quelle: AntiPattern, 01.03.2008, 16:30 GMT+1)-]

 
 
March 02, 2008, at 04:29 PM by chueser -
Added lines 615-616:

(Quelle: Strategy-Muster, Sebastian Bergmann, 01.03.2008, 16:30 GMT+1)

 
 
March 02, 2008, at 04:22 PM by chueser - + strategy dp
Added lines 508-615:

Problem

Eine Familie von Algorithmen soll gekapselt werden, mit der Möglichkeit, sie beliebig auszutauschen.

Motivation

Das Strategie-Muster bietet sich immer dann an, wenn eine Aufgabe mit unterschiedlichen Verfahren, die sich beispielsweise in Geschwindigkeit und Speicherverbrauch unterscheiden, zu lösen ist. Der Eingabe entsprechend kann so das jeweils beste Verfahren dynamisch zur Laufzeit verwendet werden. Es kann ebenfalls genutzt werden, wenn verwandte Klassen sich nur in ihrem Verhalten unterscheiden oder eine Klasse unterschiedliche Verhaltensweisen definiert und diese mit unübersichtlichen if-then-else- oder switch-case-Konstruktionen in ihren Methoden implementiert sind. Zusammenhängende Zweige dieser Bedingungsanweisungen können übersichtlich in eigene Strategieklassen ausgelagert werden.

Lösung

Die Verwandtschaft der Klassen wird durch Implementieren einer gemeinsamen Schnittstelle zum Ausdruck gebracht. Die Verwenderklasse arbeitet mit einem Objekt einer Klasse, die diese Schnittstelle bereitstellt. Dieses Objekt kann zur Laufzeit durch eine Methode setStrategy($strategy) ausgetauscht werden, wodurch das Verhalten der Verwenderklasse dynamisch geändert werden kann.

Anwendungsbeispiele

Das folgende Beispiel zeigt eine Implementierung des bekannten Sortierverfahrens Bubble-Sort. In der hier gezeigten Variante lässt sich der Vergleich von zwei Elementen der zu sortierenden Menge durch Verwendung einer Strategie austauschen.

Beispiel: Die Schnittstelle CompareStrategy

(:source lang=php linenum:)
<?php
interface CompareStrategy {
    public function compare($a, $b);
}
?>

Beispiel: Die Klasse AscendingCompare

(:source lang=php linenum:)
<?php
require_once 'CompareStrategy.php';

class AscendingCompare implements CompareStrategy {
  public function compare($a, $b) {
    return ($a == $b) ? 0 : ($a > $b) ? 1 : -1;
  }
}
?>

Beispiel: Die Klasse DescendingCompare

(:source lang=php linenum:)
<?php
require_once 'CompareStrategy.php';

class DescendingCompare implements CompareStrategy {
  public function compare($a, $b) {
    return ($a == $b) ? 0 : ($a < $b) ? 1 : -1;
  }
}
?>

Beispiel: Die Klasse BubbleSort

(:source lang=php linenum:)
<?php
require_once 'CompareStrategy.php';
require_once 'AscendingCompare.php';
require_once 'DescendingCompare.php';

class BubbleSort {
  private $strategy;

  public function setStrategy(CompareStrategy $strategy) {
    $this->strategy = $strategy;
  }

  public function sort($array) {
    for ($i = sizeof($array)-1; $i >= 0; --$i) {
      for ($j = 0; $j < $i; ++$j ) {
        $cmp = $this->strategy->compare(
          $array[$j],
          $array[$j+1]
        );

        if ($cmp > 0) {
          $tmp         = $array[$j];
          $array[$j]   = $array[$j+1];
          $array[$j+1] = $tmp;
        }
      }
    }

    return $array;
  }
}

$bs = new BubbleSort;

$bs->setStrategy(new AscendingCompare);
print_r($bs->sort(array(22, 4, 1978)));

$bs->setStrategy(new DescendingCompare);
print_r($bs->sort(array(22, 4, 1978)));
?>
Array
(
    [0] => 4
    [1] => 22
    [2] => 1978
)

Array
(
    [0] => 1978
    [1] => 22
    [2] => 4
)

In seiner Zielrichtung ist das Strategie-Muster mit der Schablonenmethode verwandt. Der Unterschied zwischen diesen beiden Mustern liegt in der Wahl des Mittels, mit dem man das Ziel zu erreichen versucht: Während das Strategie-Muster mittels Delegation den gesamten Algorithmus zur Laufzeit austauschbar macht, nutzt das Muster der Schablonenmethode Vererbung, um einzelne Schritte einer Operation variabel zu gestalten.

 
 
February 28, 2008, at 07:17 PM by chueser -
Changed line 469 from:
  • Originalität ist nach wie vor bei der Anwendung der Entwurfsmuster gefragt.
to:
Originalität ist nach wie vor bei der Anwendung der Entwurfsmuster gefragt.
Changed line 471 from:
  • Algorithmen lösen feinkörnigere Probleme (Suchen, Sortieren) und bieten weniger Freiheitsgrade in der Implementierung.
to:
Algorithmen lösen feinkörnigere Probleme (Suchen, Sortieren) und bieten weniger Freiheitsgrade in der Implementierung.
Changed lines 473-474 from:

Frameworks existieren als konkreter, wiederverwendbarer Code, Entwurfsmuster enthalten nur Beispiele von Code. Frameworks werden für konkrete Anwendungsbereiche eingesetzt, ein Entwurfsmuster kann überall eingesetzt werden.

to:
Frameworks existieren als konkreter, wiederverwendbarer Code, Entwurfsmuster enthalten nur Beispiele von Code. Frameworks werden für konkrete Anwendungsbereiche eingesetzt, ein Entwurfsmuster kann überall eingesetzt werden.
 
 
February 28, 2008, at 07:12 PM by chueser -
Changed lines 477-480 from:
  • Erzeugungsmuster
  • Strukturmuster
  • Verhaltensmuster
to:
  • Erzeugungsmuster
  • Strukturmuster
  • Verhaltensmuster
 
 
February 28, 2008, at 07:11 PM by chueser -
Added lines 462-480:

Während der letzten Jahre haben sich in der objektorientierten Programmierung einige sehr nützliche Entwurfsmuster, englisch Design Patterns, herausgebildet.

Entwurfsmuster dienen dazu, gewonnene Erfahrungen über die Lösung wiederkehrender Probleme zu vermitteln. Diese Kombination von Problem und Lösung wird zudem mit einem prägnanten Namen versehen, um ein einheitliches Vokabular zur Diskussion von Problem und Lösung zur Verfügung zu stellen.

Die Vorteile der Entwurfsmuster liegen auf der Hand: Durch die Nutzung von vorhandenem Wissen spart man Zeit bei der Entwicklung und kann Fehler vermeiden, die bereits von anderen gemacht wurden. Die Aufgabe des Softwareentwicklers verlagert sich unter Verwendung von Entwurfsmustern von der Erfindung des Rads zur Auswahl des richtigen Rads und seiner kreativen Verwendung. Hierbei sollte man jedoch die folgenden Punkte stets im Hinterkopf behalten:

  • Entwurfsmuster sind kein Allheilmittel!
  • Originalität ist nach wie vor bei der Anwendung der Entwurfsmuster gefragt.
  • Entwurfsmuster sind keine Algorithmen!
  • Algorithmen lösen feinkörnigere Probleme (Suchen, Sortieren) und bieten weniger Freiheitsgrade in der Implementierung.
  • Entwurfsmuster sind keine Frameworks!

Frameworks existieren als konkreter, wiederverwendbarer Code, Entwurfsmuster enthalten nur Beispiele von Code. Frameworks werden für konkrete Anwendungsbereiche eingesetzt, ein Entwurfsmuster kann überall eingesetzt werden.

Es gibt drei Gruppen von Entwurfsmustern:

  • Erzeugungsmuster
  • Strukturmuster
  • Verhaltensmuster
 
 
February 28, 2008, at 07:04 PM by chueser -
Deleted line 14:
 
 
February 28, 2008, at 06:49 PM by chueser -
Changed lines 13-14 from:
to:
Changed lines 433-436 from:

(Quelle: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.migration.htmlMigration, 28.02.2008)

to:

(Quelle: Migration, 28.02.2008)

Changed line 450 from:

(Quelle: http://professionelle-softwareentwicklung-mit-php5.de/oop.interceptors.htmllInterzeptionsmethoden, 28.02.2008)

to:

(Quelle: Interzeptionsmethoden, 28.02.2008)

 
 
February 28, 2008, at 06:47 PM by chueser -
Added lines 13-14:
Added lines 433-449:

Interzeptionsmethoden

Neben Konstruktor und Destruktor sowie den Methoden __sleep() und __wakeup() bietet PHP noch eine Reihe weiterer spezieller Methoden an, die für bestimmte Ereignisse automatisch aufgerufen werden. Da diese Methoden die entsprechenden Ereignisse in gewisser Weise "abfangen", nennt man sie Interzeptormethoden.

PHP bietet die folgenden Interzeptormethoden an. Sie werden automatisch aufgerufen beim Zugriff auf nicht deklarierte Instanzvariablen und Methoden eines Objektes, beim Versuch, ein Objekt einer nicht deklarierten Klasse zu erzeugen, sowie bei der Typumwandlung eines Objektes in einen String.

  • __autoload($className): Wird aufgerufen, wenn ein Objekt der Klasse $className erzeugt werden soll, die Klasse aber nicht deklariert ist.
  • __get($memberName): Wird aufgerufen, wenn lesend auf die Instanzvariable $memberName eines Objektes zugegriffen wird, die Instanzvariable aber nicht gesetzt ist.
  • __set($memberName, $value): Wird aufgerufen, wenn schreibend auf die Instanzvariable $memberName eines Objektes zugegriffen wird und sie vorher nicht gesetzt war. Der zweite Parameter $value enthält den Wert, den die Instanzvariable erhalten soll.
  • __call($methodName, $parameters): Wird aufgerufen, wenn eine nicht deklarierte Methode $methodName auf einem Objekt aufgerufen wird. Der zweite Parameter $parameters enthält die Parameter des Methodenaufrufes.
  • __toString(): Wird aufgerufen, wenn eine Typumwandlung eines Objektes in einen String durchgeführt werden soll.

(Quelle: http://professionelle-softwareentwicklung-mit-php5.de/oop.interceptors.htmllInterzeptionsmethoden, 28.02.2008)

 
 
February 28, 2008, at 06:11 PM by chueser -
Changed lines 5-41 from:
to:
Added line 44:

Added lines 189-192:

OOP in PHP

Changed line 236 from:
to:

Added line 276:

Changed line 378 from:
to:

Added line 433:

Added lines 444-445:

Added line 450:

Changed line 473 from:
to:

Changed line 476 from:
to:

Changed line 479 from:
to:

Added line 562:

Changed line 666 from:
to:

Changed line 669 from:
to:

Changed line 672 from:
to:

Changed line 675 from:
to:

Changed line 678 from:
to:

Changed line 681 from:
to:

Changed line 684 from:
to:

Changed line 687 from:
to:

Added line 690:

Added line 695:

Added line 700:

Added line 705:

Added line 710:

Added line 715:

Added line 720:

Added line 725:

Added line 730:

Changed line 735 from:
to:

Added line 740:

Added line 745:

 
 
February 28, 2008, at 05:39 PM by chueser -
Changed lines 278-280 from:

?>

Class [ <user> class Klasse ] {

to:

?>@]

[@Class [ <user> class Klasse ] {

Changed line 331 from:

Klasse

to:

Klasse

 
 
February 28, 2008, at 05:37 PM by chueser -
Added lines 332-388:

(Quelle: Reflection, 28.02.2008)

Migration von PHP 4 zu PHP 5

Bei der Entwicklung von PHP 5 wurde versucht, die Abwärtskompatibilität zu PHP 4 zu wahren. In diesem Abschnitt finden Sie eine Übersicht über Änderungen in PHP 5, die eine Änderung von bestehenden PHP-4-Anwendungen erforderlich macht.

Kopie versus Referenz

Mit der Einführung des neuen Objektmodells werden Objekte standardmäßig per Referenz übergeben. In PHP 4 wurde stattdessen stets eine Kopie übergeben. Für PHP-Programme, die wie von PHP 4 gewohnt eine Kopie statt einer Referenz erwarten, kann die php.ini-Direktive zend.ze1_compatibility_mode auf On gesetzt werden.

Konstruktor

In PHP 4 entsprach der Name des Konstruktors dem Namen der Klasse. In PHP 5 heißt der Konstruktor nun __construct. Wird in einer Klasse keine Methode mit dem Namen __construct deklariert, so wird nach einer Methode gesucht, die den Namen der Klasse trägt. Wird eine solche Methode gefunden, so wird sie als Konstruktor benutzt. Wird eine Methode mit dem Namen __construct gefunden, so wird diese in jedem Fall (unabhängig davon, ob auch eine Methode mit dem Namen der Klasse existiert) benutzt.

Klassendeklaration vor Objekterzeugung

In PHP 4 war es möglich, ein Objekt einer Klasse zu erzeugen, die zum Zeitpunkt der Instanzierung noch nicht deklariert war. In PHP 5 ist dies nicht mehr möglich, wenn die Klasse Sprachmerkmale verwendet, die mit PHP 5 eingeführt wurden.

Klassen, die nur Sprachmerkmale enthalten, die bereits in PHP 4 zur Verfügung standen, können weiterhin vor ihrer Deklarierung verwendet werden.

Neue Schlüsselwörter

In PHP 5 sind eine Reihe von neuen Schlüsselwörtern hinzugekommen, die nicht mehr als Namen von Klassen, Konstanten, Methoden oder Funktionen verwendet werden können:

  • abstract
  • catch
  • clone
  • final
  • implements
  • interface
  • private
  • protected
  • public
  • throw
  • try

Bei der Migration von PHP 4 nach PHP 5 müssen Klassen, Konstanten, Methoden oder Funktionen, die einen dieser Namen tragen, umbenannt werden.

Besondere Methoden

In PHP 5 sind einige Methodennamen hinzugekommen, die mit einer besonderen Semantik verknüpft sind:

  • __autoload
  • __call
  • __clone
  • __construct
  • __destruct
  • __get
  • __set
  • __toString

Bei der Migration von PHP 4 nach PHP 5 müssen Methoden, die einen dieser Namen tragen, umbenannt werden.

(Quelle: http://professionelle-softwareentwicklung-mit-php5.de/oop.foundations.migration.htmlMigration, 28.02.2008)

 
 
February 28, 2008, at 05:07 PM by chueser -
Changed lines 311-315 from:

getFileName(), getStartLine(), getEndLine(), getDocComment(), getExtension() und getExtensionName() liefern Informationen über die Deklaration der Klasse in einer Quelltextdatei beziehungsweise in einer PHP-Erweiterung.

isInternal(), isUserDefined(), isInstantiable(), isInterface(), isAbstract(), isFinal(), getParentClass(), isSubclassOf(), implementsInterface() und isIterateable() liefern Informationen über Eigenschaften der Klasse wie beispielsweise Vererbungsbeziehungen.

getMethod(), getMethods(), getProperty(), getProperties(), getConstants() und getConstant() liefern beispielsweise Objekte der Klassen ReflectionMethod und ReflectionProperty, um mit den Methoden, Instanzvariablen und Konstanten einer Klasse zu arbeiten.

to:
  • getFileName(), getStartLine(), getEndLine(), getDocComment(), getExtension() und getExtensionName() liefern Informationen über die Deklaration der Klasse in einer Quelltextdatei beziehungsweise in einer PHP-Erweiterung.
  • isInternal(), isUserDefined(), isInstantiable(), isInterface(), isAbstract(), isFinal(), getParentClass(), isSubclassOf(), implementsInterface() und isIterateable() liefern Informationen über Eigenschaften der Klasse wie beispielsweise Vererbungsbeziehungen.
  • getMethod(), getMethods(), getProperty(), getProperties(), getConstants() und getConstant() liefern beispielsweise Objekte der Klassen ReflectionMethod und ReflectionProperty, um mit den Methoden, Instanzvariablen und Konstanten einer Klasse zu arbeiten.
 
 
February 28, 2008, at 05:05 PM by chueser -
Changed lines 239-260 from:

Für den "Blick in das Innere" von Klassen und Objekten bietet PHP die so genannte Reflection API an, die die folgenden Klassen und Schnittstellen umfasst:

Reflector ist die Schnittstelle, die von allen Klassen der Reflection API implementiert wird.

Reflection ist eine statische Hilfsklasse, die die Arbeit mit Reflector-Objekten erleichtert.

ReflectionException ist die von Exception abgeleitete Ausnahme-Klasse, die für das Signalisieren von Fehlern innerhalb der Reflection API verwendet wird.

ReflectionExtension repräsentiert die Informationen über eine PHP-Erweiterung.

ReflectionFunction repräsentiert die Informationen über eine Funktion.

ReflectionParameter repräsentiert die Informationen über einen Parameter einer Funktion oder Methode.

ReflectionClass repräsentiert die Informationen über eine Klasse.

ReflectionObject ist eine Erweiterung von ReflectionClass und repräsentiert die Informationen über ein Objekt.

ReflectionMethod ist eine Erweiterung von ReflectionFunction und repräsentiert die Informationen über eine Methode.

ReflectionProperty repräsentiert die Informationen über eine Instanzvariable einer Klasse oder eines Objektes.

to:
  • Für den "Blick in das Innere" von Klassen und Objekten bietet PHP die so genannte Reflection API an, die die folgenden Klassen und Schnittstellen umfasst:
  • Reflector ist die Schnittstelle, die von allen Klassen der Reflection API implementiert wird.
  • Reflection ist eine statische Hilfsklasse, die die Arbeit mit Reflector-Objekten erleichtert.
  • ReflectionException ist die von Exception abgeleitete Ausnahme-Klasse, die für das Signalisieren von Fehlern innerhalb der Reflection API verwendet wird.
  • ReflectionExtension repräsentiert die Informationen über eine PHP-Erweiterung.
  • ReflectionFunction repräsentiert die Informationen über eine Funktion.
  • ReflectionParameter repräsentiert die Informationen über einen Parameter einer Funktion oder Methode.
  • ReflectionClass repräsentiert die Informationen über eine Klasse.
  • ReflectionObject ist eine Erweiterung von ReflectionClass und repräsentiert die Informationen über ein Objekt.
  • ReflectionMethod ist eine Erweiterung von ReflectionFunction und repräsentiert die Informationen über eine Methode.
  • ReflectionProperty repräsentiert die Informationen über eine Instanzvariable einer Klasse oder eines Objektes.
Changed line 309 from:

Stellvertretend für die Klassen der Reflection API betrachten wir die Klasse ReflectionClass. Diese bietet die folgenden Methoden:

to:

Stellvertretend für die Klassen der Reflection API betrachten wir die Klasse ReflectionClass. Diese bietet die folgenden Methoden:

 
 
February 28, 2008, at 05:02 PM by chueser - + reflection
Changed line 210 from:

[@<?php

to:

(:source lang=php linenum:)[@<?php

Added lines 233-331:

Die Reflection API

Die strukturelle Reflexion, auch Introspektion genannt, macht die Struktur eines objektorientierten Systems durch die Programmiersprache des Systems zugänglich. Dies ermöglicht das Schreiben von generischem Code, der zur Laufzeit Informationen über Klassen und deren Objekte abfragt und diese entsprechend verarbeitet. Diese Generizität wird beispielsweise für das Schreiben von Code- und Dokumentationsgeneratoren benötigt. Ferner bildet sie die Grundlage für Werkzeuge wie PHPUnit, die "fremden" Code analysieren und ausführen müssen.

Für den "Blick in das Innere" von Klassen und Objekten bietet PHP die so genannte Reflection API an, die die folgenden Klassen und Schnittstellen umfasst:

Reflector ist die Schnittstelle, die von allen Klassen der Reflection API implementiert wird.

Reflection ist eine statische Hilfsklasse, die die Arbeit mit Reflector-Objekten erleichtert.

ReflectionException ist die von Exception abgeleitete Ausnahme-Klasse, die für das Signalisieren von Fehlern innerhalb der Reflection API verwendet wird.

ReflectionExtension repräsentiert die Informationen über eine PHP-Erweiterung.

ReflectionFunction repräsentiert die Informationen über eine Funktion.

ReflectionParameter repräsentiert die Informationen über einen Parameter einer Funktion oder Methode.

ReflectionClass repräsentiert die Informationen über eine Klasse.

ReflectionObject ist eine Erweiterung von ReflectionClass und repräsentiert die Informationen über ein Objekt.

ReflectionMethod ist eine Erweiterung von ReflectionFunction und repräsentiert die Informationen über eine Methode.

ReflectionProperty repräsentiert die Informationen über eine Instanzvariable einer Klasse oder eines Objektes.

Allen Klassen der Reflection API gemein ist die statische Methode export(). Diese liefert eine textuelle Darstellung des reflektierten Sprachobjekts.

Beispiel: Verwendung von ReflectionClass::export()

(:source lang=php linenum:)
<?php
class Klasse {
  public $public;
  protected $protected;
  private $privateStatic;

  public function methode(Klasse $objekt) {
    $objekt = new ReflectionObject($objekt);
    print $objekt->getName();
  }
}

print ReflectionClass::export('Klasse');
?>

Class [ <user> class Klasse ] {
  @@ /home/sb/export.php 2-11

  - Constants [0] {
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [3] {
    Property [ <default> public $public ]
    Property [ <default> protected $protected ]
    Property [ <default> private $privateStatic ]
  }

  - Methods [1] {
    Method [ <user> public method methode ] {
      @@ /home/sb/export.php 7 - 10

      - Parameters [1] {
        Parameter #0 [ <required> Klasse $objekt ]
      }
    }
  }
}

Stellvertretend für die Klassen der Reflection API betrachten wir die Klasse ReflectionClass. Diese bietet die folgenden Methoden:

getFileName(), getStartLine(), getEndLine(), getDocComment(), getExtension() und getExtensionName() liefern Informationen über die Deklaration der Klasse in einer Quelltextdatei beziehungsweise in einer PHP-Erweiterung.

isInternal(), isUserDefined(), isInstantiable(), isInterface(), isAbstract(), isFinal(), getParentClass(), isSubclassOf(), implementsInterface() und isIterateable() liefern Informationen über Eigenschaften der Klasse wie beispielsweise Vererbungsbeziehungen.

getMethod(), getMethods(), getProperty(), getProperties(), getConstants() und getConstant() liefern beispielsweise Objekte der Klassen ReflectionMethod und ReflectionProperty, um mit den Methoden, Instanzvariablen und Konstanten einer Klasse zu arbeiten.

Im nächsten Beispiel erzeugen wir zunächst ein Objekt der Klasse ReflectionClass für die Klasse Klasse aus vorherigem Beispiel. Über die Methode getMethod() dieses Objektes gelangen wir an ein Objekt der Klasse ReflectionMethod, das die Methode Klasse::Methode repräsentiert. Diese Methode können wir mit ReflectionMethod::invoke() ausführen. Als ersten Parameter müssen wir das Objekt der zugehörigen Klasse (in unserem Beispiel Klasse) übergeben, auf dem die Methode ausgeführt werden soll. Danach folgen die Parameter der Methode, die aufgerufen werden soll.

Beispiel: ReflectionClass und ReflectionMethod im Einsatz

(:source lang=php linenum:)
<?php
require_once 'Klasse.php';

$klasse = new ReflectionClass('Klasse');
$objekt = new Klasse;

$methode = $klasse->getMethod('methode');
$methode->invoke($objekt, $objekt);
?>

Klasse

 
 
February 28, 2008, at 04:31 PM by chueser - + Serialization
Added lines 194-232:

Serialisierung von Objekten

Objekte existieren nur zur Laufzeit, können aber durch die Speicherung ihres Zustandes persistent (dauerhaft) gemacht werden. Sie können somit gespeichert und zu einem späteren Zeitpunkt wieder geladen werden. Die Speicherung eines Objektes und seines Zustands wird auch Serialisierung genannt.

PHP bietet die Funktionen serialize() und unserialize(), um ein Objekt zu serialisieren beziehungsweise aus der serialisierten Form wieder ein Objekt zu erstellen. Hierbei erzeugt serialize() aus einem Objekt einen String, in dem die relevanten Daten kodiert sind. Dieser String kann dann beispielsweise in eine Datei geschrieben oder in einer Datenbank abgelegt werden. Analog erwartet unserialize() einen String in diesem Format, um ein Objekt aus dem kodierten String wiederherzustellen.

Verfügt die Klasse des Objektes, das serialisiert werden soll, über eine __sleep-Methode, so wird diese automatisch vor der eigentlichen Serialisierung auf dem Objekt aufgerufen. Diese Methode muss ein Array mit den Namen derjenigen Instanzvariablen zurückliefern, die serialisiert werden sollen. So kann die __sleep-Methode einer Klasse, die eine Datenbankverbindung kapselt, beispielsweise Sorge dafür tragen, dass nur die für den Verbindungsaufbau nötigen Parameter gespeichert werden, nicht aber die Ressource-ID der aktuell bestehenden Verbindung.

Verfügt die Klasse des Objektes, das deserialisiert werden soll, über eine __wakeup-Methode, so wird diese automatisch nach der eigentlichen Deserialisierung auf dem Objekt aufgerufen.

Wird ein Objekt durch Ablegen in dem Array $_SESSION[] als Session-Variable registriert, so kümmert sich PHP automatisch um Serialisierung und Deserialisierung des Objektes zwischen den einzelnen Requests der Session.

Beispiel: Verwendung der Methoden __sleep() und __wakeup()

<?php
class Klasse {
  function __sleep() {
    print "__sleep() aufgerufen.\n";
    return get_class_vars(get_class($this));
  }

  function __wakeup() {
    print "__wakeup() aufgerufen.\n";
  }
}

$objekt               = new Klasse;
$serialisiertesObjekt = serialize($objekt);
$objekt               = unserialize($serialisiertesObjekt);
?>

__sleep() aufgerufen. __wakeup() aufgerufen.

Weiterhin ist hier das Konzept Object-relational mapping (ORM) zu erwähnen, welches die Speichern von Objekten in einer relationalen Datenbank ermöglicht.

(Quelle: Serialization, 24.02.2008)

 
 
February 28, 2008, at 02:37 PM by chueser -
Changed lines 14-16 from:

So leistet der prozedurale Code in Beispiel 1.1 zwar die von ihm erwartete Aufgabe, ist aber in mehrerlei Hinsicht "unschön". An eine Wiederverwendung des Codes im eigentlichen Sinne von "einmal schreiben, mehrfach verwenden" ist allerdings nicht zu denken. Höchstens durch Duplizierung und Anpassung an den neuen Kontext kann er an anderer Stelle eingesetzt werden.

Beispiel 1.1: Zugriff auf eine MySQL-Datenbank ohne objektorientierte Konzepte

to:

So leistet der prozedurale Code in dem Beispiel unten zwar die von ihm erwartete Aufgabe, ist aber in mehrerlei Hinsicht "unschön". An eine Wiederverwendung des Codes im eigentlichen Sinne von "einmal schreiben, mehrfach verwenden" ist allerdings nicht zu denken. Höchstens durch Duplizierung und Anpassung an den neuen Kontext kann er an anderer Stelle eingesetzt werden.

Beispiel: Zugriff auf eine MySQL-Datenbank ohne objektorientierte Konzepte

 
 
February 28, 2008, at 11:32 AM by chueser -
Changed line 158 from:

(:source lang=php linenum:)[@class Klasse {

to:

(:source lang=java linenum:)[@class Klasse {

 
 
February 28, 2008, at 11:27 AM by chueser - + polymorpie
Changed lines 8-9 from:
to:

Warum OOP?

Changed lines 50-52 from:

(Quelle: OOP, 28.02.2008)

to:

(Quelle: Motivation, 28.02.2008)

Added lines 150-193:

Polymorphie

Mit dem Prinzip der Polymorphie (Vielgestaltigkeit) wird ein dynamisches Verhalten von Methoden verfolgt, das von Anzahl und Typ der übergebenen Parameter abhängt. Im Falle einer dynamisch getypten Programmiersprache wie PHP gestaltet sich dies jedoch anders als in einer statisch getypten Programmiersprache wie beispielsweise Java. Für unterschiedliche Anzahl oder Typen der erwarteten Parameter einer Methode schreibt man beispielsweise in Java mehrere Methoden mit dem gleichen Namen.

Beispiel: Polymorphie in Java

(:source lang=php linenum:)
class Klasse {
  public void methode(String variable) {
    System.out.println("Ein String wurde übergeben.");
  }

  public void methode(int variable) {
    System.out.println("Ein Integerwert wurde übergeben.");
  }
}

In PHP ist die Deklaration von mehreren Methoden des gleichen Namens nicht vorgesehen. Polymorphes Verhalten von Methoden kann in PHP auf eine der folgenden Arten erreicht werden:

Eine Methode kann wegen der dynamischen Typisierung einen Parameter akzeptieren, der unterschiedliche Typen enthalten darf.

Eine Methode kann eine variable Anzahl an Parametern akzeptieren, indem optionale Parameter mit Standardwerten versehen und an das Ende der Parameterliste gesetzt werden.

Häufig verwendet man ein assoziatives Array als einzigen Parameter einer Methode. Dies ermöglicht eine variable Anzahl an (benannten) Parametern für die Methode, deren Reihenfolge aufgrund der Assoziativität beliebig sein kann.

Beispiel: Polymorphie in PHP

(:source lang=php linenum:)
<?php
class Klasse {
  public function methode($variable) {
    if (is_string($variable)) {
      print "Ein String wurde übergeben.\n";
    }

    else if (is_integer($variable)) {
      print "Ein Integerwert wurde übergeben.\n";
    }
  }
}
?>

(Quelle: Polymorphie, 28.02.2008)

 
 
February 28, 2008, at 11:23 AM by chueser - + typing
Added lines 50-148:

Eine Klasse für den Zugriff auf eine MySQL-Datenbank

(:source lang=php linenum:)
<?php
class DB_MySQL {
  private $connection = NULL;
  private $result = NULL;

  public function connect($host, $database, $user, $pass) {
    $this->connection = mysql_connect(
      $host,
      $user,
      $pass,
      TRUE
    );

    mysql_select_db($database, $this->connection);
  }

  public function disconnect() {
    if (is_resource($this->connection)) {
      mysql_close($this->connection);
    }
  }

  public function query($query) {
    if (is_resource($this->connection)) {
      if (is_resource($this->result)) {
        mysql_free_result($this->result);
      }

      $this->result = mysql_query(
        $query,
        $this->connection
      );
    }
  }

  public function fetchRow() {
    if (is_resource($this->result)) {
      $row = mysql_fetch_assoc($this->result);

      if (is_array($row)) {
        return $row;
      } else {
        return FALSE;
      }
    }
  }
}
?>

Verwendung der MySQL-Klasse

(:source lang=php linenum:)
<?php
require_once 'DB_MySQL.php';

$mysql = new DB_MySQL;
$mysql->connect('localhost', 'test', 'root', '');
$mysql->query('SELECT spalte FROM tabelle');

while ($row = $mysql->fetchRow()) {
  // ...
}

$mysql->disconnect();
?>

Bei der Deklaration von Methoden erlaubt PHP die Angabe eines Klassen- oder Schnittstellennamens für als Parameter übergebene Objekte. Im Gegensatz zu statisch getypten Programmiersprachen erfolgt die Typprüfung jedoch nicht zum Zeitpunkt der Kompilierung, sondern erst zur Laufzeit. Diese so genannten Type Hints ersparen dem Programmierer Schreibarbeit, wie an folgenden Beispielen zu erkennen ist.

Typprüfung mit Type Hints

(:source lang=php linenum:)
<?php
class Vector {
  public function add(Vector $vector) {
    // ...
  }
}
?>

Typprüfung mit dem instanceof-Operator

(:source lang=php linenum:)
<?php
class Vector {
  public function add($vector) {
    if (!($vector instanceof Vector)) {
      die('Parameter muss vom Typ Vector sein.');
    }

    // ...
  }
}
?>

Die Beispiele sind semantisch äquivalent und unterscheiden sich nur in der Art der Typprüfung durch die Type Hints beziehungsweise den instanceof-Operator.

(Quelle: OOP, 28.02.2008)

 
 
February 28, 2008, at 11:02 AM by chueser - + motivation
Added lines 4-49:

Motivation

Wir wollen uns zunächst fragen, warum wir eine PHP-Anwendung überhaupt objektorientiert entwerfen wollen und nicht den vermeintlich einfacheren Weg der prozeduralen Programmierung wählen.

Für Code, der für die Verwendung durch andere Entwickler bestimmt ist oder von mehreren Programmierern erstellt wird, sollte vor dem eigentlichen Schreiben zunächst ein Design erstellt werden. Der Sinn eines solchen Designs ist es, gut organisierten und konsistenten Code zu schreiben, der einfach zu erweitern und zu warten ist. Ende der 60er, Anfang der 70er Jahre des vergangenen Jahrhunderts hielten neue Sprachkonstrukte, und mit ihnen ein neues Programmierparadigma, Einzug in Programmiersprachen wie Simula oder Smalltalk. Sie sollten die Formulierung des Designs in der Programmiersprache gegenüber dem etablierten prozeduralen Ansatz erleichtern.

So leistet der prozedurale Code in Beispiel 1.1 zwar die von ihm erwartete Aufgabe, ist aber in mehrerlei Hinsicht "unschön". An eine Wiederverwendung des Codes im eigentlichen Sinne von "einmal schreiben, mehrfach verwenden" ist allerdings nicht zu denken. Höchstens durch Duplizierung und Anpassung an den neuen Kontext kann er an anderer Stelle eingesetzt werden.

Beispiel 1.1: Zugriff auf eine MySQL-Datenbank ohne objektorientierte Konzepte

(:source lang=php linenum:)
<?php
$connection = mysql_connect('localhost', 'root', '');

if ($connection === FALSE)
  handle_error();

if (mysql_select_db('test', $connection) === FALSE)
  handle_error(mysql_error($connection));

$result = mysql_query(
  'SELECT spalte FROM tabelle',
  $connection
);

if ($result === FALSE)
  handle_error(mysql_error($connection));

while(($row = mysql_fetch_assoc($result)) !== FALSE) {
  // ...
}

mysql_free_result($result);

function handle_error($message = '') {
  // ...
}
?>

Wünschenswert wäre eine wiederverwendbare Einheit, die die Datenbankverbindung und die mit ihr assoziierten Operationen zusammenfasst. Ist sie einmal mit den Verbindungsparametern initialisiert, kümmert sich diese Einheit für ihren Verwender unsichtbar um buchhalterische Aufgaben wie Verbindungsauf- und -abbau, Verarbeitung von Anfragen und dergleichen mehr.

Um eine solche Einheit programmiertechnisch umsetzen zu können, bedarf es eines zusätzlichen Sichtbarkeitsbereiches (englisch: scope) neben dem von Hauptprogramm und Prozedur. Dieser neue Sichtbarkeitsbereich soll Variablen und Prozeduren, die diese Variablen manipulieren, in einer Einheit zusammenfassen.

(Quelle: OOP, 28.02.2008)

 
 
February 27, 2008, at 11:48 PM by chueser -
Changed line 3 from:

[ Zurück: Kapselung von lokalen Besonderheiten? | Index: Übersicht | Vor: ]

to:
 
 
February 27, 2008, at 11:46 PM by chueser -
Changed lines 3-4 from:
to:

[ Zurück: Kapselung von lokalen Besonderheiten? | Index: Übersicht | Vor: ]

 
 
February 25, 2008, at 01:48 AM by chueser - + links
Changed lines 80-82 from:
to:

(Quelle: Factory Method, 24.02.2008)

Changed lines 127-128 from:
to:

(Quelle: Factory Method, 24.02.2008)

Added lines 189-191:

(Quelle: Singleton, 24.02.2008)

 
 
February 25, 2008, at 01:45 AM by chueser -
Added line 225:

(Quelle: Abstract Singleton, 24.02.2008)

 
 
February 25, 2008, at 01:38 AM by chueser - + factory
Added lines 80-123:

Eine Factory ist also ein Hilfsmittel zur Erzeugung von Objekten. Sie wird verwendet, wenn die zur Generierung des Objekts verwendete Klasse erst zur Laufzeit bekannt ist.

Ein weiteres Beispiel für eine Factory Methode könnte folgendermaßen aussehen:

(:source lang=php linenum:)
class Meine_Klasse
{
    static public function factory($className, $params = null)
    {
        if (! is_string($className) || ! strlen($className)) {
            throw new exception(
                'Die zu ladende Klasse muss in einer Zeichenkette benannt werden');
        }

        require_once $className . '.php';
        return new $className($params);
    }
}

$params = array(
    'param1' => null,
    'param2' => null,
);

$object = Meine_Klasse::factory('test_klasse_konkret', $params);

In diesem Beispiel ist MDB2::connect() die factory-Methode und liefert ein Datenbank-Verbindungs-Objekt oder im Fehlerfall ein PEAR-Error-Objekt zurück.

(:source lang=php linenum:)
require_once 'MDB2.php';

$dsn = 'pgsql://someuser:apasswd@localhost/thedb';
$options = array(
    'debug' => 2,
    'result_buffering' => false,
);

$mdb2 = MDB2::connect($dsn, $options);
if (PEAR::isError($mdb2)) {
    die($mdb2->getMessage());
}

// ...

$mdb2->disconnect();
 
 
February 25, 2008, at 01:34 AM by chueser -
Changed line 143 from:

Abstraact Singleton

to:

Abstract Singleton

 
 
February 25, 2008, at 01:33 AM by chueser - + abstr singleton
Added lines 142-179:

Abstraact Singleton

Ab PHP 5.3 möglich, da get_called_class() erst dort verfügbar:

(:source lang=php linenum:)
error_reporting(E_ALL|E_STRICT);  
ini_set('display_errors', true); 

abstract class Singleton 
{  
    private static $instances = array(); 

    final public static function getInstance()  
    {  
        $class = get_called_class(); 
        if (empty(self::$instances[$class])) { 
            $rc = new ReflectionClass($class);
            self::$instances[$class] = $rc->newInstanceArgs(func_get_args()); 
        } 
        return self::$instances[$class]; 
    }  

    protected function __construct() 
    {} 

    final private function __clone() 
    {} 
}  

class ConcreteSingleton extends Singleton  
{ 
    protected function __construct($string, Array $array) 
    { 
        echo __METHOD__ . '(' . $string . ', ' . print_r($array, true) . ')'; 
    } 
}  

$test = ConcreteSingleton::getInstance('Hello World', array(1,2,3));
 
 
February 24, 2008, at 06:34 PM by chueser - + singleton
Added lines 84-142:

Das Singleton-Muster wird in Situationen angewandt, in denen nur eine einzige Instanz einer Klasse benötigt wird. Das bekannteste Beispiel dafür ist eine Datenbankverbindung. Mit diesem Muster implementiert macht der Entwickler diese eine Instanz einfach erreichbar von vielen anderen Objekten.

Example: Singleton Function

(:source lang=php linenum:)
<?php
class Example
{
   // Hold an instance of the class
   private static $instance;

   // A private constructor; prevents direct creation of object
   private function __construct() 
   {
       echo 'I am constructed';
   }

   // The singleton method
   public static function singleton() 
   {
       if (!isset(self::$instance)) {
           $c = __CLASS__;
           self::$instance = new $c;
       }

       return self::$instance;
   }

   // Example method
   public function bark()
   {
       echo 'Woof!';
   }

   // Prevent users to clone the instance
   public function __clone()
   {
       trigger_error('Clone is not allowed.', E_USER_ERROR);
   }

}

?>

Dies erlaubt eine einzige Instanz der Klasse Example aufzurufen.

(:source lang=php linenum:)
<?php
// This would fail because the constructor is private
$test = new Example;

// This will always retrieve a single instance of the class
$test = Example::singleton();
$test->bark();

// This will issue an E_USER_ERROR.
$test_clone = clone($test);

?>
 
 
February 24, 2008, at 06:28 PM by chueser -
Changed line 54 from:

[@<?php

to:

(:source lang=php linenum:)[@<?php

Changed line 72 from:

[@<?php

to:

(:source lang=php linenum:)[@<?php

 
 
February 24, 2008, at 06:24 PM by chueser - + factory
Added lines 49-80:

Das Factory-Muster erlaubt die Instanziierung eines Objektes zur Laufzeit. Da es für die "Herstellung" von Objekten zuständig ist, wird es Factory-Muster gennant.

Beispiel: Factory Method

<?php
class Example
{
   // The factory method
   public static function factory($type)
   {
       if (include_once 'Drivers/' . $type . '.php') {
           $classname = 'Driver_' . $type;
           return new $classname;
       } else {
           throw new Exception ('Driver not found');
       }
   }
}
?>

Diese Methode definiert in einer Klasse erlaubt das Laden von Treibern "on the fly". Wenn z.B. die Beispiel Klasse eine Datenbank Abstraktionsklasse wäre, so könnten die Treiber MySQL und SQLite wie folgt geladen werden:

<?php
// Load a MySQL Driver
$mysql = Example::factory('MySQL');

// Load a SQLite Driver
$sqlite = Example::factory('SQLite');
?>
 
 
February 24, 2008, at 05:46 PM by chueser - + spell checking
Changed lines 23-29 from:

Dieses Pattern ist insbesondere Vorteilhaft für die Kapselung von lokalen Besonderheiten bei verteilter Weiterentwicklung von Open-Source Applikationen.

Reine Vererbung ist zwar gut wiederverwendbar, jedoch schlecht wartbar, was leicht zu Unklarheiten in der Vererbungshierarchie führen kann, falls neues Hinzugefügt wird.

Interface Implementierungen in Unterklassen macht es möglich spezielles Verhalten für jede Unterklasse zu definieren, jedoch auf Kosten von Code-Redundanz und Code-Douplizierung, was den Code schlecht zu warten macht.

Durch das Strategy-Pattern, welches ein Verhaltensmuster ist, werden veränderbare Teile aus den konstanten Teilen herausgezogen und Interfaces (Supertyp) definiert. Die Klassen der Verhaltensweisen implementieren die Interfaces. Andere Klassen können nun diese Verhaltensweisen in sich aufnehmen und in Form einer Komposition delegieren. Getter- und Setter-Methoden in diesen Methoden machen den dynamischen Austausch zur Laufzeit möglich. Dadurch sind Verhaltensweisen leicht austauschbar und wiederverwendbar. Der resultierende Code ist gut wiederverwendbar und wartbar. Demnach erfüllt das Strategy-Muster die drei oben aufgeführten Entwurfsprinzipien.

to:

Reine Vererbung ist zwar im Hinblick auf Wiederverwendbarkeit gut, jedoch schlecht im Hinblick auf Wartbarkeit. Leicht kann es zu Unklarheiten in der Vererbungshierarchie kommen, falls Neues hinzugefügt wird.

Interface Implementierungen wiederum machen es zu jeder Zeit möglich spezielles Verhalten zu definieren und der Typhierarchie hinzuzufügen, jedoch geschieht dies auf Kosten von Code-Redundanz und Code-Duplizierung, was zur Folge hat, dass der Code schlecht zu warten ist.

Durch das Strategy-Pattern, welches ein Verhaltensmuster ist, werden veränderbare Teile aus den konstanten Teilen herausgezogen und Interfaces (Supertyp) definiert. Die Klassen der Verhaltensweisen implementieren die Interfaces. Andere Klassen können nun diese Verhaltensweisen in sich aufnehmen und in Form einer Komposition delegieren. Getter- und Setter-Methoden in diesen KLassen machen den dynamischen Austausch zur Laufzeit möglich. Dadurch sind Verhaltensweisen leicht austauschbar und wiederverwendbar. Der resultierende Code ist gut wiederverwendbar und wartbar. Demnach erfüllt das Strategy-Muster die drei oben aufgeführten Entwurfsprinzipien.

 
 
February 24, 2008, at 05:28 PM by chueser - + class diagram SP
Added lines 35-41:

––

 
 
February 23, 2008, at 09:29 PM by chueser -
Changed lines 23-26 from:

Dieses Pattern ist insbesondere Vorteilhaft für die Kapselung von lokalen Besonderheiten.

Reiner Vererbung ist zwar gut wiederverwendbar, jedoch schlecht wartbar, was leicht zu Unklarheiten in der Vererbungshierarchie führen kann, falls neues Hinzugefügt wird.

to:

Dieses Pattern ist insbesondere Vorteilhaft für die Kapselung von lokalen Besonderheiten bei verteilter Weiterentwicklung von Open-Source Applikationen.

Reine Vererbung ist zwar gut wiederverwendbar, jedoch schlecht wartbar, was leicht zu Unklarheiten in der Vererbungshierarchie führen kann, falls neues Hinzugefügt wird.

Changed lines 29-30 from:

Durch das Strategy-Pattern wird für Veränderbare Teile ein Interface definiert, welches Klassen bestimmtes Verhalten implementiert wird. Andere Klassen können nun diese Verhaltensweisen in sich aufnehmen in Form einer Komposition. Dadurch sind Verhaltensweisen leicht austauschbar und wiederverwendbar. Der resultierende Code ist gut wiederverwendbar und wartbar. Demnach erfüllt das Strategy-Muster die drei oben aufgeführten Entwurfsprinzipien.

to:

Durch das Strategy-Pattern, welches ein Verhaltensmuster ist, werden veränderbare Teile aus den konstanten Teilen herausgezogen und Interfaces (Supertyp) definiert. Die Klassen der Verhaltensweisen implementieren die Interfaces. Andere Klassen können nun diese Verhaltensweisen in sich aufnehmen und in Form einer Komposition delegieren. Getter- und Setter-Methoden in diesen Methoden machen den dynamischen Austausch zur Laufzeit möglich. Dadurch sind Verhaltensweisen leicht austauschbar und wiederverwendbar. Der resultierende Code ist gut wiederverwendbar und wartbar. Demnach erfüllt das Strategy-Muster die drei oben aufgeführten Entwurfsprinzipien.

Deleted line 31:
Changed lines 34-35 from:
to:
  • Klasse Ente delegiert nun die Verhaltensweisen und ruft in den eigenen Methoden die jeweils entsprechende Methode der Verhaltensweisen (des Supertyps) auf statt diese selbst zu implementieren. Der dynamisch Austausch von Verhaltensweisen zur Laufzeit wird durch Getter- und Setter-Methoden ermöglicht.
Deleted line 37:
 
 
February 23, 2008, at 11:57 AM by chueser -
Changed lines 1-4 from:

Design-Pattern & Anti-Pattern

[ Zurück: Tests? | Index: Übersicht | Vor: UML? ]

to:

OO-Design & Entwurfsmuster

[ Zurück: Prozessmodell zur Stud.IP-Entwicklung | Index: Übersicht | Vor: Kapselung von lokalen Besonderheiten?

 
 
February 23, 2008, at 11:46 AM by chueser -
Added lines 17-18:

(siehe auch: Entwurfsmuster)

 
 
February 23, 2008, at 11:44 AM by chueser -
Added lines 71-94:

Außerirdische Spinnen

Ein Design, das den Namen nicht verdient. Sehr gesprächige, kommunikative Objekte, die sich alle gegenseitig kennen. Überhaupt keine Nutzung von Entwurfsmustern. Bei n Objekten gibt es n*(n-1)/2 Kommunikationspaare (englisch Alien spiders).

Gasfabrik

Eine Gasfabrik ist ein unnötig komplexes Systemdesign für eine recht einfache Aufgabenstellung. Beispielhafte Nutzung: „Ich wollte eine Softwarelösung haben, keine Gasfabrik.“ (englisch Gas factory)

Gottobjekt

Ein Objekt, das zu viel weiß respektive zu viel macht. Aufteilung nach Verantwortlichkeiten, Kapselung und Einhaltung von Entwurfsmustern helfen diesem Muster zu begegnen. Auch als Gottklasse (God class) und Blob bekannt (englisch God object).

Innere Plattform-Effekt

Ein System besitzt derartig weitreichende Konfigurationsmöglichkeiten, dass es letztlich zu einer schwachen Kopie der Plattform wird, mittels der es gebaut wurde. Ein Beispiel sind "flexible" Datenmodelle, die auf konkrete (anwendungsbezogene) Datenbanktabellen verzichten und statt dessen mittels allgemeiner Tabellen eine eigene Verwaltungsschicht für die Datenstruktur implementieren. Derartige Systeme sind typischerweise schwer zu beherrschen und leiden unter erheblichen Performanceproblemen. (englisch Inner platform effect)

Spaghetti-Code

Eine sehr kompakte Systemstruktur, deren Kontrollfluss einem Topf Spaghetti ähnelt.

Sumo-Hochzeit

Ein Fat Client ist unnatürlich stark abhängig von der Datenbank. In der Datenbank ist sehr viel Logik in Form der datenbankeigenen Programmiersprache positioniert, in Oracle zum Beispiel mit PL/SQL. Die ganze Architektur ist sehr unflexibel. Soll die Anwendung zu einer Internet-Anwendung migriert oder die Datenbank gewechselt werden, so müssen auf beiden Schichten (Client und Datenhaltung) viele Bereiche neu entwickelt werden. Die Systeme sind nicht entkoppelt (englisch Sumo Marriage).

 
 
February 23, 2008, at 11:40 AM by chueser -
Changed lines 83-87 from:

Switch-Statement in Java: Verhalten von Objekten gemäß ihrem Status (state) sollten mit Statusobjekten und dem state-Pattern gesteuert werden, nicht mit konditionalem Code.

to:

Switch-Statement

Verhalten von Objekten gemäß ihrem Status (state) sollten mit Statusobjekten und dem state-Pattern gesteuert werden, nicht mit konditionalem Code.

 
 
February 23, 2008, at 11:39 AM by chueser - + anti pattern
Changed lines 70-93 from:

Anti-Pattern

to:

Anti-Pattern

Zwiebel

Neue Funktionalität wird um (oder über) die alte gelegt. Häufig zu beobachten, wenn ein Entwickler ein Programm erweitern soll, welches er nicht geschrieben hat. Der Entwickler möchte oder kann die bereits existente Lösung nicht komplett verstehen, und setzt seine neue Lösung einfach drüber. Dies führt mit einer Vielzahl von Versionen und unterschiedlichen Entwicklern über die Jahre zu einem Zwiebel-System (englisch Onion).

Programmierung mittels Copy & Paste

Der Programmierer entwickelt den Code nicht neu, sondern bedient sich bereits existenter Quelltexte, aus denen er Passagen herauskopiert. Die Gefahr ist sehr groß, dass er Fehler mitkopiert oder die Kopie für den neuen Bereich nicht optimal einsatzbereit ist. Der Entwickler reflektiert weniger über sein Programm, als wenn er jede Zeile selbst entwickeln würde. Fehleranfälliges Vorgehen, wenn der Entwickler nicht weiß, was er eigentlich macht. Weit verbreitet (englisch Copy And Paste Programming). Dieses Vorgehen ist auch unter dem Begriff „Verteile und beherrsche nicht“ (als zynische Anspielung auf das informatische Konzept des "divide and conquer" / divide et impera / Teile und herrsche) bekannt.

Lavafluss

Beschreibt den Umstand, dass in einer Anwendung immer mehr „toter Quelltext“ herumliegt. Dieser wird nicht mehr genutzt. Statt ihn zu löschen, werden im Programm immer mehr Verzweigungen eingebaut, die um den besagten Quelltext herumlaufen oder auf ihn aufbauen (englisch Lava flow). Switch-Statement in Java: Verhalten von Objekten gemäß ihrem Status (state) sollten mit Statusobjekten und dem state-Pattern gesteuert werden, nicht mit konditionalem Code.

Magic Values

Hierbei handelt es sich um Daten (Literale) mit besonderer Bedeutung. Sie sind hartkodiert (hardcoded) und nur mit besonderem Wissen über die konkrete Verwendung zu verstehen. Werte sollten zentral als Variable definiert werden, optimalerweise als typsicheres Objekt (typesafe).

Reservierte Wörter

Die Verwendung von reservierten Wörtern in SQL-Anweisungen kann zu schwer zu findenden Fehlern führen. Ein Austausch der Datenbank eines Herstellers gegen ein anderes Produkt kann dazu führen, dass weitere Namen als reserviert betrachtet werden müssen.

(Quelle: AntiPattern, 22.02.2008)

 
 
February 23, 2008, at 11:30 AM by chueser -
Added lines 2-4:

[ Zurück: Tests? | Index: Übersicht | Vor: UML? ]

 
 
February 23, 2008, at 11:28 AM by chueser -
Changed lines 29-30 from:
  • Klasse Ente hat die Verhaltensweisen Flugverhalten und Quakverhalten und die Unterklassen StockEnte, MoorEnte, GummiEnte, LockEnte.
  • Flugverhalten und Quakverhalten ist jeweils gekapselt durch Interface und dessen Implementierungen. Diese Implementierungen sind leicht austauschbar.
to:
  • Klasse Ente nimmt als Komposition die Supertypen der Verhaltensweisen Flugverhalten und Quakverhalten auf. Klasse Ente hat die Unterklassen StockEnte, MoorEnte, GummiEnte, LockEnte.
  • Flugverhalten und Quakverhalten ist jeweils gekapselt durch Interfaces (Supertyp) und deren Implementierungen. Diese Implementierungen sind leicht austauschbar.
 
 
February 23, 2008, at 11:24 AM by chueser -
Changed lines 7-9 from:
  1. Kapselung: Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  2. Interface: Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
to:
  1. Kapselung von veränderbaren Teilen: Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  2. Supertyp durch Interface oder abstrakte Superklasse: Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
 
 
February 23, 2008, at 11:19 AM by chueser -
Changed lines 7-11 from:
  1. Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  2. Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
  3. Ziehen Sie Komposition (HAT-EIN) der Vererbung (IST-EIN) vor.
to:
  1. Kapselung: Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  2. Interface: Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
  3. Komposition: Ziehen Sie Komposition (HAT-EIN) der Vererbung (IST-EIN) vor.
 
 
February 23, 2008, at 11:18 AM by chueser - + pattern
Changed lines 5-10 from:
  • Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  • Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
  • Ziehen Sie Komposition der Vererbung vor.
to:

Die OO-Basics (Abstraktion, Kapselung, Polymorphismus, Vererbung) machen nicht allein ein gutes OO-Design. Gute Entwürfe sind wiederverwendbar, erweiterbar und wartbar.

  1. Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  2. Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
  3. Ziehen Sie Komposition (HAT-EIN) der Vererbung (IST-EIN) vor.
Added lines 20-31:

Reiner Vererbung ist zwar gut wiederverwendbar, jedoch schlecht wartbar, was leicht zu Unklarheiten in der Vererbungshierarchie führen kann, falls neues Hinzugefügt wird.

Interface Implementierungen in Unterklassen macht es möglich spezielles Verhalten für jede Unterklasse zu definieren, jedoch auf Kosten von Code-Redundanz und Code-Douplizierung, was den Code schlecht zu warten macht.

Durch das Strategy-Pattern wird für Veränderbare Teile ein Interface definiert, welches Klassen bestimmtes Verhalten implementiert wird. Andere Klassen können nun diese Verhaltensweisen in sich aufnehmen in Form einer Komposition. Dadurch sind Verhaltensweisen leicht austauschbar und wiederverwendbar. Der resultierende Code ist gut wiederverwendbar und wartbar. Demnach erfüllt das Strategy-Muster die drei oben aufgeführten Entwurfsprinzipien.

Bsp:

  • Klasse Ente hat die Verhaltensweisen Flugverhalten und Quakverhalten und die Unterklassen StockEnte, MoorEnte, GummiEnte, LockEnte.
  • Flugverhalten und Quakverhalten ist jeweils gekapselt durch Interface und dessen Implementierungen. Diese Implementierungen sind leicht austauschbar.
 
 
February 23, 2008, at 03:26 AM by chueser - + pattern headers
Added lines 20-51:

Das Decorator-Muster

Das Factory-Muster

Das Singleton-Muster

Das Command-Muster

Das Adapter & Facade Muster

Das Template Methode-Muster

Das Iterator & Composite Muster

Das State-Muster

Das Proxy-Muster

zusammengesetzte Muster

 
 
February 22, 2008, at 11:24 PM by chueser -
Changed line 7 from:
  • '''Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
to:
  • Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
 
 
February 22, 2008, at 11:24 PM by chueser -
Changed lines 5-9 from:

Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.

Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.

Ziehen Sie Komposition der Vererbung vor.

to:
  • Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.
  • '''Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.
  • Ziehen Sie Komposition der Vererbung vor.
 
 
February 22, 2008, at 11:23 PM by chueser - + design pattern
Added lines 1-21:

Design-Pattern & Anti-Pattern

Entwurfsprinzipien

Identifizieren Sie die Aspekte Ihrer Anwendung, die sich ändern können, und trennen Sie sie von denen, die konstant bleiben.

Programmieren Sie auf eine Schnittstelle, nicht auf eine Implementierung.

Ziehen Sie Komposition der Vererbung vor.

Design-Pattern

Das Strategy-Muster

Das Strategy Pattern definiert eine Familie von Algorithmen, kapselt sie einzeln und macht sie austauschbar. Das Strategy-Muster ermöglicht es, den Algorithmus unabhängig von den Clients die ihn einsetzen, variieren zu lassen.

Dieses Pattern ist insbesondere Vorteilhaft für die Kapselung von lokalen Besonderheiten.

Das Observer-Muster

Anti-Pattern

 

 

Source: Basis-Wiki-Hilfe | Last change: August 23, 2008, at 12:31 AM, chueser | Local view: Basis-Hilfe