Decorator with public methods

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Excuse me a moment while I delve into complex OO. :-)

I have an object to which I want to add behavior (methods).  I cannot use 
inheritance here because the object is already of a type or subtype (vis, I am 
already using inheritance for something else), and because I want to be able 
to add multiple types of behavior at runtime.  The normal OO response to this 
situation is the Decorator pattern.

--------------------

interface F {
  function doStuff();
}

class Foo {
  function doStuff() { ... }
}

class Decorator implements F {
  protected $foo;

  function __construct(Foo $foo) {
    $this->foo = $foo;
  }

  function doStuff() {
    $this->foo->doStuff();
  }
}

class Bar extends Decorator {
  function doThings() { ... }
}

$f = new Foo();

$b = new Bar($f);
$b->doStuff();
$b->doThings();

--------------------

OK, great, that's wonderful.  You can also nest such decorators indefinitely, 
provided that they only override methods from Foo and change its behavior, 
then pass on up the chain.  Neat.  What you cannot do, however, is nest 
decorators that have public methods.  That is:

--------------------

class Baz extends Decorator {
  function doOtherThings();
}

$f = new Baz(new Bar(new Foo));
$f->doOtherThings(); // Works.
$f->doStuff(); // Works.
$f->doThings(); // Fail.

--------------------

Now, PHP does have a loophole around this problem in the form of __call().  
Specifically, instead of Decorator wrapping each method of F/Foo directly it 
implements __call():

--------------------
class Decorator {
  protected $foo;

  function __construct(Foo $foo) {
    $this->foo = $foo;
  }

  function __call($method, $args) {
    return call_user_func_array(array($this->foo, $method), $args);
  }
}
--------------------

That should work and allow the code snippet above to run, but it has two 
significant problems:

1) Because Decorator does not directly implement F, you cannot use type 
hinting.

2) __call() and call_user_func_array() are both fairly slow operations, and 
stacking them then becomes a nightmare for performance.

#1 can largely be solved by both directly implementing F *and* implementing 
__call(), but we're still left with the performance problems of #2.  While for 
some uses cases that is OK, it can add up to unpleasant microseconds lost.

Can anyone suggest an alternate solution that has less of a performance hit?

-- 
Larry Garfield
larry@xxxxxxxxxxxxxxxx

-- 
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux