trait的五种应用场景
- php中继承是单继承,如果某个类有成员要被其他类使用,就需要称为其他类的父类才行,这样可能会导致继承链会长,合适吗?
引入:继承的角度出发,继承链可以解决问题,但是的确效率会打折扣,同时,如果某些功能是共性使用,但是并不符合(不属于同一类)继承条件,那么使用继承也有所违背面上对象规则,此时可以使用php提供的另外一种代码复用技术trait
1:代码复用
2:在继承上下文中的应用
3:实现功能扩展
4:在trait组合中命名冲突的解决方案
5:trait和interface的组合
1.代码复用
- 定义:trait是为类似php的单继承语言而准备的一种代码复用机制,trait可以使得但继承语言为了复用而不得不继承的尴尬,让面向对象更加纯粹
- 1-1、trait是一种类似class的关键字
<?php
// 定义trait
trait show{
}
- 1-2、trait内部可以像类一样拥有成员属性(包含静态),成员方法(包含静态),但不能有常量
示例:
trait uSername //定义trait
{
public function show() //定义一个公用的方法
{
return '姓名:'.$this->username.'|国家:'.$this->nation.'|电话:'.$this->mobile;
//返回一段用户信息
}
public static function show1() //定义一个show1静态方法
{
printf('<pre>%s</pre>',print_r(get_class_vars(__CLASS__),true));
//打印当前类返回的数组get_class_vars
}
}
class User //定义一个User类
{
use uSername; //调用trait
protected $username ='曹操'; //为隐私属性赋值
public $nation ='魏国'; //为公用属性赋值
private $mobile ='13666666666';//为私有属性赋值
}
echo (new User)->show(); //实例化User调用trait
echo '<hr>';
class User1 //定义一个User1类
{
use uSername; //调用trait
public $username ='刘备'; //为公用属性赋值
private $nation ='蜀国'; //为私有属性赋值
protected $mobile ='13555555555'; //为隐私属性赋值
}
echo (new User1)->show(); //实例化User调用trait
echo '<hr>';
User::show1(); //使用静态实例化调用trait中的show1方法
echo '<hr>';
User1::show1(); //使用静态实例化调用trait中的show1方法
示例图
学习心得:在使用trait中能体会到在类中需要引入外部的数据或者方法很繁琐,使用trait可以任意调用使用。
2:在继承上下文中的应用
- 在实例中创建了trait及抽象类和子类,知道了通过实例化子类对程序的执行顺序。
示例:
trait aTtribute //创建trait
{
public static function show() //创建一个静态的show方法
{
echo '我是trait->show<br>';
return Attribute1::show(); //返回一条字符串
}
}
abstract class Attribute1 //抽象类不可以被实例化
{
use aTtribute; //调用trait
public static function show() //抽象类中创建一个静态的show方法
{
//返回trait的show方法
return '我是父类show<br>';
}
}
class Attribute2 extends Attribute1 //创建Attribute的子类
{
use aTtribute; //调用trait
public static function show() //创建一个静态的show方法
{
echo '我是子类show<br>';
return aTtribute::show(); //返回抽象类的的show方法
}
}
echo Attribute2::show(); //输出Attribute2子类的show()方法
//子类同名成员优先大于父类同名成员
//如果子类、父类、trait中存在同名方法的时候,而trait在子类中调用
//先调用子类->在调用trait->父类
总结:在编写程序的过程中,知悉了trait在上下文中的执行顺序
3:实现功能扩展
示例:
trait tDemo
{
//1.打印所有属性
public function getProps()
{
printf('<pre>%s</pre>',print_r(get_class_vars(__CLASS__),true));
}
}
trait tDemo1
{
//2.打印所有方法
public function getMethost()
{
printf('<pre>%s</pre>',print_r(get_class_methods(__CLASS__),true));
}
//__CLASS__:返回当前类的名称字符串
//self:返回当前类的引用
}
trait tDemo2
{
use tDemo1,tDemo;
}
class Users
{
use tDemo2;
public $name ='刘备';
public $nation ='蜀国';
public function show()
{
return $this->name.':'.$this->nation;
}
//扩展这个类的功能
//添加二个公共方法
}
echo (new Users)->show();
echo (new Users)->getProps();
echo (new Users)->getMethost();
示例图
总结:将两个trait放到一个trait中在类里调用,通过实例化将trait实例化,程序中可以看出,trait可以为类扩展出无限个方法和属性。
4:在trait组合中命名冲突的解决方案
- 在trait中有重名的方法,可以使用insteadOf替换将重名的的方法重命名。
- 格式
trait名称::重名的方法 as 新的命名;
trait名称::重名的方法 insteadOf替换 trait名称::重名的方法;
示例:
<?php
trait tDemo
{
public static $car = '汽车';
public static function show() //定义的了静态show方法
{
return self::$car;
}
}
trait tDemo1
{
public static $train = '火车';
public static function show() //定义的了静态show方法
{
return self::$train;
}
}
trait tDemo2
{
use tDemo, tDemo1 {
tDemo1::show as toshow;
// 给tDemo1::show()起个别名: toshow
tDemo::show insteadOf tDemo1;
// 调用tDemo1::show()替换掉tDemo::show()
}
}
class Work
{
use tDemo2;
}
echo (new Work)->show();
echo '<hr>';
echo (new Work)->toshow();
5:trait和interface的组合
通过trait实现interface接口的方法,然后实例化
示例
<?php
interface iDemo //定义一个接口
{
public static function index();
}
trait tDemo //定义一个trait
{
public static function index() //在tDemo中定义一个接口方法
{
return '我是在trait中的!';
}
}
class Hello implements iDemo //定义一个类实现iDemo
{
use tDemo; //类中应用trait
}
echo (new Hello)->index();
//本实例定义一个接口,然后在trait中实现接口方法,然后实例化
示例图