我有以下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
我想要避免对类类型进行额外检查。
答案 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
,我可以通过查看接口做出以下假设:
AnimalDoInterface
(因为$a->getName();
在合同中有此内容)AnimalInterface
(因为$b->makeFriend($a);
在合同中我可以传递任何实施AnimalDoInterface
的内容但是使用您的代码,如果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方法都应该是安全的。