Design Patterns bieten elegante Lösung für häufig aufkehrende Probleme in der objektorientierten Programmierung.
Hi,
Ich möchte hier mal einige Design Patterns, auf Deutsch Entwurfsmuster, vorstellen. Dieser Thread
soll auch als Sammelthread für solche Entwurfsmuster sein. Na ja, ich lege mal vor:
Singleton
Oft besteht das Problem, dass man zB bei einer Datenbankklasse ein Konstrukt hat, dass die
Verbindung zur Datenbank herstellt. Aber nun möchte man nicht, dass die Verbindung 2 Mal
ausgeführt wird, sprich, es darf nur einmal ein Objekt dieser Klasse vorhanden sein und es dürfen
auch nicht mehr Objekte erstellt werden.
<?php
class Singleton
{
//Unser Objekt der Klasse Singleton
private static $objekt = null;
//Konstruktor
private function __construct ()
{
//Könnte auch ne Datenbankverbindung sein
printf('Konstruktor der Klasse %s wurde aufgerufen!',
get_class($this) );
}
public static function getInstance ()
{
//Falls noch nich getan, Referenz
//der Klasse auf das Objekt übertragen
if(is_null(self::$objekt))
self::$objekt = new Singleton;
//Gibt die Instanz der Klasse zurück
//und führt den Konstruktor aus
$Objekt = Singleton::getInstance();
//Noch eine Instanz
//Diesmal wird der Konstruktor nicht ausgeführt,
//man erhält genau das gleiche Objekt wie $Objekt
$Objekt2 = Singleton::getInstance();
?>
Fabrik
Eine Fabrik baut Objekte zur Laufzeit zusammen. Das ist ganz praktisch, wenn man zur Laufzeit
des Skriptes noch nicht genau weiß, ob überhaupt und welches Objekt benötigt wird. Welche Klasse
benötigt wird, kann zB erst über Get-Parameter deutlich werden, daher wäre es sinnlos, alle 100
Klassen zu includen, obwohl nur eine benötigt wird.
<?php
class Factory
{
/**
* Konstante, Pfad zu den Klassen
*/
const DIR = '/Pfad/zu/den/Klassen/';
/**
* Baut das Objekt zusammen, mit Fehlerbehandlung
* und Paketmöglichkeiten
*
* @param string $class - Name der Klasse
* @param mixed $param - Parameter der Klasse
* @return objekt - Instanz der Klasse
*/
public static function getClass ($className, $param = null)
{
//Ist der Klassenname gültig?
if(!is_string($className))
exit ('Kein gültiger Klassenname: ' . $className);
//Objekt bauen
$objekt = new $class($param);
}
catch(Exception $e)
{
throw new Exception ('Fehler beim Konstruiren des Objektes<br />
Failes to construct the object: \'' . $className . '\'');
}
}
}
//Anwendung:
//Bindet Klasse: /Pfad/zu/den/Klassen/Datenbak/MySQL.class.php ein
//Und erstelt Objekt der Klasse 'MySQL'
$objekt =& Factory::getClass('Datenbank::MySQL');
//Man könnte in der Fabrik noch ein Singleton einbaun,
//also ein Array, das alle erstellten Objekte speichert.
//Und wenn eins erneut erstellt wird, das passende Objelt
//aus dem Array zurückgeben...
?>
Es gibt noch viele weitere Design Patterns, aber jetzt hab ich grad keine Lust mehr
Hinzu kommt noch:
Observer Pattern
Fassade Pattern
Es gibt noch viele viele mehr, also schön sammeln
Freue mich über jedes hier reingestelltes Design Pattern
Gruß,
Basti
Post wurde schon 6x editiert, das letzte mal am 22.08.2010 um 15:30 von Andavos
so jetzt kommt das Oberver (Beobachter) Pattern. Dabei wird eine Klasse mein Ablauf "beobachtet"
und wenn sich die zu beobachtete Klasse verändern, werden die Beobachter informiert und
reagieren darauf meist mit einer Ausgabe. Das kann
man gut zur Aufgabentrennung der Klassen benutzen, zB im MVC (Model-View-Controller) Konzept anwenden. Dabei ist der Observer für die
Präsentation (view) verantwortlich und die beobachtete Klasse (model) für die Bearbeitung der Daten.
Ok, ich hab mir mal was ganz anschaulisches programmiert im Bezug auf ein Countdown.
//Interface zur Kontrolle
interface isObserver {
}
interface Observable {
}
/**
* Die Klasse, die beobachtet wird
* Um sie eindeutig als zu beobachtene Klasse
* zu definieren, bekommt die das passende Interface
*/
class Countdown implements Observable
{
public $counts = 10; //Zählstand (Status)
private $observers = array(); //Alle Oberserver dieser Klasse
//Fügt dieser Klasse einen neuen Beobachter zu
//Aber nur, wenn das Objekt dem Interface "isObserver" angehört
//attach (deut. = anhängen)
public function attach (isObserver $observer)
{
$this->observers[] = $observer;
}
//dettach (=trennen) schließt einen Observer aus
public function dettach (isObserver $observer)
{
if(in_array($observer, $this->observers))
unset($this->observers[$observer]);
}
//notify (=benachrichtigen) sagt allen
//Observern bescheid
private function notify ()
{
foreach($this->observers as $observer)
$observer->update($this);
}
//Zählt den Status runter
public function startCount ()
{
while($this->counts > 0)
{
$this->counts--; //Um 1 runterzählen
/* Allen Observern sagen, dass wir
unseren Status geändert haben */
$this->notify();
}
}
}
//Unser erster Beobachter, Observer1
class Observer1 implements isObserver
{
//Ausgabe, welche Klasse sich geändert hat
public function update (Observable $subjekt)
{
printf('Der Status der Klasse "%s" hat sich geändert!<br />',
get_class($subjekt) );
}
}
//Beobachter vom Typ 2
class Observer2 implements isObserver
{
//Ausgabe, den aktuellen Status der Klasse ausgeben (Zählstand)
public function update (Observable $subjekt)
{
echo 'Der Zählstand beträgt nun: ' . $subjekt->counts . '<br /><br />';
}
}
//Objekt der zu beobachteten Klasse erzeugen
$wird_beobachtet = new Countdown;
//Dem Countdown einen Beobachter hinzufügen
$beobachter_1 = new Observer1;
$wird_beobachtet->attach($beobachter_1);
//Und den 2ten Beobachter hinzufügen
$beobachter_2 = new Observer2;
$wird_beobachtet->attach($beobachter_2);
//Das Zählen starten
$wird_beobachtet->startCount();
?>
Die Ausgabe vom diese Code:
PHP
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Der Status der Klasse "Countdown" hat sich geändert!
Der Zählstand beträgt nun: 9
Der Status der Klasse "Countdown" hat sich geändert!
Der Zählstand beträgt nun: 8
Der Status der Klasse "Countdown" hat sich geändert!
Der Zählstand beträgt nun: 7
.
.
.
Wenn iwas unverständlich oder unklar ist, immer nachfragen
nun wieder das nächste Entwurfsmuster, das Fassade (Facade) Pattern. Das lässt sich gut
anhand eines Beispiels mit einem Auto erklären. Du bist besitzer eines Autos, das diese Eigenschaften
hat:
<?php
class Auto
{
//Eigenschaften des Autos
private $farbe = 'schwarz';
private $raeder = 5;
private $modell = 'BMW';
//Eine Eigenschaft ausgeben
public function angucken ($eigenschaft)
{
switch ($eigenschaft)
{
case 'farbe': return $this->farbe;
case 'raeder': return $this->raeder;
case 'modell': return $this->modell;
default: return 'Das Auto hat kein ' . $eigenschaft;
}
}
//Mit dem Auto wegfahren :)
public function losfahren ()
{
echo 'Auto ist gestartet.';
}
}
?>
Jetzt will man das Auto aber ausstellen, sodass es jeder angucken kann. Natürlich willst du sein
Auto behalten, also darf keiner damit losfahren können.
Aber das soll ermöglicht werden, ohne irgendetwas an der Klasse "Auto" zu verändern. Dabei hilft dieses
Pattern! Wir lassen das Auto durch ein Vertreter Objekt vertreten, diese hat dann den beschränkten
Zugriff auf das Auto.
Jetzt kann man das Auto nur noch angucken und nicht mehr damit wegfahren Sowas ist ganz
nützlich, wenn man nur beschränkten Zugriff auf eine Klasse erlauben will. Wie man dieses Pattern
noch erweitern kann, zeigt mein nächstes Pattern Decorator. Momentan haben wir die Auto-Klasse
delegriert, also sozusagen verkleinert. Mit dem Decorator können wir die Klasse noch um Funktionen erweitern.
Dazu später mehr
Gruß,
Basti
Post wurde schon 1x editiert, das letzte mal am 12.10.2009 um 00:54 von B.C.
Orginal von electro_dave
und was bringen diese patterns??
Hab ich doch oben beschrieben:
Singleton
Wenn man von einer Klasse nur ein Objekt haben will und nicht mehr. Dies verhindert auch, dass
dadurch der Konstruktor merhmals ausgeführt wird.
Fabrik
Wenn du zur Laufzeit des Skriptes noch nicht weiß, welche Klasse benötigt wird, kann man
dadurch die Klassen ganz gut hinzufügen. Einfach die Factory-Methode aufrufen und schon ist die
Datei includier und wird erstellt. Ist eigentlich ganz praktisch ...
Observer
Wenn du den Status einer Klasse beobachten und auf Statusänderungen reagieren willst. Ein
Beispiel zeit sich deutlich im MVC Konzept (Einfach bei WIkipedia nachlesen )
Fassade
Hab ich doch mit dem Auto gut erklärt oder nicht
Zitat:
Original von yoshi-
nö sind richtig schlecht erklärt, jmd der nicht weißt wofürm man es bruacht versteht nichts
Wenn man nicht in OOP programmiert (wie electro_dace vermutlich) sind diese Patterns
natürlich schwerer zu verstehen, wenn man die ganze OOP-Logik noch nicht kennt.
Was meint du aber micht richtig schlecht erklärt? Ohne Begründung ist das eine richtig schlechte Kritik
Gruß,
Basti
Post wurde schon 1x editiert, das letzte mal am 19.10.2009 um 18:42 von B.C.
Und dann kommen wir bei jedem Pattern mit Vor- und Nachteilen, und so weiter.
Singelton => Lohnt sich nur, wenn eine Klasse einzeln vorhanden sein soll, weil man hiervon nicht mehre Objekte braucht. Typische Beispiele wäre eine Template Klasse oder Datenbankklasse in einem bestimmten Umfeld. (Man kann beides aber auch mehrfach gebrauchen, je nach Programm)
Das hier vorgeführte SingelTon Modell ist noch eine Abschwächung, da hier immer noch geklont werden kann. Man muss also __clone noch verhindern.
Nachteile können entstehen, wenn man, wie oben erwähnt, doch mal von etwas eine neue Instanz braucht. Oftmals macht so was in einer sehr abgeschwächten Form Sinn oder bei einer Grundklasse, die z.B. verschiedene Ansätze für das selbe Problem bereit stellt.
<?php
class MailSender {
protected function __construct() { }
public static function getMailSender($type) {
$className = $type.'MailSender';
$file = 'dir/'.$className.'.class.php';
if (file_exists($file)) {
require_once($file);
if (class_exists($className) {
return new $className();
}
}
return null
}
}
class SmtpMailSender extends MailSender {
}
Es ist, wie gesagt eine abgeschwächte Form mit einer Factory.
Damit sind wir bei den Factories. Auch diese machen nur Sinn, wenn man diese in einem Kontext stellt, wie hier beim MailSender vorgeführt. Eine allgemeine Factory macht selten Sinn, da viele Klassen meist auch Parameter haben, oder bei einer ordentlichen Unterteilung macht es mit diesen keinen Sinn, da zu viele Fälle abgefangen werden müssen. Selbes gilt für Autoload.
Factory macht also nur Sinn in einem Zusammenhang von Klassen, die einen Kontext zusammen bilden. Ich benutze dieses also immer genau dann, wenn ich eine abstrakte Klasse habe, die für eine Aufgabe gedacht ist, diese aber auf verschiedene Arten implementiert werden kann. Hier kann ich die Factory auch immer direkt soweit anpassen, wie es angepasst werden muss.
Autoload nutze ich immer dann, wenn ich eine Klasse habe, die im Konsturktor Parameter haben und vorallem wenn es immer wiederkehrende Klassen sind, wie Exceptions oder auch kleinere Hilfsklassen.
Observer-Klassen machen in wenigen Fällen wirklich Sinn, vorallem in PHP und dem dortigen MVC-Schema. Je nach Schema, was man implementiert sind. WCF kann man sich gerne dafür anschauen. Observer machen Sinn in einem Dienst, in Java nutze ich sowas schon öfters, die einem anderen Thread meldet, wenn sich was im aktuellen Thread ändert. Bei Skriptsprachen ist so was zwar schön einzubauen, aber Sinnvoll ist es weniger. Vorallem weil der Controller meist den Aufruf abarbeitet und dann entsprechend mit den Modell-Klassen umgeht. Fazit für Observer in PHP => Absolut Schwachsinnig, da es eine Skriptsprache ist und der Controller meist diese Aufgabe übernimmt. Das MVC ist hier also eher so zu verstehen
View <-> Controller <-> Model
Bei Skriptsprachen ist das aber auch einfacher anzuwenden, also das übliche MVC-Modell.
Zugriffsbeschränkungen mit Fassaden macht meistens Sinn, wenn man eine Pluginschnittstelle realisieren will, wo andere Klassen auf Zustände reagieren des Objektes, aber das Objekt nicht selbst ändern dürfen. Man übergibt also ein Objekt einem weitern Objekt und reicht dieses dann an die entsprechende Schnittstelle.
Post wurde schon 1x editiert, das letzte mal am 21.10.2009 um 10:14 von Teralios