特征与接口
我最近一直在尝试学习PHP,我发现自己被一些特质所吸引。我理解水平代码重用的概念,并且不希望必然从抽象类继承。我不明白的是:使用特征与接口之间的关键区别是什么?
我尝试过搜索一篇像样的博客文章或文章,解释何时使用其中一个或另一个,但到目前为止,我发现的例子似乎非常相似,以至于完全相同。
我最近一直在尝试学习PHP,我发现自己被一些特质所吸引。我理解水平代码重用的概念,并且不希望必然从抽象类继承。我不明白的是:使用特征与接口之间的关键区别是什么?
我尝试过搜索一篇像样的博客文章或文章,解释何时使用其中一个或另一个,但到目前为止,我发现的例子似乎非常相似,以至于完全相同。
公益公告:
我想郑重声明,我相信特征几乎总是代码气味,应该避免使用组合。在我看来,单一继承经常被滥用到反模式的地步,而多重继承只会加剧这个问题。在大多数情况下,通过支持组合而不是继承(无论是单个或多个),您将获得更好的服务。如果您仍然对特征及其与界面的关系感兴趣,请继续阅读...
让我们先说:
面向对象编程 (OOP) 可能是一个难以掌握的范例。仅仅因为你使用的是类并不意味着你的代码是面向对象的(OO)。
要编写OO代码,您需要了解OOP实际上是关于对象的功能。你必须根据班级能做什么而不是他们实际做什么来考虑。这与传统的过程式编程形成鲜明对比,传统的过程式编程的重点是让一些代码“做点什么”。
如果OOP代码是关于规划和设计的,那么接口就是蓝图,对象是完全构建的房子。同时,特征只是一种帮助构建蓝图(界面)布置的房子的方法。
那么,我们为什么要使用接口呢?很简单,接口使我们的代码不那么脆弱。如果您对此陈述有疑问,请询问任何被迫维护不是针对接口编写的遗留代码的人。
该接口是程序员和他/她的代码之间的契约。界面上写着:“只要你遵守我的规则,你就可以按照你喜欢的方式实现我,我保证我不会破坏你的其他代码。
因此,举个例子,考虑一个真实的场景(没有汽车或小部件):
您希望为 Web 应用程序实现缓存系统以减少服务器负载
首先,使用 APC 编写一个类来缓存请求响应:
class ApcCacher
{
public function fetch($key) {
return apc_fetch($key);
}
public function store($key, $data) {
return apc_store($key, $data);
}
public function delete($key) {
return apc_delete($key);
}
}
然后,在 HTTP 响应对象中,在执行生成实际响应的所有工作之前,检查缓存命中:
class Controller
{
protected $req;
protected $resp;
protected $cacher;
public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) {
$this->req = $req;
$this->resp = $resp;
$this->cacher = $cacher;
$this->buildResponse();
}
public function buildResponse() {
if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) {
$this->resp = $response;
} else {
// Build the response manually
}
}
public function getResponse() {
return $this->resp;
}
}
这种方法效果很好。但也许几周后,您决定要使用基于文件的缓存系统而不是APC。现在,您必须更改控制器代码,因为您已经对控制器进行了编程,以使用类的功能,而不是使用表达类功能的接口。假设您使类依赖于 a 而不是具体,而不是上述内容,如下所示:ApcCacher
ApcCacher
Controller
CacherInterface
ApcCacher
// Your controller's constructor using the interface as a dependency
public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)
为了做到这一点,你定义你的接口,如下所示:
interface CacherInterface
{
public function fetch($key);
public function store($key, $data);
public function delete($key);
}
反过来,您和您的新类都实现了 ,并且您对类进行了编程以使用接口所需的功能。ApcCacher
FileCacher
CacherInterface
Controller
此示例(希望如此)演示了如何对接口进行编程允许您更改类的内部实现,而不必担心这些更改是否会破坏其他代码。
另一方面,特征只是一种重用代码的方法。不应将接口视为特征的相互排斥的替代方案。事实上,创建满足接口所需功能的特征是理想的用例。
仅当多个类共享相同的功能(可能由同一接口决定)时,才应使用特征。使用特征为单个类提供功能是没有意义的:这只会混淆该类的作用,而更好的设计会将特征的功能移动到相关类中。
请考虑以下特征实现:
interface Person
{
public function greet();
public function eat($food);
}
trait EatingTrait
{
public function eat($food)
{
$this->putInMouth($food);
}
private function putInMouth($food)
{
// Digest delicious food
}
}
class NicePerson implements Person
{
use EatingTrait;
public function greet()
{
echo 'Good day, good sir!';
}
}
class MeanPerson implements Person
{
use EatingTrait;
public function greet()
{
echo 'Your mother was a hamster!';
}
}
一个更具体的例子:想象一下,你和你的界面讨论中都使用相同的方法来确定缓存条目是否过时并且应该被删除(显然在现实生活中不是这种情况,但要使用它)。您可以编写一个特征,并允许两个类都使用它来满足通用接口要求。FileCacher
ApcCacher
最后要注意的是:注意不要过分使用特质。通常,当独特的类实现就足够了时,特征被用作不良设计的拐杖。您应该将特征限制为满足接口要求,以实现最佳代码设计。
接口定义实现类必须实现的一组方法。
当一个特征被“d”时,方法的实现也会出现 - 这在.use
Interface
这是最大的区别。
Traits 是一种在单继承语言(如 PHP)中重用代码的机制。Trait 旨在通过使开发人员能够在位于不同类层次结构中的多个独立类中自由重用方法集来减少单继承的某些限制。