我最近一直在努力研究PHP,我发现本身陷入了特质。 我理解水平代码重用的概念,而不是必须从抽象类继承。 我不明白的是:使用特征与接口之间的关键区别是什么? php
我已经尝试过寻找一个体面的博客文章或解释什么时候使用其中一个的文章,但到目前为止我发现的例子看起来很是类似。 程序员
一个常常用来描述Traits的比喻是Traits是与实现的接口。 数组
在大多数状况下,这是一种很好的思考方式,但二者之间存在一些细微差异。 函数
首先, instanceof
运算符不能使用traits(即,trait不是真实对象)因此你不能让咱们看看一个类是否具备某种特征(或者看看两个不相关的类是否共享一个特征)特征)。 这就是它们做为水平代码重用的构造的意思。 oop
PHP中如今有一些函数可让你得到一个类使用的全部特征的列表,但特征继承意味着你须要进行递归检查以可靠地检查一个类在某个时刻是否具备特定的特征(这里有一个例子) PHP doco页面上的代码)。 可是,它确定不像instanceof那么简单和干净,恕我直言,这是一个可使PHP变得更好的功能。 spa
此外,抽象类仍然是类,所以它们不能解决与多继承相关的代码重用问题。 请记住,您只能扩展一个类(实际或抽象),但实现多个接口。 .net
我发现traits和接口很是适合用手建立伪多重继承。 例如: code
class SlidingDoor extends Door implements IKeyed { use KeyedTrait; [...] // Generally not a lot else goes here since it's all in the trait }
这样作意味着您可使用instanceof来肯定特定的Door对象是否为Keyed,您知道您将得到一组一致的方法等,而且全部代码都位于使用KeyedTrait的全部类中的一个位置。 对象
基本上,您能够将特征视为代码的自动“复制粘贴”。 继承
使用特征是危险的,由于在执行以前没有必要知道它的做用。
可是,因为缺少继承等限制,特征更加灵活。
特征能够用于注入将某些东西检查到类中的方法,例如,存在另外一个方法或属性。 一篇很好的文章(但用法语,对不起) 。
对于可以得到它的法语读者,GNU / Linux Magazine HS 54有一篇关于这个主题的文章。
一个trait
是基本上是PHP的一个实施的mixin
,而其实是一组能够经过添加的添加到任何类的扩展方法trait
。 而后,这些方法成为该类实现的一部分,但不使用继承 。
从PHP手册 (强调个人):
特征是一种在单继承语言(如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
定义的方法已经有一个方法体 - Interface
定义的方法不能。
另外 - 与许多其余语言同样,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[] bases
能够包含MyClass
实例。 没有这样的多态结构可用于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
-version MyClass
class
提供了本身的SomeMethod()
版本 - 它优先于trait
-version。 结论
Interface
不能提供方法体的默认实现,而trait
能够。 Interface
是一个多态的 , 继承的构造 - 而trait
则不是。 Interface
能够在同一个类中使用,所以可使用多个trait
。 特征仅用于代码重用 。
接口只提供要在类中定义的函数的签名 ,能够根据程序员的判断使用它。 从而为咱们提供了一组课程的原型 。
供参考 - http://www.php.net/manual/en/language.oop5.traits.php
主要区别在于,使用接口,您必须在实现所述接口的每一个类中定义每一个方法的实际实现,所以您可让许多类实现相同的接口但具备不一样的行为,而traits只是注入的代码块一类; 另外一个重要的区别是特征方法只能是类方法或静态方法,不像接口方法也能够(一般是)实例方法。