PHP OOP声明接口实现兼容性

时间:2014-02-09 22:16:12

标签: php oop interface

我有以下OOP结构:

<?php


interface AnimalInterface
{
    public function getName();
}

class Dog implements AnimalInterface
{
    public function getName() {
        return 'dog';
    }

    public function makeFriends()
    {
        echo 'I have friends now :)';
    }
}

class Cat implements AnimalInterface
{
    public function getName() {
        return 'cat';
    }

    public function hateFriends()
    {
        echo 'I cant make friends :(';
    }
}

interface AnimalDoInterface
{
    public function makeFriend(AnimalInterface $animal);
}

class DogFriend implements AnimalDoInterface
{
    public function makeFriend(Dog $dog)
    {
        $dog->makeFriends();
    }
}

class CatFriend implements AnimalDoInterface
{
    public function makeFriend(Cat $cat)
    {
        $cat->hateFriends();
    }
}

现在,Object Interfaces上的PHP手册说:

  

实现接口的类必须使用与接口中定义的完全相同的方法签名。不这样做会导致致命的错误。

为什么会这样?我完全误解了界面吗?当然,我应该能够使用该接口的接口或实现声明AnimalDoInterface::makeFriend?在这种情况下,它应该在技术上与Cat实现AnimalInterface兼容,这正是它所期望的。

无论我的OOP是否出错,有没有办法在PHP中实现这个?

所以看起来我不够清楚,我的不好。但是,基本上我想要实现的是让AnimalDoInterface的实现比它的界面更具限制性。所以在这种情况下,我希望DogFriend::makeFriend只允许Dog类作为它的参数,在我看来它应该是可接受的,因为它实现了AnimalInterface和{{1允许CatFriend类,同样的事情。

编辑:修改了课程,还添加了我想要实现的目标。

编辑2:

所以目前,我必须实现它的方式如下:

Cat

我想要避免对类类型进行额外检查。

3 个答案:

答案 0 :(得分:4)

接口唯一的工作是强制执行两个对象以相同方式运行的事实,而不管它们如何实现该行为。这是一份合同,声明两个对象可以互换用于某些特定目的。

(编辑:这部分代码已得到纠正,但作为一个很好的介绍。)接口AnimalInterface定义行为(函数)getAnimalName(),以及声称要实现该接口的任何类都必须实现该行为。 class Dog声明为implements AnimalInterface,但未实现所需行为 - 您无法在getAnimalName()的实例上调用Dog。所以我们已经有了一个致命的错误,因为我们还没有达到界面定义的“合同”。

修复并继续,然后你有一个接口AnimalDoInterface,它具有makeFriend(AnimalInterface $animal)定义的行为(函数) - 意思是,你可以传递任何对象来实现{ {1}}到任何对象的AnimalInterface方法,该方法实现makeFriend

但是,然后您使用更严格的行为定义AnimalDoInterface - 其class DogFriend版本只能接受makeFriend个对象;根据接口,它也应该能够接受Dog个对象,这些对象也会实现Cat,所以再次,接口的“契约”不会被满足,我们将得到一个致命的错误。 / p>

如果我们要解决此问题,您的示例中会出现另一个问题:您有AnimalInterface的来电,但如果您的论证属于$cat->hateFriends();AnimalInterface,那么您会无法知道存在AnimalDoInterface函数。 PHP,对这些事情非常放松,如果事实证明不存在,那么就会让你尝试并在运行时爆炸;更严格的语言只允许你使用保证的函数,因为它们是在界面中声明的。


要理解为什么你不能比界面更具限制性,想象你不知道特定对象的类,你只知道它实现了一个特定的接口。

如果我知道对象hateFriends()实现$a,而对象AnimalInterface实现$b,我可以通过查看接口做出以下假设:

  1. 我可以致电AnimalDoInterface(因为$a->getName();在合同中有此内容)
  2. 我可以致电AnimalInterface(因为$b->makeFriend($a);在合同中我可以传递任何实施AnimalDoInterface的内容
  3. 但是使用您的代码,如果AnimalInterface$a,而Cat$b,我的假设#2将会失败。如果界面允许这种情况发生,它将无法正常工作。

答案 1 :(得分:3)

实现接口的所有类必须具有相同方法的原因是,无论实例化哪个子类型,都可以在对象上调用该方法。

在这种情况下,您的逻辑不一致,因为两种子类型Cat和Dog具有不同的方法。所以你不能在对象上调用makeFriends(),因为你不知道对象那个方法。

这是使用接口的重点,因此您可以使用不同的子类型,但同时您可以确定至少一些常用方法。

在您的情况下处理此问题的一种方法是确保Cat实现相同的方法,但使该方法在运行时抛出异常,表明它不可能。这允许接口在编译时满足(我知道PHP没有编译器,但这模仿像Java那样在编译时进行接口检查的语言)。

class Cat implements AnimalInterface
{
    public function makeFriends()
    {
        throw new RuntimeException('I cant make friends :(');
    }
}

答案 2 :(得分:0)

实现AnimalDoInterface的类必须具有makeFriend方法,该方法接受任何实现AnimalInterface的对象。在你的情况下,试图声明

class DogFriend implements AnimalDoInterface {
  public function makeFriend(Dog $foo) { }
}

不会准确地实现这一点,因为将实现AnimalInterface的任何东西传递给实现AnimalDoInterface的任何东西的makeFriend方法都应该是安全的。