了解接口的第一件事是,它们不用于继承。这一点非常重要。如果您试图让多个类共享相同的具体代码,那么接口就不是为此而设计的。
第二件需要了解的事情是客户端代码和服务端代码之间的区别。
客户端代码本质上是数据请求序列中的“最后一步”。MVC中的控制器或视图可以被认为是客户端代码。与此同时,模型可以被认为是服务端代码。
接口旨在强制执行客户端代码对从服务中获取的数据类型的一致性。或者可以这样想——接口是服务确保它们将与来自客户端代码的请求兼容的一种方式。它们确实提供了一种访问数据的接口,而不是多个类可以共享实现的接口。
因此,以下是一个具体的例子:
客户端代码——用户论坛资料的ProfileViewController类class ProfileViewController
{
public function showProfile(User $user)
{
$user->getProfile();
}
}
Service Code - 一个用户模型,它检索数据并将其传递给请求它的客户端代码
class User
{
public function getProfile()
{
$profile = Do some SQL query here or something
return $profile;
}
}
现在假设你之后决定将用户分为会员、管理员、裁判、调解员、作家、编辑等,并且每个人都有自己独特的个人资料类型。(例如,它拥有自己的自定义查询、数据或其他)
这里现在存在两个问题:
- 您需要确保无论传递什么,都将包含 getProfile() 方法。
- 如果传递的不是 User 对象,则 showProfile() 将失败。
1 很容易通过抽象类和方法(或通过接口)来解决。2 一开始也很容易解决,因为您可以将 Moderators、Admins 和 Members 都作为 User 基类的子类。
但是,如果在以后,除了用户资料之外,您还想要为事物创建通用资料怎么办。也许您想要显示运动员的档案,甚至是名人档案。他们不是用户,但他们仍然有个人资料/详情页。
因为他们不是用户,所以考虑将它们视为 User 的子类可能没有任何意义。
那么现在你有点困难了。showProfile() 需要能够接受的对象不仅仅是 User 对象。实际上,您不知道最终想要传递什么类型的对象。但同时,由于您始终想能够获得 $user->getProfile(),因此您传递的任何内容都必须足够通用,以便通过实现具体的 getProfile() 方法进行传递。
解决方案?接口!
首先是一些服务代码
interface IHasProfile
{
public function getProfile();
}
class User implements IHasProfile
{
public function getProfile()
{
$profile = Your unique user profile query here
return $profile;
}
}
class Celebrity implements IHasProfile
{
public function getProfile()
{
$profile = Your unique celebrity profile query here
return $profile;
}
}
class Car implements IHasProfile
{
public function getProfile()
{
$profile = Your unique vehicle profile query goes here
return $profile;
}
}
接下来是使用它的客户端代码
class ProfileViewController
{
public function showProfile(IHasProfile $obj)
{
$obj->getProfile();
}
}
就是这样。showProfile()现在已经被抽象化到足够程度,它不关心得到的对象是什么,只关心该对象是否具有公共的getProfile()方法。因此,现在您可以尽情地创建新类型的对象,如果这些对象需要具有配置文件,则只需给它们“implements IHasProfile”,它们将自动与showProfile()配合使用。
这可能是一个人为的例子,但它至少说明了接口的概念。
当然,您也可以“懒惰”一点,根本不对对象进行类型转换,从而允许传入任何对象。但那是另一个完全不同的话题;)