【web开发】使用Trait解决PHP面向对象中类只支持单继承的限制
前言
众所周知,PHP的面向对象和Java一样,类只支持单继承,即是一个类只能继承自一个父类,不能存在多个父类,这也很好理解,就像现实的人类社会一样,儿子继承自你的父亲,父亲继承自祖父。。。,但是在实际开发中很多时候我们想像c++一样使用多重继承。奈何PHP只能使用单继承,在Trait出现之前,在PHP中要想实现多继承,只能使用接口,只有接口是可以实现多继承的,一个类可实现多个接口,而且接口和接口之间是可以多继承的。我们一般所说的类只能支持单继承是指类与类之间的关系。但是毕竟接口中的方法都是抽象方法,没有具体的实现,实现接口需要实现接口中的所有方法,有时候并不是我们想要的,因为我们很多时候不但要规范开发,还要能够使代码的可重用性提高,甚至达到代码重复使用价值的最大化。
从PHP5.4开始,便开始引入Trait这个特性。Trait和类非常相似,甚至他们的声明方式都相当接近,除了类是使用Class修饰,Trait使用trait标识符修饰外,其他几乎一模一样,包括成员属性和成员方法的定义。类中的一般特性Trait基本都可以实现,但很多会疑问,Trait到底是不是类的替代呢?可以肯定Trait并不是用来代替类的。而是混入类中,Trait是为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集。我们也可以简单理解为Trait是类的延伸。Trait和类组合的语义是定义一种方式来减少复杂性,避免传统多继承相关的典型问题。比如:需要同时继承两个抽象类,这是PHP所不支持的,Trait就是为了解决这个问题。或者可以理解为在继承类链中隔离了子类继承父类的某些特性,相当于要用父类的特性的时候,如果有Trait在就优先调用Trait中的成员。
Trait的声明
我们上面说到,声明Trait和声明class是相似的,只不过声明类需要使用class关键字,而声明Trait需要使用到trait关键字,类有的特性Trait基本都有,Trait支持final、static、和abstract等修饰词,所以Trait也就支持抽象方法的使用、类定义静态方法,当然也是可以定义属性。但是Trait无法像类一样使用new关键字进行实例化,因为Trait就是用来混入类中使用的,不能单独使用。所以我们前面也说了,Trait并不是用来代替类的,只是类的一种延伸。如果哪interface和Trait比较,Trait会有更多便捷的地方。接下来我们看看Trait是怎么声明和定义的。
注:使用Trait的需要在PHP5.4+
Trait声明代码如下:
<?php
trait TestTrait{
public $a = 1;
static $b = "HELLO";
function methods1(){
//todo
}
abstract public function methods2();//抽象方法
}
类(Class)声明的代码如下:
<?php
abstract class TestClass{
public $a = 1;
static $b = "HELLO";
function methods1(){
//todo
}
abstract public function methods2();//抽象方法
}
从上面代码的对比可以看出,除了trait和class关键字外,声明方式几乎一模一样。
Trait的使用
与类不同的是,Trait不能通过它自身来实例化对象,必须将其混入类中使用,相当于将Trait中的成员复制到类中,在应用类时就像使用自己的成员一样。其实就很像多模块手机,我们需要哪个模块或者模组,我们只需插入相关模块或者模组即可。如果要在类中使用Trait,需要通过use关键字将Trait混入类中。
<?php
trait TestTrait{
public $a = 1;
static $b = "HELLO";
function methods1(){
//todo
echo "这是methods1<br>";
}
function methods2(){
//todo
echo "这是methods2<br>";
}
}
class TestClass{
use TestTrait;//将TestTrait混入类中
}
//调用方式
$o1 = new TestClass();
$o1->methods1();
$o1->methods2();//就跟使用自己的方法一样
执行结果如下:
上面例子中,通过使用use关键字,在TestClass中混入了TestTrait中对成员。也可以通过use关键字一次混入多个Trait一起使用。通过逗号分隔(注意代码所说的符号一般都是英文半角的符号),在use声明列出多个Trait,可以都插入到一个类中,比如现在有2个Triat,分别命名为TestTrait1、TestTrait2.在TestClass中使用use关键字混入Trait。
<?php
class TestClass{
use TestTrait1,TestTrait2;//将TestTrait1,TestTrait2混入类中
}
虽然在实际的使用中我们可以一次性混入多个Trait,但是需要注意的是,多个Trait之间同时使用难免会发生冲突,为了避免此情况发生,在PHP5.4就带入了相关的关键字语法"insteadof",这个关键的意思是,当发生冲突时使用insteadof关键字进行替换为指定的Trait的相关成员,具体使用代码如下:
<?php
trait TestTrait1{
function methods1(){
//todo
echo "这是TestTrait1中的methods1<br>";
}
}
trait TestTrait2{
function methods1(){
//todo
echo "这是TestTrait2中的methods1<br>";
}
}
class TestClass{
use TestTrait1, TestTrait2{
TestTrait1::methods1 insteadof TestTrait2;
}//将TestTrait1,TestTrait2混入类中,如果发生冲突使用TestTrait1的methods1
function methods(){
//todo
echo "这是methods<br>";
}
}
//调用方式
$o1 = new TestClass();
$o1->methods1();
不仅可以在类中使用use关键字将Trait中的成员混入类中,也可以在Trait中使用use关键字将另外一个Trait中的成员混入进来,这样就形成了Trait之间的嵌套
<?php
trait TestTrait1{
function methods1(){
//todo
echo "这是TestTrait1中的methods1<br>";
}
}
trait TestTrait2{
use TestTrait1;//TestTrait1混入TestTrait2中
function methods2(){
//todo
echo "这是TestTrait2中的methods2<br>";
}
}
class TestClass{
use TestTrait2;//TestTrait2混入类中
function methods(){
//todo
echo "这是类中的methods<br>";
}
}
//调用方式
$o1 = new TestClass();
$o1->methods();
$o1->methods1();
$o1->methods2();
执行结果如下图:
为了对使用的类施加强制的要求,Trait支持抽象方法的使用.如果在Trait中声明需要实现抽象方法,这样就能让使用它的类必须现实现它,这个跟继承一个抽象类是一样的,必须先实现类中的抽象方法.
<?php
trait TestTrait{
abstract public function methods();
}
class TestClass{
use TestTrait;//将TestTrait混入类中
function methods(){
//todo
echo "这是methods<br>";
}
}
//调用方式
$o1 = new TestClass();
$o1->methods();
总结
上面我们详细介绍了Trait比较常见的基本应用。使用Trait我们最应该了解的一下几点:
- Trait会覆盖调用类继承的父类方法
- 从基类继承的成员被Trait插入的成员所覆盖。优先顺序是:来自当前类的成员覆盖了Trait的方法,而Trait则覆盖了被继承的方法,其实就是有点就近原则的意思,先以自身的成员为主。
- Trait不能像类一样使用new直接实例化对象
- 在单个类中,使用use引入Trait,可以一次性引入多个Trait
- 单个Trait可有多个Trait组成,也就是可组成一个Trait的大集合,避免在类中使用use重复编写多余的代码,提升代码的可重用性。
- Trait支持使用诸如final,static,abstract等修饰关键字
- 可以使用insteadof及as操作符解决Trait之间的冲突
- 使用as语法还可以用来调整方法的访问控制
- 点赞
- 收藏
- 关注作者
评论(0)