在现有的代码库中,我有一个返回实例的静态构建器方法。下面是一个简单的例子:
class Grandparent{
}
class Parent extends Grandparent{
}
class Child extends Parent{
public static fetchChildById($id){
// ...
return new Child;
}
}
在实际代码中,我有一个单一的Grandparent
类和几个子类类似于Parent
和Child
(不仅仅是Parent
和Child
)。
我现在需要在Grandparent
实现一个在fetchChildById()
使用的新方法。这种方法需要利用同一父节点的所有子节点共有的某些数据。因为我还没有一个类实例,我被迫使一切都是静态的,但是,当然,这将不能正常工作,因为它是不可能覆盖静态属性和方法:
class Grandparent{
protected static $data = array(
'default',
);
protected static function filter(){
foreach(self::$data as $i){ // <!-- Will always be `default'
// ...
}
}
}
class Parent extends Grandparent{
protected static $data = array(
'one',
'two',
);
}
class Child extends Parent{
public static fetchChildById($id){
self::filter();
// ...
return new Child;
}
}
我相信这是一个用于后期静态绑定的用例,但是代码需要在PHP/5.2.0上运行:(
我不太喜欢我想过的明显的变通方法:
创建一个单独的构建器类建议进行更多的重构,而不是我目前所能承受的:
$builder = new ChildBuilder; $bart = $builder->fetchChildById(1);
创建额外的实例看起来很难看(也意味着许多更改):
$builder = new Child; $bart = $builder->fetchChildById(1);
全局变量……哦,好吧,我还没那么绝望。
我是否忽略了一些明显的机制来定制$data
?
这里有一个使用反射的替代方法。这将需要修改所有的fetchChildById
实现,但是用全局查找/替换:
self::filter(__CLASS__); // this is the modification
则filter
将变成:
protected static function filter($className){
$reflect = new ReflectionClass($className);
$data = $reflect->getStaticPropertyValue('data');
foreach($data as $i){
// ...
}
}
更新:属性$data
需要公开以上工作(抱歉-我在探索期间编写了public
)。但是有一个等价的版本没有这个要求:
$reflect = new ReflectionProperty($className, 'data');
$reflect->setAccessible(true);
$data = $reflect->getValue();