首先要了解接口是它们不用于继承。理解这一点非常重要。如果您试图让多个类共享相同的具体代码,那不是接口的用途。
第二件要了解的是客户端代码和服务代码之间的区别。
客户端代码本质上是一系列数据请求中的“最后一步”。MVC 中的控制器或视图可以视为客户端代码。该模型同时可以被认为是服务代码。
接口旨在供客户端代码强制其从服务获取的数据类型的一致性。或者另一种思考方式——接口是服务确保它们与来自客户端代码的请求兼容的一种方式。这就是他们所做的一切。它们确实提供了一个访问数据的接口,而不是多个类可以共享的实现。
所以给你一个具体的例子:
客户端代码 - 用户论坛个人资料的 ProfileViewController 类
class ProfileViewController
{
public function showProfile(User $user)
{
$user->getProfile();
}
}
服务代码 - 一种用户模型,用于检索数据并将其传递给请求它的客户端代码
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->getProfile(),因此您传入的任何内容都必须足够通用才能传入,并实现具体的 getProfile() 方法。
解决方案?接口!!!!
首先是一些服务代码
// First define an interface for ANY service object that will have a profile
interface IHasProfile
{
public function getProfile();
}
// Next, define the class for an object that should have a profile. I'll do a bunch for the sake of an example...
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() 方法。因此,现在您可以根据自己的意愿创建新类型的对象,如果它们打算具有配置文件,您可以只给它们“实现 IHasProfile”,它们将自动与 showProfile() 一起使用。
有点人为的例子,但它至少应该说明接口的概念。
当然,您可以只是“懒惰”并且根本不对对象进行类型转换,从而允许传入任何对象。但这完全是一个单独的主题;)