最近我一直在努力学习PHP,我发现自己被trait缠住了。我理解横向代码重用的概念,并且不希望必然地继承抽象类。我不明白的是:使用特征和使用界面之间的关键区别是什么?

我曾试着搜索过一篇像样的博客文章或文章,解释什么时候使用其中一种或另一种,但到目前为止,我找到的例子似乎非常相似,甚至完全相同。


当前回答

该特征与我们可以用于多重继承目的和代码可重用性的类相同。

我们可以在类中使用trait,也可以在同一个类中使用'use keyword'来使用多个trait。

接口用于代码可重用性与特性相同

接口是扩展多个接口,所以我们可以解决多个继承问题,但当我们实现接口时,我们应该在类中创建所有的方法。 欲了解更多信息,请点击以下链接:

http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php

其他回答

该特征与我们可以用于多重继承目的和代码可重用性的类相同。

我们可以在类中使用trait,也可以在同一个类中使用'use keyword'来使用多个trait。

接口用于代码可重用性与特性相同

接口是扩展多个接口,所以我们可以解决多个继承问题,但当我们实现接口时,我们应该在类中创建所有的方法。 欲了解更多信息,请点击以下链接:

http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php

我认为trait对于创建包含方法的类是有用的,这些方法可以被用作几个不同类的方法。

例如:

trait ToolKit
{
    public $errors = array();

    public function error($msg)
    {
        $this->errors[] = $msg;
        return false;
    }
}

您可以在任何使用此特性的类中拥有并使用此“error”方法。

class Something
{
    use Toolkit;

    public function do_something($zipcode)
    {
        if (preg_match('/^[0-9]{5}$/', $zipcode) !== 1)
            return $this->error('Invalid zipcode.');
        
        // do something here
    }
}

而对于接口,你只能声明方法签名,而不能声明它的函数代码。此外,要使用接口,您需要遵循层次结构,使用实现。但特质却不是这样。

这是完全不同的!

基本上,您可以将trait视为代码的自动“复制-粘贴”。

使用trait是很危险的,因为在执行前我们无法知道它的作用。

然而,由于缺乏遗传等限制,性状更加灵活。

trait在注入方法时很有用,它可以检查类中是否存在另一个方法或属性。这是一篇不错的文章(但是是法语,抱歉)。

对于能读法语的人来说,GNU/Linux杂志HS 54有一篇关于这个主题的文章。

trait本质上是PHP对mixin的实现,实际上是一组扩展方法,可以通过添加trait将其添加到任何类中。然后,这些方法成为该类实现的一部分,但不使用继承。

来自PHP手册(强调我的):

trait是单继承语言(如PHP. ...)中代码重用的一种机制它是对传统继承的补充,并支持行为的水平组合;也就是说,类成员的应用不需要继承。

一个例子:

trait myTrait {
    function foo() { return "Foo!"; }
    function bar() { return "Bar!"; }
}

有了上面的特征,我现在可以做以下事情:

class MyClass extends SomeBaseClass {
    use myTrait; // Inclusion of the trait myTrait
}

在这一点上,当我创建一个MyClass类的实例时,它有两个方法,称为foo()和bar()——它们来自myTrait。注意,trait定义的方法已经有一个方法体,而接口定义的方法没有。

此外,PHP与许多其他语言一样,使用单一继承模型——这意味着一个类可以从多个接口派生,但不能从多个类派生。但是,PHP类可以包含多个trait——这允许程序员包含可重用的部分——就像包含多个基类一样。

有几件事需要注意:

                      -----------------------------------------------
                      |   Interface   |  Base Class   |    Trait    |
                      ===============================================
> 1 per class         |      Yes      |       No      |     Yes     |
---------------------------------------------------------------------
Define Method Body    |      No       |       Yes     |     Yes     |
---------------------------------------------------------------------
Polymorphism          |      Yes      |       Yes     |     No      |
---------------------------------------------------------------------

多态:

在前面的例子中,MyClass扩展了SomeBaseClass, MyClass是SomeBaseClass的一个实例。换句话说,像SomeBaseClass[] bases这样的数组可以包含MyClass的实例。类似地,如果MyClass扩展了IBaseInterface,则IBaseInterface[]基数组可以包含MyClass的实例。trait不存在这样的多态结构——因为trait本质上只是为了程序员方便而复制到每个使用它的类中的代码。

优先级:

如手册所述:

从基类继承的成员被Trait插入的成员覆盖。优先级顺序是当前类的成员重写Trait方法,Trait方法反过来重写继承的方法。

所以,考虑下面的场景:

class BaseClass {
    function SomeMethod() { /* Do stuff here */ }
}

interface IBase {
    function SomeMethod();
}

trait myTrait {
    function SomeMethod() { /* Do different stuff here */ }
}

class MyClass extends BaseClass implements IBase {
    use myTrait;

    function SomeMethod() { /* Do a third thing */ }
}

当创建MyClass的实例时,会发生以下情况:

Interface IBase需要提供一个名为SomeMethod()的无参数函数。 基类BaseClass提供了该方法的实现—满足需求。 trait myTrait也提供了一个名为SomeMethod()的无参数函数,它优先于baseclass版本 类MyClass提供了它自己的SomeMethod()版本-它优先于特征版本。

结论

Interface不能提供方法体的默认实现,而trait可以。 接口是一种多态的、可继承的结构,而特征则不是。 在同一个职业中可以使用多个接口,也可以使用多个特征。

接口定义了实现类必须实现的一组方法。

当trait被使用时,方法的实现也会出现——这在接口中不会发生。

这是最大的不同。

PHP RFC的水平重用:

trait是一种在单继承语言(如PHP)中代码重用的机制。Trait旨在通过允许开发人员在不同类层次结构中的几个独立类中自由地重用方法集来减少单个继承的一些限制。