标题可能不太清楚,所以这是一个例子。
abstract Class A {
public static $property = null;
}
Class B extends A {
}
Class C extends A {
}
我希望在扩展A的所有类中都有一个“None”方法,并且此方法应该返回类类的实例 - 始终相同。所以:
B::None() // Returns a "default" B
C::None() // Returns a "default" C
为什么这是:我有(简化)几个Slots,可能会也可能不会分配给几种活动。所以我可以有一个冲浪槽和一个游泳槽。那个Slot可能是null。在我的代码中,在报告时,我当然可以执行类似
的操作if (Slot.Surfing == null) {
println "Not surfing anywhere";
} else {
println Slot.Surfing.Location;
}
但我想不检查,只写
println Slot.Surfing.Location;
并将插槽预先分配给Surfing :: None()。实际上我会在超类中执行此操作并让它自动分配“无”的“正确”实例。
这样,Slot.Surfing(无处)是来自Slot.Swimming(无处)的不同的空,但对我来说现在实际上是功能。< / p>
问题是,如果我真的想检查一下游泳,我一定要确定
if (Slot.Surfing == Surfing::None()) {
的工作原理。为此,None()必须始终返回相同的对象。我可以在Surfing字段上运行检查,也许是一个非i18n-ed整数值... 0或-1是一个典型的选择......但为此目的添加一个属性似乎设计不合理。
注意:这与Singleton(反)模式有很多相似之处,但它实际上并不是Singleton(见下图)。
但是,如果我在A中实现None()
方法,我必须处理这样一个事实,即任何静态属性只能在A中“生存”一次,而不是在每个子类中生存。所以我创建了一个B的默认实例,将其保存在A中,然后对A的其他子类的所有后续调用将查找并返回该实例 - 而C::None()
将是一个实例B。
我可以让None()方法每次都创建一个 new 默认实例。这有效,但现在我有几个“默认”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和其他地方检查了some questions和answers,而the most relevant one使用的是相同的方法(请注意在被调用类上编入索引的$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,C来自B,该属性存在于哪里,或者它应该存在于哪里?现在? - 我没有令人满意的答案。)
上述方法实际上与Singleton
没有太大区别。它seems(感谢Touki指点我)我可以通过依赖注入来摆脱单身人士。但是在这种情况下,需要将None_C
传递给所有可能需要默认值以引用C实例的方法。然后我必须将None_C
推送到{ {1}}对象,让我知道我可能声明的A的任何子类。乍一看,这闻起来更多(虽然实际上添加A 的另一个子类相当于改变系统的配置......这将是 更改{{1>的原因}})。
总而言之,假设上述方法 有效,
答案 0 :(得分:0)
如果记忆不是反对意见(现在很少有人反对),那么解决方案只是向OOP土地迈进一步。
需要“儿童单身人士”的真正问题是我希望比较是可靠的。当然,两个不同的“无效”对象虽然都是“无效的”,因此在这方面是相同的,但不是相同的对象。如果它们具有相同的属性,那么==
运算符将返回TRUE,但如果其中一个因某些原因(即由于错误而)略有变化,{{1}将返回FALSE。在单例上下文中使用==
PHP5运算符将解决此问题,但会引入更多复杂性。
(对我来说不那么明显)简单的解决方案是完全放弃比较并简单地替换:
===
有合适的
if (Slot->Surfing == Surfing::None())
然后我可以在 parent 类中声明isEmpty方法,这表明我确实还需要一个更具体的 equals 和比较运营商:
Slot->Surfing->isNull() // or maybe isEmpty() depending on semantics
这种方法的另一大优势是,现在每个活动都是一个独立的对象,并且可以安全地更新而不改变其他实例(如果abstract Class A {
...
public static function getNull() {
A $nulled = new A();
$nulled->setNull(); // Whatever is needed to nullify an object
return $nulled;
}
public function setNull() {
$this->nullFlag = True; // Example
}
public function isNull() {
return (True === ($this->nullFlag)); // Or whatever else
}
abstract function equals($obj);
abstract function compareTo($obj);
}
Class Surfing extends A {
public function equals($obj) {
if (null == $obj) {
return False;
}
if ($this === $obj) {
return True;
}
if (!($obj instanceOf Surfing)) {
return False;
}
// Properties
foreach(array('property1','property2',...) as $val) {
if ($this->$val != $obj->$val) {
return False;
}
// Maybe some other check
...
return True;
}
}
...
Slot->Surfing = Surfing::getNull();
...
if (Slot->Surfing->isNull()) {
...
}
是一个空的对象,则设置它对于我需要重新分配的其他东西,我不能简单地继续修改它 - 这实际上引入了可怕的耦合并且具有巨大潜在的微妙错误。)
自我注意:我需要逐步淘汰整个“==”概念。