CHAPTER 4 – Implementing the Delegation Pattern Using Reflection

Times arise where a class (One) is supposed to do everything another class (Two) does and more. The preliminary temptation would be for class One to extend class Two, and thereby inheriting all of its functionality. However, there are times when this is the wrong thing to do, either because there isn't a clear semantic is-a relationship between classes One and Two, or class One is already extending another class, and inheritance cannot be used. Under such circum- stances, it is useful to use a delegation model (via the delegation design pat- tern), where method calls that class One can't handle are redirected to class Two. In some cases, you may even want to chain a larger number of objects where the first one in the list has highest priority. The following example creates such a delegator called ClassOneDelegator that first checks if the method exists and is accessible in ClassOne; if not, it tries all other objects that are registered with it. The application can register additional objects that should be delegated to by using the addObject($obj) method. The order of adding the objects is the order of precedence when Class OneDelegator searches for an object that can satisfy the request: class ClassOne { function callClassOne() { print "In Class Onen"; } } class ClassTwo { function callClassTwo() { print "In Class Twon"; } } class ClassOneDelegator { private $targets; function __construct() { $this->target[] = new ClassOne(); } function addObject($obj) { $this->target[] = $obj; } function __call($name, $args) { foreach ($this->target as $obj) { $r = new ReflectionClass($obj); if ($method = $r->getMethod($name)) { if ($method->isPublic() && !$method->isAbstract()) { return $method->invoke($obj, $args); } } } } } $obj = new ClassOneDelegator(); $obj->addObject(new ClassTwo()); $obj->callClassOne(); $obj->callClassTwo(); Running this code results in the following output: In Class One In Class Two You can see that this example uses the previously described feature of overloading method calls using the special __call() method. After the call is intercepted, __call() uses the reflection API to search for an object that can satisfy the request. Such an object is defined as an object that has a method with the same name, which is publicly accessible and is not an abstract method. Currently, the code does nothing if no satisfying function is found. You may want to call ClassOne by default, so that you make PHP error out with a nice error message, and in case ClassOne has itself defined a __call() method, it would be called. It is up to you to implement the default case in a way that suits your needs. 4.6 SUMMARY This chapter covered the more advanced object-oriented features of PHP, many of which are critical when implementing large-scale OO applications. Thanks to the advances of PHP 5, using common OO methodologies, such as design patterns, has now become more of a reality than with past PHP ver- sions. For further reading, we recommend additional material on design pat- terns and OO methodology. A good starting point is www.cetus-links.org, which keeps an up-to-date list of good starting points. Also, we highly recom- mend reading the classic book Design Patterns: Elements of Reusable Object- Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John M. Vlissides.

Post Comment
Login to post comments