让PHP子类继承静态属性的单独实例

时间:2013-09-06 09:09:25

标签: php oop inheritance

标题可能不太清楚,所以这是一个例子。

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 questionsanswers,而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>的原因}})。

TL; DR

总而言之,假设上述方法 有效,

  • 是否允许父类从OOP的角度维护其“活动”子级的静态数组?
  • 有更好的和/或更清洁的方法吗?

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()) { ... } 是一个空的对象,则设置它对于我需要重新分配的其他东西,我不能简单地继续修改它 - 这实际上引入了可怕的耦合并且具有巨大潜在的微妙错误。)

自我注意:我需要逐步淘汰整个“==”概念。