PHP Entwurfsmuster: Fassade (Facade)
Ich weiß ja, dass ihr die Programmierthemen am liebsten mögt (habe ich an der regen Kommentar Beteiligung von gestern ableiten können), deswegen wollen wir uns heute mal wieder den Entwurfsmuster widmen. Beim Fassaden Muster geht es darum Komplexität vom Anwender fern zu halten, sie also zu reduzieren. Zumindest ist das einer der Hauptvorteile.
Ihr kennt das vielleicht auch. Ihr habt eine Klasse, die alles kann. Ihr wollt aber eigentlich nur ein minimales Subset des Interfaces zur Verfügung stellen. Vielleicht wollt ihr auch einfach Funktionalitäten entfernen, weil die Verwendung an dieser Stelle eh verboten ist. Userdaten speichern in der View Schicht zum Beispiel. Ganz fiese Sache. Nehmen wir uns also ein ziemlich einfaches User Modell:
class User
{
public function __construct( $userId )
{
// ...
}
public function getName( )
{
// ...
}
public function store( )
{
// ...
}
}
Wenn ich ein User Objekt jetzt an die View übergebe (oder sie es sich holt), dann sollte die Methode store()
dort nicht mehr aufrufbar sein, denn keine Logik in der Ausgabeschicht. Ganz naiv könnte man ja jetzt sagen, dass wir einfach Ableiten und die Methode store private machen. Geht aber nicht, weil wir da gegen eines der wichtigsten OOP Prinzipien verstoßen. Jede abgeleitete Klasse verhält sich nach außen wie ihre Eltern-Klasse. Mathematisch: Sei q(x) eine beweisbare Eigenschaft von Objekten x des Typs T. Dann soll q(y) für Objekte y des Typs S wahr sein, wobei S ein Untertyp von T ist. Klingt klug ,oder? Ist aber nur abgeschrieben. Werde da trotzdem bald mal näher drauf eingehen.
Aber wieder zu unserem eigentlichen Problem. Wir wollen die store Methode wegbekommen. Das Zauberwort heißt hier, wie in vielen anderen Fällen auch, Delegation.
class ReadOnlyUserFacade
{
private $user;
public function __construct( $userId )
{
$this->user = new User( $userId );
}
public function getName( )
{
return $this->user->getName( );
}
}
Fertig. Wenn ihr jetzt ganz sauber arbeiten wollt, dann baut ihr euch noch zwei Interfaces. iReadOnlyUser
und iUser extends iReadOnlyUser
. Dann passt das nämlich auch wieder wunderbar in das durchgängige OOP Konzept, was wir ja alle so lieben. Ist doch echt eine feine Sache, wir haben die User Klasse nicht angefasst und trotzdem haben wir die Methode wegnehmen können.
Wenn man ein wenig weiter nachgrübelt, dann fallen einem bestimmt noch viele weitere Punkte ein, wo man dieses Entwurfsmuster verwenden kann. Sicherheit, saubere Trennung von view und controller, Reduzierung der Komplexität, und und und.
Präzise zusammengefasst – was will man mehr? Ich freue mich schon auf den Artikel über LSP. 🙂
Hmm. Das bedeutet mein Controller arbeitet mit der User-Klasse, übergibt dem View aber ein Object der Klasse ReadOnlyUserFacade?
Das heißt als Programmierer des Models muss ich mich darauf verlassen, dass der Programmierer des Controllers dem View eine Instanz der richtigen Klasse gibt um die Programmlogik aus dem View fern zu halten, bzw. muss selbst dran denken, wenn ich beide Klassen geschrieben habe/schreibe. Dann kann ich mich aber doch gleich darauf verlassen, dass in der View keine Programmlogik implementiert wird und mit die Fasade sparen, oder? 😛
Außerdem ist mir noch ein Fehler im Code aufgefallen:
$private $user;
Da ist ein $ zu viel.
So, jetzt bin ich ruhig. Versprochen 😉
@ghost: Das war ja nur ein Beispiel. Wie immer muss man sich die Stellen aussuchen, wo das Muster Sinn macht. Ein wenig mehr abstrahieren 😉
Ok, dann betrachte das nicht als Kritik am Entwurfsmuster, sondern als Kritik am Beispiel *fg*
Habs aber verstanden und wenn ich das mal brauche, werde ich dran denken 😉
Danke für den Beitrag.
Mein letztes Pattern Buch ist schon wieder her und
nach der Überschrift wußte ich nicht mehr was „Fassade“ war.
Jetzt ists wieder aufgefrischt. 🙂
Danke für die einfache und schöne Erklärung.
Ein Fehler im Satz: “ Ist dich echt eine feine Sache, “ – ist doch echt…
Mal ein Lob, lese deinen Blog sehr gerne, mich interessieren zuletzt noch mehr die Projekt Themen, wie gestaltet man seine Arbeit, wie gehen andere Programmierer an eine Aufgabenstellung/Problem ran. Wie unterteilen andere Programmierer ihre Schwerpunkte. Usw.
Sonnige Morgengrüße aus Potsdam
„Wie unterteilen andere Programmierer ihre Schwerpunkte. Usw.“
Das kommt ganz darauf an, wo die Schwerpunkte eines Programmierers liegen. Wenn man z. B. so wie ich hin und wieder ein kleines Privates Projekt schreibt oder nur bestehende Module erweitert, dann wird man nicht wirklich viel mit Entwurfsmustr am Hut haben, wobei ich mir momentan auch verstärkt CodeIgniter und das MVC Muster anschaue. Professionelle Programmierer die wirklich komplett mit einer IDE arbeiten haben da schon andere Schwerpunkte wie beispielsweise den korrekten Einsatz der verschiedenen Entwurfsmuster.
Puh, an das Pattern kann ich mich nicht erinnern. Vielleicht liegt es daran, dass ich es unschön finde.
Es wird hier also eine Hülle um eine Klasse gebaut, und einige Funktionen werden einfach weggelassen, um sie zu verstecken.
„Hmm. Das bedeutet mein Controller arbeitet mit der User-Klasse, übergibt dem View aber ein Object der Klasse ReadOnlyUserFacade?“
Genau das finde ich unschön an der Geschichte. Außerdem muss ich dann für sehr viele Klassen solch eine weitere Klasse bauen. Wenn ich an der User-Klasse eine Methode hinzufüge, muss ich auch immer die ReadOnlyUserFacade anfassen (und die entsprechenden Interfaces). Ziemlich viel Mehrarbeit finde ich und der Nutzen ist relativ gering, man könnte auch einfach darauf achten, die store()-Methode nicht zu benutzen an den falschen Stellen.
Vielleicht ist aber auch nur das Beispiel ungünstig gewählt. Wenn ich beispielsweise das Facade-Pattern dazu nutze, mehrere Aufrufe von unterschiedlichen Klassen zusammenzufassen, und damit die Komplexität zu verstecken, ist es schon viel nützlicher.
Beispielsweise:
http://en.wikipedia.org/wiki/Facade_pattern#PHP
Ohne diese Facade-Klasse „Computer“ müßte der Benutzer also manuell die Funktionen aufrufen, und zwar alle in der richtigen Reihenfolge, und ohne einen Aufruf zu vergessen. Mit Hilfe der „Computer“-Klasse kann ich das kapseln und die Nutzung einfacher und fehlerfreier machen.
Ihr werdet schon Beispiele finden, wo es für euch Sinn macht, da bin ich mir sicher 😉
Ich nutze die Facade gerne ähnlich, wie PHP Gangsta dies aufgezeigt hat. Einen bestimmten Aufgabenbereich zu einer verwaltenden Klasse zusammenfassen. So kann man sehr schön aus vorhandenen Bausteinen einen neue Komponente zusammenbasteln, welche wiederum sehr gut in der Anwendung eingesetzt werden kann.
Da hat Nils vielleicht etwas zu stark vereinfacht. Im PHP Design Patterns buch ist das Facade Pattern ganz gut beschrieben.
Die Große Autovermietung, man muss xyz dinge machen um ein Auto zum fuhrpark hinzuzufügen. eine andere abteilung mit eigenen programmierern (zb. abteilung einkauf) hängt ihr eigenes tool an die Anwendung an, die wollen nun Autos hinzufügen. Damit die aber nicht soviel von der Interner der Anwendung wissen müssen baut man eine Facade klasse. Die rufen diese dann nur noch auf. zb. addCar(), und das interner wissen wie nun wirklich eine auto hinzugefügt wird passiert in der facade.
Bei Facade gehts einfach darum aufgaben die eine klasse zwar kann aber wissen um den aufbau erfordert zu vereinfachen.
Bei einem aktuellen projekt zb. gibt es eine User Klasse. Damit ein User wirklich richtig angelegt ist, müssen mehrere methoden dieser klasse aufgerufen werden. in einer facade hab ich mehrere static methoden die das ganze erleichtern, da nur diese aufgerufen werden, und diese wiederum die klassen aus der User Klasse aufrufen.
Servus,
für diesen Einsatzzweck, wie du ihn beschreibst, könnte man doch auch einfach eine Klasse schreiben, die man mit einem Array aller erlaubten Methoden initialisiert, und die dann in __call() den versuchten Aufruf mit der Liste der erlaubten Methoden vergleicht.
Wäre halt die Frage, welche Einsatzgebiete es gibt, in denen dieser Weg nicht mehr funktioniert.
@Sebastian: Überall, wo du mit Interfaces arbeitest und das sollte fast überall sein, kannst du den __get Ansatz leider vergessen. Das klappt dann nämlich nicht mehr.
@Ludwig: „Bei Facade gehts einfach darum aufgaben die eine klasse zwar kann aber wissen um den aufbau erfordert zu vereinfachen.“ Das stimmt nicht so ganz. Es geht darum die Schnittstelle zu vereinfachen. Dies kann auch bedeuten, dass man Funktionalitäten wegnimmt, die man in einem bestimmten Kontext nicht benötigt. Aber wahrscheinlich meinst du das auch.
Wäre in dem Beispiel nicht das Proxy Pattern besser angebracht?
Die anderen 13 Kommentare les ich später, will nur kurz Nils ergänzen: Es geht nicht nur darum, eine Klasse zu vereinfachen, sondern eine ganze Komponente, die auch gerne aus mehreren Klassen bestehen darf. Die versteckt sich eben hinter einer Fassade 😉
Oh ja, bitte mehr solche Beiträge!!!
Das obige Beispiel finde ich persönlich auch schlecht, eignet sich aber gut um auf Dependency Injection (http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection) einzugehen 😉
Im englischen Wikipedia-Artikel gibt es ein besseres Beispiel.
Ich bin noch recht Neu hier, hab die Seite erst vor ein paar Tagen entdeckt. Ist ne echt coole Ressource 😉
Allerdings frag ich mich hier, wo wirklich der Sinn dahinter liegt. Ich finde, dass es nicht wirklich zwingend alles „vereinfacht“ oder „besser strukturiert“ oder „sicherer“ macht, da man trotzdem jederzeit ja die „Hauptklasse(n)“ nutzen kann…
Ich denk in dem obrigen Beispiel macht die Fassade weniger Sinn, wahrscheinlich kann dieses Entwurfmuster erst dann seine Power zeigen, wenn man mehrere Klassen/Funktionen mit der Fassade kombiniert oder komplexe, standardisierbare Abläufe damit vereinfacht.
Mir fällt da grad schon was für mein eigenes Projekt ein… Danke für den Denkanstoß 😉
Ich finde das Beispiel vom Ludwig passender. Allerdings gefällt mir nicht, dass die Methoden darin statisch sind. Eine Facade sollte auch ein ganz normales Objekt sein, d.h. über Instanzmethoden verfügen.
@Enrico: Wie kommst du auf Dependency Injection? Ich seh da keine, und auch keine Notwendigkeit, das zu nutzen. Klär mich auf.
@PHP Gangsta: Im Prinzip geht es darum Abhängikeiten aus den einzelnen Klassen zu entfernen, z.B. Warum muss die Klasse ReadOnlyUserFacade wissen wie die Klasse User instanziert wird? Vielleicht ist das komplizierter und wird über eine Factory, Builder oder whatever gesteuert. Eventuell möchte ich auch das ReadOnlyUserFace auf einer von User abgeleiteten Klasse arbeitet usw.
Beispiel:
class ReadOnlyUserFacade
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
…
}
Aber es ging ja eh um die Facade und gleich alles in ein Beispiel zu packen macht es nur kompliziert. 🙂
Das stimmt natürlich. In dem einfachen Beispiel ging es nur darum einzelne Methoden „zu verstecken“, an den bestehenden Aufrufen und Paramtern sollte sich ja nichts ändern. Deshalb hab ich da keine Dependency gesehen 😉
Denn mit deinem Beispiel hat man nun für ReadOnlyUserFacade und User plötzlich unterschiedliche Konstruktoren. Vorher waren sie noch gleich. Führt evtl. zu Verwirrung.