I have a class that is extended, and its children extended further, an arbitrary number of times. Each extension provides more functionality than its predecessor. The catch is, not all parameters can be provided at initialization time. It is complex enough to warrant passing more configuration options via methods, and then calling some sort of build() or configure() method to ready itself for operation.
The catch is, each class in the hierarchy needs a chance to configure itself, and i开发者_Go百科t needs to cascade from the parent to all of the children.
I successfully do this below, but this solution requires that each class remember to call it's parent's method, otherwise it breaks. I want to remove that responsibility from those who might forget to do such.
How do I modify this code to achieve this?
<?php
class A {
function configure() {
print("I am A::configure() and provide basic functionality.<br/>\n");;
}
}
class B extends A {
function configure() {
parent::configure();
print("I am B::configure() and provide additional functionality.<br/>\n");
}
}
class C extends B {
function configure() {
parent::configure();
print("I am C::configure() and provide even more functionality.<br/>\n");
}
}
$c = new C;
$c->configure();
?>
Output:
I am A::configure() and provide basic functionality.
I am B::configure() and provide additional functionality.
I am C::configure() and provide even more functionality.
Thanks for your help! :)
Without claiming it's pretty, I'd suggest the following. I left out your configure() functions for brevity.
<?php
class A {
function configure_master() {
$init_class = get_class($this);
$ancestry = array();
while (! empty($init_class)) {
$ancestry[] = $init_class;
$init_class = get_parent_class($init_class);
}
for ($i = count($ancestry) - 1; $i >= 0; $i--) {
call_user_func(array($this, $ancestry[$i] . '::configure'));
}
}
}
$c = new C;
$c->configure_master();
I tested this, and it works. call_user_func($ancestry[$i] . '::configure')
also works (at least in php 5.3, which is where I tested it), but it relies on the odd (to me) scoping of $this. I'm uncertain whether this is reliable or will carry forward into future versions. The version above "should be" more robust, given my limited knowledge.
If keeping the outside call as configure() is a requirement, I'd suggest renaming all your "configure()" methods to "configure_local()" or some such, and rename "configure_master()" to "configure()". In fact, that's what I'd do to start with, but that's a matter of personal taste.
If you need both the internal method and the external API to remain the same... Then you're stuck, since the base class method is overridden, but I'm sure you knew that.
An arbitrary amount of inheritance? I can't say that's a road I'd want to take, but if you must do so, you might be able to write some type of configure method in the base class that takes the calling (child) class as a parameter. That method would then determine the class type, walk the inheritance tree (pushing each parent class type onto a stack), and then simply run each configure method in succession. I think that the underlying principle might be called reflection, but I'm not sure. (edit: Even less sure, now. Definitely read about it before taking my word.)
The method in the base class might have to be static, though...
I believe that something like this would be possible, but I'm not really up to speed on PHP. I"ll do a little bit of searching and see if syntax and code allows this. Hopefully, though, you can use this idea.
This is the terrible pseudocode form of the idea that I have.
<?php
class A {
static function configure(class) {
//get qualified class name from argument
//get inheritance tree for qualified class name, push each tier into array
//loop: pop array, call configure method for each class in hierarchy
//this might possibly work?
}
}
class B extends A {
function configure() {
print("I am B::configure() and provide additional functionality.<br/>\n");
}
}
class C extends B {
function configure() {
print("I am C::configure() and provide additional functionality.<br/>\n");
}
}
$c = new C;
A::configure($c);
?>
Again, I'll take a look and see if anything in PHP could support this theory. In the meantime, I can live with a few downvotes.
Edit: Actually, this might not have to be static, as long as the method names don't overlap during inheritance. That actually might be a little more elegant, as the object could find its own class name and hierarchy by calling a master configure method.
How about this? (Forgive my PHP syntax if it is imperfect)
<?php
class MyBase {
private $_isChild = true;
public function __construct()
$this->_isChild = false;
}
public function behave() {
if $this->_isChild {
parent::behave();
}
self::doBehave();
}
protected function doBehave()
// do stuff here!
}
}
?>
In your child classes, just implement doBehave.
Max
精彩评论