标题可能不太清楚,所以这里有一个例子。
abstract Class A {
public static $property = null;
}
Class B extends A {
}
Class C extends A {
}
我想在所有扩展 A 的类中都有一个“无”方法,并且这个方法应该返回一个实例——总是相同的——被调用的类。所以:
B::None() // Returns a "default" B
C::None() // Returns a "default" C
为什么会这样:我有(简化)几个插槽,它们可能分配给也可能不分配给多种活动。所以我可以有一个冲浪插槽和一个游泳插槽。并且那个 Slot 可能是空的。在我的代码中,报告时,我当然可以做类似的事情
if (Slot.Surfing == null) {
println "Not surfing anywhere";
} else {
println Slot.Surfing.Location;
}
但我根本不想检查,只写
println Slot.Surfing.Location;
并将插槽预分配给 Surfing::None()。实际上,我会在超类中执行此操作,并让它自动分配 None 的“正确”实例。
这样,Slot.Surfing(nowhere) 与 Slot.Swimming(nowhere) 是不同的 null,但对我来说现在实际上是一个feature。
问题是如果我真的想检查我在某个地方游泳,我必须确定
if (Slot.Surfing == Surfing::None()) {
作品。为此, None() 必须始终返回相同的对象。我可以在 Surfing 字段上运行检查,也许是一个非 i18n-ed 整数值...... 0 或 -1 是典型的选择......但为此目的添加一个属性似乎设计不当。
注意:这与单例(反)模式有很多相似之处,但它实际上不是单例(见底部)。
但是,如果我在 A 中实现该None()
方法,我必须处理这样一个事实,即任何静态属性只会在 A 中“存活”一次,而不是在每个子类中。所以我创建了一个 B 的默认实例,将它保存在 A 中,然后对 A 的其他子类的所有后续调用都会找到并返回该实例——并且C::None()
将是 B 的一个实例。
我可以让 None() 方法每次都创建一个新的默认实例。这行得通,但现在我有几个“默认”B,在某些情况下,两个属性都设置为B::None()
将非常正确地被认为是不同的。
最后我想出了这个解决方法(PHP 5.3.+):
private static $nones = array(); // array of already created "null" instances
protected static function None() {
// If I do not already have an instance for this class...
if (!isset(self::$nones[$ChildName = get_called_class()])) {
// ... I create a default instance.
self::$nones[$ChildName] = new $ChildName(/*...*/);
}
// And I return the default instance.
return self::$nones[$myClass];
}
我已经检查了Stack Overflow 和其他地方的一些问题和答案,最相关的问题和答案采用了相同的方法(注意在$instance
被调用类的名称上索引的数组):
class A {
public static function getInstance(){
// Maybe use this function to implement the singleton pattern ...
return self::$instance[get_called_class()];
}
public function className(){
return get_class(self::getInstance());
}
}
然而,也许是因为我仍然是一个耳朵后面湿透的OOPer,对我来说这种方法闻起来很臭。我认为应该有一种方法可以在父类中声明一个子静态属性,并从超类访问它(当然,那我不得不问自己:如果 A 声明了一个“下游”静态属性,B 继承自A 和来自 B 的 C,该物业现在住在哪里,或者应该住在哪里? ——我没有令人满意的答案)。
附录 - 单身人士
上述方法在实践中与Singleton
. 看来(感谢Touki向我指出)我可以通过依赖注入摆脱单例。然而在这种情况下,它需要传递None_C
给所有可能需要默认值来引用 C 实例的方法。然后我必须推None_C
入我的Configuration
对象,并让它知道 A 的任何子类我可以宣布。乍一看,这更难闻(尽管实际上添加 A 的另一个子类相当于更改系统的配置......这将是更改的原因Configuration
)。
TL;博士
因此,长话短说,假设上述方法确实有效,
- 从 OOP 的角度来看,是否让父类维护其“活动”子代的静态数组?
- 有更好和/或更清洁的方法吗?