phptrait多重继承的实现案例
自PHP5.4.0起,PHP实现了代码复用的一个方法,称为traits。
Traits是一种为类似PHP的单继承语言而准备的代码复用机制。Trait为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集。Traits和类组合的语义是定义了一种方式来减少复杂性,避免传统多继承和混入类(Mixin)相关的典型问题。
Trait和一个类相似,但仅仅旨在用细粒度和一致的方式来组合功能。Trait不能通过它自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用类的成员不需要继承。
在我理解说白了就是在继承类链中隔离了子类继承父类的某些特性(就是子类“要用父类的特性的时候”,如果trait有,就优先调用trait的方法、属性等)。
var.PHP_EOL;???}????functiona()???{???????echo"a".PHP_EOL;???}}?interfaceMyInterface{???function__construct();???functionb();}?abstractclassMyAbstract{???protected$var2="MyAbstract_var";????useMyTrait;????functionb()???{???????echo"b".PHP_EOL;???}}?classMyClassextendsMyAbstractimplementsMyInterface{???protected$var3="MyClass_var";????//也可以在这里引用,不区分继承关系???//useMyTrait;???functionc()???{???????echo"c".PHP_EOL;???}}?$class=newMyClass();$class->a();$class->b();$class->c();
输出结果
MyTrait_varabc
优先级
从基类继承的成员被trait插入的成员所覆盖,来自当前类的成员覆盖trait的方法。
classBase{???publicfunctionsayHello(){???????echo''Hello'';???}}
traitSayWorld{???publicfunctionsayHello(){???????parent::sayHello();???????echo''World!'';???}???publicfunctionsayHellos(){???????echo''HelloWorld!'';???}}
classMyHelloWorldextendsBase{???useSayWorld;
???publicfunctionsayHellos(){???????echo''HelloUniverse!'';???}}
$o=newMyHelloWwww.shanxiwang.netorld();$o->sayHello();?#HelloWorld!$o->sayHellos();?#HelloUniverse!3、多个trait
通过逗号分隔,在use声明列出多个trait,可以都插入到一个类中。
traitHello{???publicfunctionsayHello(){???????echo''Hello'';???}}
traitWorld{???publicfunctionsayWorld(){???????echo''World'';???}}
classMyHelloWorld{???useHello,World;}
$o=newMyHelloWorld();$o->sayHello();#Hello$o->sayWorld();#World4、多Traits冲突的解决
如果两个trait都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。
为了解决多个trait在同一个类中的命名冲突,需要使用insteadof操作符来明确指定使用冲突方法中的哪一个。
以上方式仅允许排除掉其它方法,as操作符可以将其中一个冲突的方法以另一个名称来引入。
traitA{???publicfunctionsmallTalk(){???????echo''a'';???}???publicfunctionbigTalk(){???????echo''A'';???}}
traitB{???publicfunctionsmallTalk(){???????echo''b'';???}???publicfunctionbigTalk(){???????echo''B'';???}}
classTalker{???useA,B{???????B::smallTalkinsteadofA;???????A::bigTalkinsteadofB;???}}
classTalkers{???useA,B{???????B::smallTalkinsteadofA;???????A::bigTalkinsteadofB;???????B::bigTalkasbTalk;???}}
$o=newTalker();$o->smallTalk();#b$o->bigTalk();#A
$os=newTalkers();$os->smallTalk();#b$os->bigTalk();#A$os->bTalk();#B5、修改方法的访问控制
还可以使用as语法来改变Traits中函数的访问权限属性。
traitHelloRuesin{???publicfunctionsayHello(){???????echo''HelloRuesin'';???}}
classHello{???useHelloRuesin{???????sayHelloasprotected;##修改sayHello的访问控制???}}
classRuesin{???useHelloRuesin{???????sayHelloasprivatesayHellos;##给方法一个改变了访问控制的别名而原版sayHello的访问控制则没有发生变化???}}
$o?=newHello();$os=newRuesin();#$o->sayHello();#无法访问$os->sayHello();#HelloRuesin#$os->sayHellos();#无法访问6、用Traits组成新Traits
正如类能够使用trait一样,其它trait也能够使用trait。在trait定义时通过使用一个或多个trait,它能够组合其它trait中的部分或全部成员。
traitHello{???publicfunctionsayHello(){???????echo''Hello'';???}}
traitRuesin{???publicfunctionsayRuesin(){???????echo''Ruesin'';???}}
traitHelloRuesin{???useHello,Ruesin;}
classSayHelloRuesin{???useHelloRuesin;}
$o=newSayHelloRuesin();$o->sayHello();?#Hello$o->sayRuesin();#Ruesin7、Trait的抽象成员
为了对使用的类施加强制要求,trait支持抽象方法的使用。表示通过抽象方法来进行强制要求
traitHello{???publicfunctionsayHelloRuesin(){???????echo''Hello''.$this->getName();???}???abstractpublicfunctiongetName();}
classHelloRuesin{???private$name;???useHello;???publicfunction__construct($name){???????$this->name=$name;???}???publicfunctiongetName(){???????return$this->name;???}}
(newHelloRuesin(''Ruesin''))->sayHelloRuesin();#HelloRuesin8、trait的静态成员
静态变量可以被trait的方法引用,但不能被trait定义。Traits能够为使用的类定义静态方法。
traitCounter{???publicfunctioninc(){???????static$c=0;???????$c=$c+1;???????echo"$c\n";???}
???publicstaticfunctionHelloRuesin(){???????#return''Doingsomething'';???????echo''HelloRuesin'';???}}
classC{???useCounter;}
$o=newC();?$o->inc();#1$o->inc();#2C::HelloRuesin();#HelloRuesin''9、Trait定义属性
文章来自ruesin.com
如果trait定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。如果该属性在类中的定义与在trait中的定义兼容(同样的可见性和初始值)则错误的级别是E_STRICT,否则是一个致命错误。
Ruesin''sBlogtraitPropertiesTrait{???public$x=1;}
classPropertiesExample{???usePropertiesTrait;???#public$same=true;#StrictStandards???#public$different=true;#致命错误}
$example=newPropertiesExample;$example->x;#1
总结:
从本质上说,trait和include文件的概念差不多
trait可以更加方便的实现代码复用,因为我们用继承关系实现的无法在父类中访问子类的private属性与方法,而trait就和把代码直接写在对象里效果一样。
使用trait时候应该坚决避免命名冲突,尤其是同时使用多个trait时。如果产生了命名冲突,如果两者的可见性、初始值、static与否完全相同,则trait中的会覆盖掉对象中的,并抛出E_STRICT错误,否则会抛出E_COMPILE_ERROR错误,终止编译。
|
|