Semantik im MVC-Pattern
Wer schon häufiger und vor allem auch umfangreicher mit dem MVC-Pattern programmiert hat - sei es mit einem Framework wie dem Zend Framework oder einer Eigenproduktion, sowie bei mir MVCLite - wird wissen, dass es gelegentlich gar nicht so einfach ist, seine Controller zu gestalten.
Was macht es schwierig? Die Interdependenzen.
Warum? Eine Komponente gehört logisch zu einer anderen Komponente.
Was heißt das? Als Beispiel führe ich einen Webshop an. Webshop ist die
gesamte Komponente. In dieser Komponente gibt es weitere Komponenten
wie z.B. die Artikel oder den Warenkorb. Zu diesen Artikeln sollen
allerdings auch Rezensionen geschrieben werden können, die dann
wiederum bewertet werden können (siehe Amazon).
Wie wird so was ermöglicht? Ich versuche es mal darzustellen.
+--+ Webshop
|
+--+ Artikel
| |
| +--+ Rezensionen
| |
| +--+ Bewertungen
|
+--+ Warenkorb
Hier sieht man die Struktur der Unterkomponenten. Zu jeder Komponente gehören einige Aktionen, die man ausführen kann:
- Artikel
- Erstellen/Ändern
- Löschen
- Anzeigen
- Rezensionen
- Erstellen/Ändern
- Löschen
- Anzeigen
- Bewertungen
- Abgeben
- Warenkob
- Artikel hinzufügen und entfernen
- Bestellung aufgeben
Dies ist nur eine kurze Liste und zeigt nur einige der wichtigsten Möglichkeiten an. Wie würde so was im Controller aussehen: Ich stelle es mir mal vor:
public function addArticleAction ();
[...]
public function addArticleReviewAction ();
[...]
public function addArticleReviewRatingAction ();
[...]
Natürlich kann man einige Methoden auch besser benennen, aber das geht leider nicht immer. Daraus entstehen unelegante URLs:
/webshop/addArticle
[...]
/webshop/addArticleReview
[...]
/webshop/addArticleReviewRating
[...]
Die finde ich nicht nur sehr unleserlich, sondern auch sehr
verwirrend. Dennoch müssen die URLs semantisch sein. Aber es geht nicht
nur um die Schönheit der URLs und deren Methodennamen. Nein, es hat
direkten Einfluss auf den Code. Wenn ich alles in den
Webshop-Controller implementiere, dann wird der Controller riesig mit
mehreren tausend Zeilen Code - was natürlich immer vom Programmierer
und dem tatsächlich Umfang zusammenhängt -, was die unliebsame Tendenz
zeigen soll.
Nebenbei möchte ich anmerken, dass man mittels des Zend Frameworks die "Ebenen" mit den Modulen möglich ist. Dadurch würden die URLs folgendermaßen aussehen:
/webshop/article/add
[...]
/webshop/article/addReview
[...]
/webshop/article/addReviewRating
[...]
Besser, aber dennoch nicht perfekt. Die Kapselung ist definitiv
besser, aber es löst das Problem einfach noch nicht. Eine weitere Idee,
die mir gekommen ist, sieht folgendermaßen aus:
Vor jeden Großbuchstaben in der Action-Methode wird ein Slash gesetzt.
// ohne die Module im ZF
public function articleAddAction ();
[...]
public function articleReviewAddAction ();
[...]
public function articleReviewRatingAddAction ();
[...]
Daraus enstünden folgende URLs:
/webshop/article/add
/webshop/article/review/add
/webshop/article/review/rating/add
Sieht auf jeden Fall sehr semantisch und lesbar aus. Das
darunterliegende Prinzip wird außerdem nicht so deutlich sichtbar, wie
bei der Verwendung von Methodennamen ohne den Suffix "Action".
Leider löst es das Problem überfüllter Controller nicht.
Mein Ansatz zur Lösung des Problems ist simpel aber dafür sehr
effektiv. Nebenbei muss ich sagen, dass keine Module verwendet werden.
Ich erstelle neue Controller:
- WebshopController
- ArticleController
- ReviewController
- RatingContrller
Der WebshopController ist die Eingangsseite des Webshops und stellt die Seite zusammen, ist für die technische Seite eher nicht von Belang.
Ich erstelle jetzt einfach mal die Signaturen der verschiedenen Controller:
// ArticleController
public function addAction ();
public function deleteAction ();
public function editAction();
public function indexAction ();
public function showAction ();
// ReviewController
public function addAction ();
public function deleteAction ();
public function editAction();
public function indexAction ();
// RatingController
public function addAction ();
public function deleteAction ();
public function indexAction ();
Die URLs nochmal aufzulisten tue ich der Kürze halber nicht.
Nun sind die ganzen Funktionalitäten in eigene Controller ausgelagert
worden. Der logische Zusammenhang ist anhand der URL zwar nicht mehr zu
erkennen, aber dafür wird der Code wartbarer, was meiner Meinung nach
wichtiger sein sollte. Wenn wirklich Bedarf besteht, den logischen
Zusammenhang in der URL ersichtlich zu machen, gibt es nette Features
wie mod_rewrite, die so was ändern können.
Ein Manko ist jedoch, dass es bei einer großen Applikation zu Problemen
kommen kann, wenn man wirklich viele Controller hat, aber so was ist
bei wirklich großen Projekten häufig der Fall.
Durch diesen neuen Aufbau ist es übrigens viel leichter möglich, sie
auf andere Komponenten anzuwenden: Die Wiederverwendbarkeit wird
signifikant gesteigert. Warum keine schriftl. Bewertungen für Verkäufer
einführen mithilfe des ReviewControllers? Das ist jetzt ganz einfach:
// ReviewController
public function addAction ()
{
// $component ist ein über den URL übergebener Parameter
// $data ist der Inhalt der Rezension
switch ($component)
{
default:
case 'Article':
$this->reviewArticle($data);
// korrektes Template setzen
break;
case 'Seller':
$this->reviewSeller($data);
// korrektes Template setzen
break;
}
}
Wie der restliche Code aussieht, wird sicherlich leicht zu erahnen sein, weshalb ich darauf nicht näher eingehen werde.
Wer dennoch Vorschläge bzgl. grundlegenden Verfahren hat, der darf sich gerne bei mir melden. ;-)
Es mag vllt. so aussehen, als ob dieses zu einem überfüllten
ReviewController mutiert, wenn man den ReviewController exzessiv
wiederverwendet, aber die Aussage muss ich relativieren, denn die
Rezensionen sind immer sehr ähnlich aufgebaut, sodass ein Großteil des
Codes wiederverwendet werden kann. Sicherlich gibt es u.U. bei
verschiedenen Komponenten einige spezielle Eigenschaften, aber der
Großteil bleibt doch konsistent.
Wie ich hoffentlich zeigen konnte, ist die Semantik ein wichtiger Punkt
in MVC-Applikationen. Und ich würde mich freuen, wenn ich euch auch
näher bringen konnte, wie man mehr Semantik in den MVC-Pattern
einfließen kann.
Im Übrigen werden ich dieses System demnächst auch auf meiner eigenen
Webseite integrieren. Es handelt sich dabei um die Kommentare, sodass
man endlich meine Blogeinträge kommentieren kann. Natürlich werde ich
dann von dem Aufbau profitieren, sodass man später sicherlich auch
Inhaltsseiten oder andere Komponenten kommentieren kann.