php继承---trait代码复用
思考:php中继承是单继承,如果某个类有成员要被其他类使用,就需要成为其他类的父类才行,这样可能会导致继承链会长,合适吗?
引入:从继承的角度出发,继承链可以解决问题,但是的确效率会打折扣,同时,如果某些功能是共性使用,但是并不符合继承条件(不属于同一类)
那么使用继承也有所违背面上对象规则,此时可以使用php提供的另外一种代码复用技术trait
trait代码复用【掌握】
定义:trait是为类似php的单继承语言而准备的一种代码复用机制,trait可以使单继承语言为了复用而不得不继承的尴尬,让面向对象更加纯粹
1.trait是一种类似class的关键字
<?php // 定义trait trait show{ }
2.trait内部可以像类一样拥有成员属性(包含静态),成员方法(包含静态),但不能有常量
<?php // 定义trait trait show{ public $name; protected $host; //允许定义,但实际不用 private $info; public function show_time(){ echo $this->info; } protected function showhost(){ //允许定义,但实际不用 echo $this->name; } const pi=3.1415926; //错误 trait中不能有常量 }
3.trait是用来实现代码的复用的,不可以被实例化也不可以被继承
<?php // 定义trait trait show{ public $name; protected $host; //允许定义,但实际不用 private $info; public function show_time(){ echo $this->info; } protected function showhost(){ //允许定义,但实际不用 echo $this->name; } } new show(); //trait 不允许被实例化
4.trait是用来将公共代码提供给其他类使用的,而类要使用trait的掐你是加载对应的trait
<?php // 定义trait trait Show{ public $name="d"; public function show_time(){ echo $this->name; } } //类中加载trait class add{ //加载:使用use关键字 use Show; //use 就表示将trait show中所有的东西拿到了当前类add中 } //使用trait中的内容 $s=new add(); $s->show_time(); //show add类中自己没有show_time方法,但是因为使用了trait show 所以可用
5.一个类可以使用多个trait
<?php // 定义trait trait Show{ public $name="d"; public function show_time(){ echo $this->name; } } trait get{ public $age=18; public function get_age(){ echo $this->age; } } //类中加载trait class add{ //加载:使用use关键字 use Show; //use 就表示将trait show中所有的东西拿到了当前类add中 use get; } //使用trait中的内容 $s=new add(); $s->show_time(); //show add类中自己没有show_time方法,但是因为使用了trait show 所以可用 $s->get_age();
6.如果同时引入的多个trait中有同名方法,那么会产生冲突:解决冲突的方法是使用insteadof代替处理以及对被代替的方法使用别名
<?php // 定义trait trait show{ public $name="d"; public function show_time(){ echo $this->name; } } trait get{ public $age=18; public function show_time(){ echo $this->age; } } //类中加载trait class add{ // use get,show; //错误 2个trait存在同名的方法 // 方案1 使用其中一个同名方法 use get,show{ // 意思 使用show中的show_time 代替get中的show_time,并且会被覆盖无法使用 show::show_time insteadOf get; // 方案2 给get中的show_time取一个别名 就可以使用了。 get::show_time as show_insteadostime; } } $s=new add(); $s->show_time(); //输出的是d 而不是18 代替成功 $s->show_insteadostime(); //显示18
7.同名覆盖问题:如果类中有与引入的trait同名成员,会有不同处理
属性:不允许重名,即类中不允许定于与trait中同名的成员属性(静态属性也一样)
方法:类覆盖trait
<?php // 定义trait trait show{ public $name="d"; public function show_time(){ echo $this->name; } } trait get{ public $age=18; public function show_time(){ echo $this->age; } } //类中加载trait class add{ // use get,show; //错误 2个trait存在同名的方法 // 方案1 使用其中一个同名方法 use get,show{ // 意思 使用show中的show_time 代替get中的show_time,并且会被覆盖无法使用 show::show_time insteadOf get; // 方案2 给get中的show_time取一个别名 就可以使用了。 get::show_time as show_insteadostime; } } $s=new add(); $s->show_time(); //输出的是d 而不是18 代替成功 $s->show_insteadostime(); //显示18
8.继承覆盖问题:如果类中在使用trait的同时,也是继承自父类,而trait中与父类中有同名方法,那么trait中将覆盖父类同名方法,如果要访问父类方法,
可以在trait同名方法中使用parent关键字访问父类同名方法。
<?php // 代码复用 trait posen{ public $name="杜伟"; public $age=31; public function show(){ // 如果想用父类中的方法 parent::show(); echo $this->name.$this->age; } } // 父类 class human{ public function show(){ echo 'human::show<hr>'; } } // 定义子类 class son extends human{ use posen; } // 实例化 $s=new son(); $s->show(); //显示的是杜伟 31 trait中的方法会覆盖继承中父类的方法
9.另外,trait自己不能访问,只是用来给其他类提供代码复用的,因此允许类在使用trait时更高里面方法的访问控制权。在as 之后,使用目标访问修饰限定符。
<?php // 代码复用 trait posen{ private function show(){ echo '你看到我了'; } } class son{ use posen{ //因为我只是引用了一个show,如果是两个的话 需要具体的体现是哪个 posen::show(这个) show as public pshow; //注意 as是用来起别名的,虽然没有同名show 但是系统认为已经存在,并且改变trait中show的访问权限为public 变成了一个新的方法,原来的方法不动。 } } // 实例化 $s=new son(); $s->pshow(); //调用别名方法
10.trait中可以使用抽象方法,用来规范使用类必须实现对应抽象方法,使用了欸要么为抽象类,要么就必须实现抽象方法
<?php // 代码复用 trait posen{ abstract public function add(); //trait 定义抽象方法 } // 第一种方式 abstract class man{ use posen; //抽象类可以不实现抽象方法 } // 第二种 class son{ use posen; public function add(){ echo '实现了trait中的抽象方法'; //实现抽象方法 } }
总结:
1.trait的hi一种类似class结构关键字,trait不能被实例化,可以拥有所有类成员结构
2.trait是用来实现代码复用的,为其他类提供公共代码(方法),其他类如果使用trait用use关键字引入
3.在了类中use是具体trait就相当于将trait内部的所有代码在类中写了一遍
4.一个类中可以有多个trait,但是要注意同名问题
同名方法可以使用insteadof来实现代替:一个trait中的同名方法代替另外一个,类就访问替代的那个
同名方法可以在被替代之后使用as制作方法使用as制作方法别名:类就可以拥有两个方法
5.类中在引入trait后,要注意与trait中的同名成员问题
同名属性:不允许
同名方法:允许,类中的方法会覆盖trait中的方法
6.如果在使用trait的同时也继承其他类,那么trait中出现的同名方法会覆盖基类的同名方法
7.类在使用trait时可以修改trait方法的控制级别,更严或这更宽松都可以,注意修改控制级别时使用的时别名机制,一定要改成别名:trait名::方法名 as 访问修饰限定符 别名;
8.trait中可以使用抽象方法,那么使用该trait的类就必须本身为抽象类或者将抽象方法实现
9.trait使用机制
有公共代码要实现(方法),而这些方法可能在很多类中会用到
公共代码不是属于某一类事务特有,而是很多事务都有(不符合继承)