Re: [PHP] Decorator with public methods
On Sat, Dec 27, 2008 at 12:02 AM, Larry Garfield la...@garfieldtech.comwrote: On Friday 26 December 2008 11:06:07 pm Nathan Nobbe wrote: to summarize, using your example above, i would most liely add doThings() to Baz, or create another decoration interface for doThings() if you plan on using the Bar implementation of doThings() in many places, interface G { function doThings(); } class Bar extends Decorator implements G { function doThings() { // concreate implementation } } class Baz implements F, G { // recycle Bar::doThings() public function doThings() { return $this-foo-doThings(); } public function doOtherThings() {} } i appologize if this response is long winded, but im a big fan of the decorator, and ive actually got some pretty slick code in production in the photobucket code that uses the decorator pattern :D it took about 2 months to code, and i leraned a lot about some of the practical aspects of decroration, specifically within the realm of php. i know i repeated a few things there, but i felt it neccessary to better explain myself. -nathan Thanks, Nathan. Unfortunately, what you describe is impossible. It requires me to know all the possible decorators ahead of time and implement the interface for each in each decorator, at which point I've gotten no benefit at all over just putting everything in the original base class in the first place. That defeats the purpose of decorators if I can't come up with a new one a month from now and not have to modify any of the existing code. i see it more on a need-to-expose basis. for example, why not, when presented w/ the current problem, just expose, Baz::doThings() now, b/c you know you need it in Baz instances ? later if you find theres another method thats in Bar that isnt yet exposed, you just go in and add a similar wrapper for it in Baz at that time. this is a typical flow in a layered architecture in my experience; often times controllers are revised to expose a new entry point for something on the backend that previously was unavailble from the front end, just as an example. i only pull interfaces into the equation when i what to bring a group of things together, it certainly isnt necessary to define a second interface, but i was just trying to highlight doThings() as your example essentailly had 2 layers of decorators, G(F(Foo)), as i saw it. from a design perspective, yes, id probly not define G unless / until i saw a need to join a group of classes explicitly. -nathan
Re: [PHP] Decorator with public methods
On Saturday 27 December 2008 2:49:22 am Nathan Nobbe wrote: Thanks, Nathan. Unfortunately, what you describe is impossible. It requires me to know all the possible decorators ahead of time and implement the interface for each in each decorator, at which point I've gotten no benefit at all over just putting everything in the original base class in the first place. That defeats the purpose of decorators if I can't come up with a new one a month from now and not have to modify any of the existing code. i see it more on a need-to-expose basis. for example, why not, when presented w/ the current problem, just expose, Baz::doThings() now, b/c you know you need it in Baz instances ? later if you find theres another method thats in Bar that isnt yet exposed, you just go in and add a similar wrapper for it in Baz at that time. this is a typical flow in a layered architecture in my experience; often times controllers are revised to expose a new entry point for something on the backend that previously was unavailble from the front end, just as an example. Because this is for a shipping application, not an in-house app where a month from now I can go back and adjust the interface for every class. (It's open source, but still has a stable release version.) I need to be able to add additional wrapping decorators *without* any modifications to the underlying object or its interfaces. As I said, the interface problem is solvable by having explicit delegating methods in the base decorator class and then only using __call() for nested decorators, which will be a much much smaller portion of the time. It's the performance cost of __call() and the extra call stack layers that are my concern at the moment. -- Larry Garfield la...@garfieldtech.com -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] Decorator with public methods
On Dec 26, 2008, at 7:53 PM, Larry Garfield wrote: 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. If I understand what you are wanting to accomplish, perhaps a simpler solution: Whenever I create a function in a core-level class, I add pre- and post- execution language to it. For example: class foo_bar { function foo () { if (!on_foo()) { return FALSE; } // normal foo code // normal foo code // normal foo code // normal foo code // normal foo code if (!more_foo()) { return FALSE; } } function on_foo() { return TRUE; } function more_foo() { return TRUE; } } -- then in the child class: class child_bar extends foo_bar { function on_foo() { // runtime code // runtime code // runtime code // runtime code return TRUE; // (or FALSE) } } Ken -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] Decorator with public methods
On Sat, Dec 27, 2008 at 12:56 PM, phphelp -- kbk phph...@comcast.netwrote: On Dec 26, 2008, at 7:53 PM, Larry Garfield wrote: 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. If I understand what you are wanting to accomplish, perhaps a simpler solution: Whenever I create a function in a core-level class, I add pre- and post- execution language to it. For example: class foo_bar { function foo () { if (!on_foo()) { return FALSE; } // normal foo code // normal foo code // normal foo code // normal foo code // normal foo code if (!more_foo()) { return FALSE; } } function on_foo() { return TRUE; } function more_foo() { return TRUE; } } -- then in the child class: class child_bar extends foo_bar { function on_foo() { // runtime code // runtime code // runtime code // runtime code return TRUE; // (or FALSE) } } more commonly referred to as the template method. -nathan
Re: [PHP] Decorator with public methods
On Sat, Dec 27, 2008 at 12:35 PM, Larry Garfield la...@garfieldtech.comwrote: On Saturday 27 December 2008 2:49:22 am Nathan Nobbe wrote: Thanks, Nathan. Unfortunately, what you describe is impossible. It requires me to know all the possible decorators ahead of time and implement the interface for each in each decorator, at which point I've gotten no benefit at all over just putting everything in the original base class in the first place. That defeats the purpose of decorators if I can't come up with a new one a month from now and not have to modify any of the existing code. i see it more on a need-to-expose basis. for example, why not, when presented w/ the current problem, just expose, Baz::doThings() now, b/c you know you need it in Baz instances ? later if you find theres another method thats in Bar that isnt yet exposed, you just go in and add a similar wrapper for it in Baz at that time. this is a typical flow in a layered architecture in my experience; often times controllers are revised to expose a new entry point for something on the backend that previously was unavailble from the front end, just as an example. Because this is for a shipping application, not an in-house app where a month from now I can go back and adjust the interface for every class. (It's open source, but still has a stable release version.) I need to be able to add additional wrapping decorators *without* any modifications to the underlying object or its interfaces. then you can expose all methods from the underlying layer in the first place (which is basically what happens w/ inheritence, as i mentioned earlier). also, how bad will it be when a month from now you have to make some adjustments to the code? more than likely after the first cut, you may have to change something in more than one layer, but it should be minimal as long as you arent making any major overhauls to the code. you add or change one method in a lower, layer so you need to change it in another place, one layer above, i dont see that being so bad. what you are worried about is the classic case martin fowler put forth, which is a short coming of a layered architecture, namely, in the worst case scenario, a modification at the lowest layer requires modifications to each successive layer. As I said, the interface problem is solvable by having explicit delegating methods in the base decorator class and then only using __call() for nested decorators, which will be a much much smaller portion of the time. It's the performance cost of __call() and the extra call stack layers that are my concern at the moment. there is one semi-micro optimization w/ __call(), and that is to use a variable function rather than call_user_func*. however, as you surely already know, this only works iff you know the number of parameters that need to be passed intot the function which is being wrapped. something like this, function __call($method, $args) { return $this-foo-$method($args[0], $args[1]); } the performance hit from __call() is just the price for a highly dynamic system; but you can have a faster system, at the slight risk of carpel-tunnel :) -nathan
[PHP] Decorator with public methods
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 la...@garfieldtech.com -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP] Decorator with public methods
On Fri, Dec 26, 2008 at 6:53 PM, Larry Garfield la...@garfieldtech.comwrote: 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? the main drawback to decoration vs inheritance is the fact that code is not essentially 'written for you', which results in lots of boilerplate code in decorators, in my experience. what that amounts to, is if you have 10 methods, whereby you only want to alter one of them in a decorator, then w/ decoration youve got 9 additional methods of crappy boilerplate code to write, that you wouldnt have to when extending instead. what is really irritating about the php __call() implementation is that interfaces are not implemented by it. i think this is pretty silly (there may be a solid reason the internals people could mention). what i mean is, intuitively, i would expect class A implements B { public function __call() {} } to work, no matter what B declares. but it doesnt, WEAK, *cough* *cough*.. *sigh* sadly, i think the best solution is just to add bolierplate code in your decorators, such that deocration methods explicitly implement the interrface w/ each successive layer, and not incur the performance overhead of __call(). essentially this is what the language would do for you when extending something. i rarely put methods all on one line elsewhere, but when decorating ill just end up doing something like this /* these are here just to implement interface F */ function doStuff() { return $this-doStuff(); } another alternative is to really buy into the whole 'dynamic' thing, and forget about the interface, roll w/ __call() and take the performance hit, which is more akin to something like youd see in javascript. personally i like to use a medly of the two. i never did much with it, but i suspect C++ templates make decoration nearly as simple as inheritence, and avoid performace overhead of something like __call(). which brings me to the third option, only really for the truely brave php programmer. templating php w/ php; its often done in code generation systems, like propel and other orm's, but basically, you'd have a template w/ skeletons for all the methods and then youd have to run some sort of generation script in order to create concreate classes for actual use. esoteric, and perhaps a little ugly (editors dont really work 'right' when templating php w/ php =/), but the technique is very powerful. to summarize, using your example above, i would most liely add doThings() to Baz, or create another decoration interface for doThings() if you plan on using the Bar implementation of doThings() in many places, interface G { function
Re: [PHP] Decorator with public methods
On Friday 26 December 2008 11:06:07 pm Nathan Nobbe wrote: to summarize, using your example above, i would most liely add doThings() to Baz, or create another decoration interface for doThings() if you plan on using the Bar implementation of doThings() in many places, interface G { function doThings(); } class Bar extends Decorator implements G { function doThings() { // concreate implementation } } class Baz implements F, G { // recycle Bar::doThings() public function doThings() { return $this-foo-doThings(); } public function doOtherThings() {} } i appologize if this response is long winded, but im a big fan of the decorator, and ive actually got some pretty slick code in production in the photobucket code that uses the decorator pattern :D it took about 2 months to code, and i leraned a lot about some of the practical aspects of decroration, specifically within the realm of php. i know i repeated a few things there, but i felt it neccessary to better explain myself. -nathan Thanks, Nathan. Unfortunately, what you describe is impossible. It requires me to know all the possible decorators ahead of time and implement the interface for each in each decorator, at which point I've gotten no benefit at all over just putting everything in the original base class in the first place. That defeats the purpose of decorators if I can't come up with a new one a month from now and not have to modify any of the existing code. -- Larry Garfield la...@garfieldtech.com -- PHP General Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php