作为PHP5.4引入新特性trait,有不少读者感觉比较陌生。
本篇从背景、作用、使用三个方面分析trait,力求以最通俗的语言解释清楚。
背景
PHP在面向对象的设计时, 被设计为单继承. 什么叫单继承, 一个类A只能继承一个类B, 再由B类继承C类. 而不能A类同时继承B类和C类.
正确的写法:demo1.php
<?php
class C{}
class B extends C{}
class A extends B{}
$obj = new A;
var_dump($obj);
会得到一个对象
/home/vagrant/Code/blog/demo1.php:11: object(A)[1]
//注意
//A类同时继承B类和C类, 这样写是不允许的
例如:
<?php
class C{}
class B{}
class A extends B,C{}
$obj = new A;
var_dump($obj);
//代码是错误的。
( ! ) Parse error: syntax error, unexpected token ",", expecting "{" in /home/vagrant/Code/blog/demo2.php on line 6
说人话, 就是一个儿子只能有一个父亲, 但是反过来呢?一个父亲是可以有多个儿子的.
如果想A类既使用B的方法又使用C的方法, PHP提出了接口(Interface). PHP虽然是单继承, 但是可以多实现. 什么叫多实现, 一个类A可以同时实现B接口和C接口.(demo3.php)
<?php
interface C{}
interface B{}
class A implements B,C{}
//A类同时实现接口B和接口C, 这样是允许的
$obj = new A;
var_dump($obj);
同样会得到一个对象
/home/vagrant/Code/blog/demo3.php:11: object(A)[1]
作用
关键词: 假多继承, 代码复用, 类的包含
现在有这样一个场景
需求: A类既可以使用B类的方法又可以使用C的方法, A类和B类是继承关系, 但是B和C没有继承关系, 怎么解决?
方法:A类继承B类, 同时实现C接口(demo4.php)
<?php
interface C{
public function ccc();
}
class B{
public function bbb()
{
echo "我是B类的bbb方法<br>";
}
}
class A extends B implements C{
public function ccc()
{
echo "我是A类,我要实现接口C定义的方法<br>";
}
}
$obj = new A;
$obj ->ccc();
$obj ->bbb();
~
~
强烈建议上面的代码,敲10遍以上。
(拓展: 接口)
接口不能被实例化
接口是为了规范显现它的子类,以达到统一的目的
接口中只有:常量和抽象方法
接口里的所有方法都是抽象方法,不能写方法体,并且,接口的抽象方法不需要写abstract。
子类继承接口的方法,叫 实现,所以说接口是用来实现的(涉及php基础知识)
如果这个时候有一个D类, 也要继承B类, 实现C接口, 需要重复的写这段代码(demo5.php)
<?php
interface C{
public function ccc();
}
class B{
public function bbb()
{
echo "我是B类的bbb方法<br>";
}
}
class A extends B implements C{
public function ccc()
{
echo "我是A类,我要实现接口C定义的方法<br>";
}
}
class D extends B implements C{
//这段代码是重复的
public function ccc()
{
echo "我是D类,我要实现接口C定义的方法<br>";
}
}
$obj = new A;
$obj ->ccc();
$obj ->bbb();
使用
思考: 能不能将这部分重复的代码定义在一个类中, A类和D类包含这个类呢?
这个时候trait应用而生(demo6.php)
<?php
interface C{
public function ccc();
}
class B{
public function bbb()
{
echo "我是B类的bbb方法<br>";
}
}
trait E{
public function ccc()
{
echo "我是trait,我实现了ccc方法<br>";
// echo "我是trait,我是".__CLASS__."类,我实现了ccc方法<br>";
}
}
class A extends B implements C{
use E;
}
class D extends B implements C{
use E;
}
$obj = new A;
$obj ->ccc();
$obj ->bbb();
运行结果如下:
我是trait,我实现了ccc方法
我是B类的bbb方法
上面的代码可以继续优化一下
通过上面的用法, 我们就可以这样总结
- trait本质上还是一个类
- 接口(interface)规定了方法的定义, trait规定了方法的实现
- 可以认为A类D类等等其他类在需要ccc方法的时候包含了trait
综上, trait是PHP实现多继承的一种折中的方法, 姑且叫它"假多继承", 可以认为一个类(A类)包含了另一个类E(trait), 最终目的是为了实现代码复用.