一 trait
php是单继承的语言,无法同时从两个基类中继承属性和方法,为了解决这个问题,php出了Trait这个特性.
个人理解的trait是: trait = abstract class - interface,其中拥有interface的多个继承的性质.'继承'的方式是 use trait;
二 单例模式
单例模式确保类在全局只能有一个实例,因为它的实例是由自己保存,在类的外部也无法对该类进行实例化.PHP的单例模式是为了避免重复创建对象带来的资源消耗.
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
实际项目中像数据库查询,日志输出,全局回调,统一校验等模块。这些模块功能单一,但需要多次访问,如果能够全局唯一,多次复用会大大提升性能。
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
三 trait与单例模式结合使用
3.1 简单demo
trait Singleton { private static $instance; static function getInstance(...$args) { if(!isset(self::$instance)){ self::$instance = new static(...$args); } return self::$instance; } } class MySingleton { use Singleton; } $mySingleton = Mysingleton::getInstance();
3.2 为什么不用 new self()
new self() 和 new static 的区别: 只有在继承中才能体现出来、如果没有任何继承、那么二者没有任何区别 然后 new self() 返回的实列是不会变的,无论谁去调用,都返回的一个类的实列, 而 new static则是由调用者决定的。
此处如果用new self() ,那么我们获取到的对象就不是MySingleton了
3.3 类,基类,trait之间同名函数的优先级
先说结论,类 > trait > 基类,简单实例:
<?php trait Dog{ public $name="dog"; public function drive(){ echo "This is dog drive"; } public function eat(){ echo "This is dog eat"; } } class Animal{ public function drive(){ echo "This is animal drive"; } public function eat(){ echo "This is animal eat"; } } class Cat extends Animal{ use Dog; public function drive(){ echo "This is cat drive"; } } $cat = new Cat(); $cat->drive(); echo "<br/>"; $cat->eat(); ?>
得出:
This is cat drive
This is dog eat
3.4 特殊情况,基类use trait,本类不use
trait A{ private static $instance; static function getInstance() { if(!isset(self::$instance)){ self::$instance = new static(); } return self::$instance; } } class B{ use A; function a() { var_dump('call at B'); } } class C extends B{ function a() { var_dump('call at c'); parent::a(); } } class D extends B{ use A; function a() { var_dump('call at D'); parent::a(); } } $b = B::getInstance(); $c = C::getInstance(); $d = D::getInstance(); $c->a(); $d->a();
得出:
string(9) "call at B" string(9) "call at D" string(9) "call at B"
此时我们与疑问了,根据3.2所写,调用者是 C::getInstance(); 怎么$c->a();像变成了 $b->a();一样呢???
(个人观点,也验证了)其实这里的$c,就是class B
接下来进行验证:
trait A{ private static $instance; static function getInstance() { if(!isset(self::$instance)){ self::$instance = new static(); } return self::$instance; } } class B{ use A;function h(){ var_dump('h call at b'); } } class C extends B{function h(){ var_dump('h call at c'); } }$b = B::getInstance(); $c = C::getInstance();$c->h();
得出:
string(11) "h call at b"
而我们去掉 class c 中的 h(), 会没有打印,验证了 这里的$c,就是class B 这句话.
本篇就讲这么多,有兴趣的可以更深入了解trait与单例模式的知识点.