【web开发】使用Trait解决PHP面向对象中类只支持单继承的限制

举报
迷彩 发表于 2023/07/26 15:22:10 2023/07/26
【摘要】 前言众所周知,PHP的面向对象和Java一样,类只支持单继承,即是一个类只能继承自一个父类,不能存在多个父类,这也很好理解,就像现实的人类社会一样,儿子继承自你的父亲,父亲继承自祖父。。。,但是在实际开发中很多时候我们想像c++一样使用多重继承。奈何PHP只能使用单继承,在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我们最应该了解的一下几点:

  1. Trait会覆盖调用类继承的父类方法
  2. 从基类继承的成员被Trait插入的成员所覆盖。优先顺序是:来自当前类的成员覆盖了Trait的方法,而Trait则覆盖了被继承的方法,其实就是有点就近原则的意思,先以自身的成员为主。
  3. Trait不能像类一样使用new直接实例化对象
  4. 在单个类中,使用use引入Trait,可以一次性引入多个Trait
  5. 单个Trait可有多个Trait组成,也就是可组成一个Trait的大集合,避免在类中使用use重复编写多余的代码,提升代码的可重用性。
  6. Trait支持使用诸如final,static,abstract等修饰关键字
  7. 可以使用insteadof及as操作符解决Trait之间的冲突
  8. 使用as语法还可以用来调整方法的访问控制
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。