PHP:两个伙伴类想要共享方法,但只能彼此共享——我做错了吗?


PHP: Two buddy classes want to share methods, but only with each other -- Am I Doing It Wrong?

我有两个类,它们是好朋友,每个类都有另一个需要使用的方法。我不想只公开这些方法,这是目前为止我所能做的。

我认为这是protected的全部目的,但它只将方法暴露给父类或子类——我的两个类只是好朋友,是一个正直的团队。

为什么这些类是如此长久的伙伴?

嗯,你看,我有一个Item班和一个Selection班。项目有一个字符串标签。选择是一组项目,但是,并不是所有的项目都必须在一个选择中(项目可以是独立的)。选择扩展了ArrayObject,这样它就可以在内部存储项目,就像一个很酷的关联数组($selection["label"]容易访问项目)。

当你改变一个项目对象的label时,选择关联数组的键需要更新以匹配!

所以,当一个项目被添加到一个选择,选择对象调用该项目的associate方法,它告诉项目它的爸爸是谁。

然后,每当Item的标签被更改时,Item调用其关联父选择对象的updateKey方法。

这样,项目、它们的标签和它们所处的选区就能和平共处了:)

无论如何,问题是我的解决方案似乎要求这两个宝贵的方法,associateupdateKey,向公众公开。

我编写了下面的演示代码,来解释我的小Item-Selection团队。

<?php
class Item {
    private $label;
    private $selection;
    function __construct ($label) {
        $this->label($label);
    }
    function label ($label=null) {
        if (!$label) return $this->label;
        if ($this->selection) $this->selection->updateKey($this->label, $label);
        $this->label = $label;
        return $this;
    }
    public function associate ($selection=null) {
        $this->selection = $selection;
        return $this;
    }
}
class Selection extends ArrayObject {
    public function updateKey ($old, $new) {
        $this[$new] = $this[$old];
        unset($this[$old]);
        return $this;
    }
    function add () {
        foreach (func_get_args() as $argi => $arg) {
            if ($arg instanceof Item) $this[$arg->label()] = $arg->associate($this);
            else throw new Exception("invalid type for selection->add");
        }
        return $this;
    }
    function item ($label) {
        return $this->add( new Item($label) );
    }
}
$selection = (new Selection)
    ->item("A")
    ->item("B");
$selection->add( new Item("C") );
$selection["A"]->label("APPLE");
$selection["B"]->label("BANANA");
$selection["C"]->label("CRANBERRY");
echo "<pre>";
print_r($selection);
echo "</pre>";
?>

如何阻止associateupdateKey向公众曝光?

首先,我试着看看是否有一种方法可以让这两个类从一个更大的类继承,希望这可能会使它们足够相关以共享受保护的方法——然而,选择已经扩展了ArrayObject, PHP不允许多重继承。我们能做什么呢?

我应该如何重构它?

我做错了吗?

因为你不能真正使这些方法只对彼此可用,你能做的是使updateKey更像一个事件,像onLabelChange($label)。这样你就不会通过保存:

来暴露选择的实现

"嘿,选择,更新你的键,因为我改变了一个标签",

这种方法更像是:

"嘿,选择,我更新了一个标签,所以,做你的业务,无论它是什么"。

在这种情况下,你需要做更多的检查,以确保$label是包含在选择的对象

EDIT:我刚刚发现了PHP 5.4中引入的trait,我不太了解它们(我只是在php.net上读到它们),但它们可能会解决你的困境。

PHP不像c++那样有友谊关系的概念,所以没有。但是,确实没有理由需要隐藏我可以看到的这些方法。事实上,它们似乎是为公众设计的。也许你需要做的是保护这些方法,并确保它们在被其他东西操纵时保持一致。

  • 由于"项目可以是独立的",你可能希望Items能够与Selections相关联和重新关联,所以associate()应该是公共的。
  • updateKey()中添加检查,以确保正在更新的项目是Item的实例,并且是给定选择的成员。
  • 你需要disassociate()方法。还要添加一个成员资格测试。确保项目在通过公共接口操作时适当地解除关联。

如果你的问题只是"如何在PHP中实现友谊",请参阅是否有一个简单的方法来模拟PHP 5.3中的友谊。一般情况下,你可以修改它,但通常不值得。