1. trait的应用场景
1.1 实现代码的复用
<?php
// trait功能之一:实现代码的复用
trait tSum
{
// 写一个公共的求和方法
public function sum ($a,$b) {
return $a+$b;
}
}
class Sum1
{
use tSum;
}
class Sum2
{
use tSum;
}
echo (new Sum1)->sum(10,20);
echo '<hr>';
echo (new Sum2)->sum(10,20);
1.2 在继承上下文中的应用
<?php
// 在继承上下文中的应用,控制优先级,降低单继承的影响
trait tMiddle
{
public static function show () {
echo '访问到trait';
}
}
// 基类/父类
abstract class Big
{
public static function show () {
echo '访问到基类';
}
}
// 扩展类/子类
class Small extends Big
{
use tMiddle;
public static function show () {
echo '访问到扩展类';
}
}
// 实例化扩展类来访问,看看优先级(遇到同名方法时)
// 1. 不引入trait时
// Small::show();
// 此时输出:访问到扩展类,表示扩展类>基类
// 2.引入trait时,并注释掉扩展类中的方法
// Small::show();
// 此时输出:访问到trait,表示trait>基类
// 3.引入trait,保留扩展类中的方法
Small::show();
// 此时输出:访问到扩展类,表示扩展类>trait和基类
// 综合上述三种情况:当遇到同名方法时,且trait在子类中调用时,扩展类>trait>基类
1.3 trait实现功能的扩展
<?php
// trait功能之实现功能的扩展:组合运用多个trait
// 求和的trait
trait Sum
{
public static function sum (int $a, int $b) :int
{
return $a+$b;
}
}
// 求差的trait
trait Sub
{
public static function subtraction (int $a,int $b) :int
{
return $a-$b>=0 ? $a-$b :$b-$a;
}
}
// 把求和与求差组合为一个trait
trait Demo
{
use Sum,Sub;
}
// 求积的class
class Multi
{
// 如果要在这个类中再加入求和与求差,只需引入Sum和Sub这两个trait
use Sum,Sub;
public static function multiplication (int $a,int $b) :int
{
return $a*$b;
}
}
echo "两数之和是:".Multi::sum(10,20),'<hr>';
echo "两数之差是:".Multi::subtraction(10,20),'<hr>';
echo "两数相乘是:".Multi::multiplication(10,20),'<hr>';
// 求商的class
class Divi
{
// 如果把以上两个trait合并为一个,那么在这个类中只需引入一个trait即可实现求和与求差
use Demo;
public static function division (int $a,int $b) :float
{
return $a/$b;
}
}
echo "两数之和是:".Divi::sum(20,20),'<hr>';
echo "两数之差是:".Divi::subtraction(20,20),'<hr>';
echo "两数相除是:".Divi::division(20,20),'<hr>';
1.4 命名冲突的解决
<?php
// trait的命名冲突解决办法
trait tDemo1
{
public function show () {
echo 'aaaaaa','<hr>';
}
}
trait tDemo2
{
public function show () {
echo 'bbbbbb','<hr>';
}
}
trait tDemo3
{
public function show () {
echo 'cccccc','<hr>';
}
}
// 示例一:
trait tDemo4
{
// 两个trait中的方法名一样,有冲突,会报错,解决方案如下:
// 1. insteadof 操作符来明确指定使用冲突方法中的哪一个
use tDemo1,tDemo2,tDemo3
{
// tDemo1::show insteadOf tDemo2,tDemo3;
// tDemo2::show insteadOf tDemo3,tDemo1;
tDemo3::show insteadOf tDemo1,tDemo2;
}
}
class Work1
{
use tDemo4;
}
(new Work1)->show();
// 示例二:
trait tDemo5
{
use tDemo1,tDemo2
{
// 2. 使用 as 可以给trait中的方法取别名,但不是重命名,也不会影响其方法
tDemo1::show insteadOf tDemo2;
tDemo1::show as s1;
tDemo2::show as s2;
}
}
class Work2
{
use tDemo5;
}
(new Work2)->show();
(new Work2)->s1();
(new Work2)->s2();
<?php
// trait中as语法调整方法的访问控制
trait tDemo
{
public static function show () {
return __METHOD__;
}
}
class Work1
{
use tDemo;
}
echo Work1::show(),'<hr>';
class Work2
{
// 将访问控制修改为protected
use tDemo{
show as protected;
}
}
// 修改访问控制为protected以后,外部将不能调用
echo Work2::show();
1.5 trait与interface的组合
<?php
// trait与interface组合:可将实现类中的代码放入trait,然后再调用,让实现类的代码尽量简介、清晰
// if (!interface_exists(iDemo)) :
interface iDemo
{
public function show ();
}
// endif;
// if (!trait_exists(tDemo)) :
trait tDemo
{
public function show () {
return 'aaa';
}
}
// endif;
// if (!class_exists(Demo)) :
class Demo
{
use tDemo;
}
// endif;
echo (new Demo)->show();
1.6 trait与接口和抽象类之间的区别
它们之间的区别我就不做实例演示,这里谈下我的理解。
它们都有自己的应用场景,比如:trait用作代码复用,不仅可以把一些公共的方法放在其中,也可以把实现类的代码放在其中,方便调用;
接口用于将设计与实现完全分离,在实际项目中。肯定是不可或缺的,需要把设计方法放在其中,之后再去实现这个接口
抽象类,可以理解为一个中间层,用它去实现接口,并且使用抽象类实现接口的好处是可以不用实现接口的所有方法,还能把真正实现类的一些公共操作放在其中,也还能在抽象类中定义一些抽象方法,最终让子类去实现