|
:: Rants ::
(Not) Applying Design Patterns in PHP
I'm writing this article at the time PHP5 is coming, and there's a noticable increase of interest in Object Oriented Programming in the PHP world. We're seeing PHP programmers digging in OO theory more and more lately. I encourage that. In fact, I've written a few articles on it which were well received.
An important element of OO theory are so called Design Patterns. In case you did not know, design patterns are "proven solutions to common problems", and there's a great book published on them called "Design Patterns" by Gamma, Helm, Johnson, and Vlissides, also refered to as the Gang of Four (GoF). This book describes a number of design patterns and when to apply them, along with UML diagrams and examples written in C++. Design patterns have been helpful to me when I was programming in Java.
Although PHP5 is influenced more and more by Java in regard to OO features, that does not mean PHP5 _is_ Java. PHP is still PHP, and we all know that one of the things that makes PHP is great is its hyperflexibility. This flexibility often makes it possible to do things in a very simplistic way. So simplistic, it's often not comparable to how you would do things in C++ or Java. This makes for an interesting question when you want to do things the OO way in PHP. You can go two ways here.
It might seem that I'm trying to encourage a certain amount of "hackerism" here. Maybe implicitly. I'm just trying to use PHP's features to their full extent, which happens to diminish the need for using design patterns in my (admittedly) strong opinion.
To put some more strength to my opinion, I will oppose a few design patterns against more simple ways to get things done.
Chain of ResponsibilitySay you want to determine what object you want to use based on incoming HTTP parameters. When going the design pattern way you would typically use the Chain of Responsibility pattern. This pattern is sometimes used in the GUI programming world and lets you determine what object to use based on an incoming value. Here is what the implementation for such a pattern could look like when applied in a web environment.
<?php class AbstractHandler { var $successor; function handleRequest($request) { die("You must override handleRequest(request)"); } function setSuccessor(&$successor) { $this->successor =& $successor; } } class ConcreteHandlerA extends AbstractHandler { function ConcreteHandlerA() {} function handleRequest($request) { if($request === 'A') $this->go(); if($this->successor != null) $this->successor->handleRequest($request); } function go() { echo('A is handling it'); } } class ConcreteHandlerB extends AbstractHandler { function ConcreteHandlerB() {} function handleRequest($request) { if($request === 'B') $this->go(); if($this->successor != null) $this->successor->handleRequest($request); } function go() { echo('B is handling it'); } } class ConcreteHandlerC extends AbstractHandler { function ConcreteHandlerC() {} function handleRequest($request) { if($request === 'C') $this->go(); if($this->successor != null) $this->successor->handleRequest($request); } function go() { echo('C is handling it'); } } $a = new ConcreteHandlerA(); $b = new ConcreteHandlerB(); $c = new ConcreteHandlerC(); $a->setSuccessor(&$b); $b->setSuccessor(&$c); $a->handleRequest($_GET['param']); ?>
That's of course all nice and abstracted, and the objects in the chain are all capable of either handling the request or passing it on to the next object in the chain. But would you seriously want to do something like this in PHP? I know I wouldn't. I would rather just build a hash of objects and use the incoming parameter to refer to the relevant hashkey. Here's an example.
<?php class AbstractHandler { function go() { die('go must be implemented'); } } class A extends AbstractHandler { function A() {} function go() { echo('A is handling it'); } } class B extends AbstractHandler { function B() {} function go() { echo('B is handling it'); } } class C extends AbstractHandler { function C() {} function go() { echo('C is handling it'); } } $handlers = array( 'A' => new A(), 'B' => new B(), 'C' => new C() ); if(array_key_exists($_GET['param'], $handlers)) $handlers[$_GET['param']]->go(); ?>
Much more obvious, isn't it? It isn't as flexible as the design pattern one because the responsibility layer is not encapsulated inside the objects, but it surely does the trick. I think using the GoF pattern would be overkill here and it might even obfuscate things if you don't have a good UML diagram at hand. That's why I avoid it.
Iterator
I've seen the Iterator pattern applied a few times in PHP. This never made too much sense to me. Often, you really don't need to use it. What the Iterator pattern does is hiding the structure of an aggregate. I don't know about you, but the only aggregate object I tend to use is PHP's array type. Of course I could build my own aggregate objects, but personally I never actually needed to do that to make my application look cleaner and make it more flexible. PHP's array type is very flexible on itself. In a language such as Java, using Iterators makes more sense, since there are many aggregate objects you would naturally choose from, such as Vector, HashMap etc. Such aggregates don't really have a place in PHP world. PHP has a myriad of built-in functions to do array operations, and I just use those. I simply don't see the need for creating an Iterator to provide a uniform way of traversing through object stacks. I simply don't need any more traversal options than PHP provides me with.
IsKindOf
The IsKindOf pattern provides a way to determine if an object is derived from any specific class, so that you can downcast it safely to a type of one of its parents. First of all, PHP is a weakly typed language and therefore has no concept of strong typing, especially not when it comes to user-defined "types" (i.e. classes). Surely you can cast to any of PHP's primitive types, but you _cannot_ cast to a user-defined type. That's not necessary, either. You don't need to do that, since you can treat every object any way you want to. PHP is just that flexible, deal with it. Oh, and if for some reason you want to know of what type a class is (derived), just click here to have a look at PHP's set of class-related functions.
The examples I've given you are just a few that I could think of right away. I just wrote this because the PHP world seemed to get a little "pattern-happy" lately. I'm not saying you shouldn't use design patterns (patterns such as Composite or AbstractFactory for example, can very well be useful), but I would not use them if there are more simple ways to find a solution to your PHP problem. And there often are. Clean solutions, too. Where patterns can be pretty resource intensive at times. Don't burn your fingers if you don't have to. Just my 2 cents on the topic.
All content on this website is copyrighted by Leendert Brouwer, unless stated otherwise. |