如何在PHP中实现组合

12
我希望使用PHP实现下面这个图表的下一片段。
请参见以下组合示例图表:

enter image description here

我们可以使用内部类在Java中实现组合。但在PHP中没有“内部类”的类比。当然,有traits。但我们可以在多个类中使用它。我已经这样实现了组合:
class Head {
    private static $instance = NULL;

    private function __construct(){}

    public static function getInstance() {
        $traces = debug_backtrace();
        if (strcmp($traces[1]['class'], 'Human')) {
            echo "<br>Only human has head"; // Exception can be thrown here
            return NULL;
        }
        if (!static::$instance) static::$instance = new self();
        return static::$instance;
    }

    public function __toString() {
        return 'Head';
    }
}

class Human {
    private $head;

    public function __construct() {
        $this->head = Head::getInstance();
    }

    public function __toString() {
        return 'I have ' . $this->head;
    }
}

class Plant {
    private $head;

    public function __construct() {
        $this->head = Head::getInstance();
    }
}

$human = new Human();
echo $human;

$superman = new Plant();

你这样做是正确的吗?

在PHP中实现组合关系是否有更好的方法?


下次请将您的代码粘贴在问题中。此外,您的代码展示了一个简单组合与单例类,有什么解决方法和解决哪个问题? - dbf
在问题中添加了代码。这是一个“解决方法”,因为可能有更好的方法来完成它 :) 更符合PHP本地化,就像Java中的内部类一样。 - profport
你在哪里读到组合是关于内部类的? - Wes
1
@profport 以及我们都阅读俄语... ;) - dbf
2
@tereško 两年后,你实际上预测了未来!:D - dbf
显示剩余2条评论
2个回答

38

看起来你对面向对象编程中的“组合”概念非常困惑。

组合是两个类之间的关联,对于第一个类的实例化,必须提供第二个类。在你提到的Human类图中,它需要一个Head。在代码中应该这样表示:

class Head {
}

class Human {
    private $head;
    public function __construct(Head $head) {
       $this->head = $head;
    }
}

$bob = new Human(new Head);

就是这样,不需要使用 debug_backtrace() 和单例模式的反模式。而且,顺便说一下,在 Java 中,即使有内部类的选项,这也几乎完全相同。

至于你发布的代码,这是错误的:

if (strcmp($traces[1]['class'], 'Human')) {
    echo "<br>Only human has head"; // Exception can be thrown here
    return NULL;
}

这个图表并没有说只有人类才有头部,只是说为了实例化必须要有头部。你也可以拥有一个$skipper = new Dog(new Head);,这也是完全可以的。


感谢 @tereško,我真的很困惑。我以为组合意味着:只有 Human 有 Head。 也许你的代码更接近于聚合。在组合中,据我所知,我们应该在另一个类内实例化某个类。例如: public function __construct() { $this->head = new Head(); } - profport
当然,在大多数情况下,将Head类重用于其他类中非常有用。但是在UML图中是否可能限制组合?也就是说,仅仅人类拥有头部?这有时可能是必要的。 - profport
1
不,无法限制,但您需要一个扩展Head的HumanHead来满足Human的要求。我还要警告不要试图将OOP适应于某些“现实世界”的情境中。编程不涉及现实世界,而是涉及抽象化。 - tereško
但是这个代码示例显示HeadHuman之间的关系就像下面链接中所示的关联关系。 - Susantha7
@klewis $bob = new Human(new Head, new Legs);,或者你可以使用提供新腿的方法来更换他的腿。 - Jimbo
显示剩余2条评论

6

组合优于继承。

基本上,组合聚合的一种更强的形式。我们可以说身体和头部之间的关系是组合关系,因为没有头部,我们无法生存,但身体和手之间的关系是聚合关系,因为我们可能会失去手,但我们仍然能活着。还有弱关系,比如直接关联临时关联

然而,依赖注入只是一种模式,在这种模式下,我们创建这些关联(通过将一个类注入到另一个类中)。

在大多数情况下,您可以通过构造函数识别组合关系,该构造函数注入另一个对象并将其分配给其属性,并且对象的生命周期在同一时刻开始和结束。

除此以外,从技术角度来看,每种关联的实现之间几乎没有太大的区别。这主要是关系定义而不是实现问题。

组合关系通常用于重写/更改或增强注入对象类的行为。不要过度激动或担心组合。在大多数情况下,与继承相比,优势很小,并且最受第三方库创建者的欢迎,他们使用组合来为其库添加额外灵活性。

从父类继承并重载/添加方法可以给我们带来类似的功能,但是使用组合我们可以获得更多的灵活性。例如,如果我们的构造函数接受继承自头部接口的对象,我们可以使用HumanHead类(如果它扩展了头部接口),但我们也可以使用我们的类增强其他从相同接口扩展的生物,比如DogHead或DuckHead...

这也许不是最好的例子,但它展示了基本概念。

一定要查看此[Aggregation vs Composition vs Association vs Direct Association]和此[Association-Aggregation-Composition-Dependency]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接