为什么 php 会引入 Trait ? 我们先来看看软件开发中的两种常用代码复用模式,继承和组合 。
- 继承:强调 父类与子类 的关系,即子类是父类的一个特殊类型;
- 组合:强调 整体与局部 的关系,侧重的一种需要的关系;
Trait 的提出弥补了 PHP 对组合支持的不足,一个 Trait 就相当于一个模块,不同的 Trait 以组合的方式注入到类中 。我们以 Laravel 的控制器为例,来介绍下继承和组合是如何在具体的场景中使用的 。
首先,底层的代码应当多使用组合 。Laravel 的底层控制器只继承了一个简单的控制器 IlluminateRoutingController,结构相对稳定 。同时,控制器使用了不同的 Trait 来组织代码,避免了对象的臃肿,极大程度的保持了架构的灵活性 。
<?phpnamespace AppHttpControllers;use IlluminateFoundationAuthAccessAuthorizesRequests;use IlluminateFoundationBusDispatchesJobs;use IlluminateFoundationValidationValidatesRequests;use IlluminateRoutingController as BaseController;class Controller extends BaseController{use AuthorizesRequests, DispatchesJobs, ValidatesRequests;}
而具体的业务逻辑或顶层代码应当多使用继承,这样能够大大提高的开发效率<?phpuse AppHttpControllersController;class UserController extends Controller{}
以上就是继承和组合的简单介绍 。接下来看看 Trait 的具体使用 。使用规范Symfony 编码规范建议在每个 Trait 之后添加 Trait 关键字 。
namespace SymfonyContractsTranslation;trait TranslatorTrait {}
PSR-12 规范建议在每个 Trait 使用一个 use 语句来声明,同时 Trait 与类的其他成员需要保持一行空行 。class ClassName{use FirstTrait;use SecondTrait;use ThirdTrait;public $a;}
成员Trait 中可包含属性、方法 与 抽象方法,这三者的结合既可以复用代码,也可以对代码的使用作出一些约定,例如 Laravel 中的自动维护 slug 字段<?phpnamespace AppTraits;use IlluminateSupportStr;trait HasSlug {public static function bootSluggable(){static::saving(function ($model) {$model->slug = Str::slug($model->getAttribute( $model->sluggable() ));});}/*** Slug 字段** @return string*/abstract public function sluggable(): string;}
Trait 中也可以包括静态属性和静态方法,以下是一个单例模式的简单封装 。trait Singleton{private static $instance;public static function getInstance() {if (!(self::$instance instanceof self)) {self::$instance = new self;}return self::$instance;}}
每个 Trait 中可以包含其他 Trait,进一步提高了代码的灵活性<?phptrait Hello{function sayHello() {echo "Hello";}}trait World{function sayWorld() {echo "World";}}class MyWorld{use Hello;use World;}
Trait 与类的冲突处理当存在同名方法时,当前类的方法会覆盖 Trait 中的方法,而 Trait 中的方法会覆盖父类的方法 。<?php// 父类,优先级最低class Base {public function sayHello() {echo 'Hello ';}}// Trait 优先级大于父类trait SayWorld {public function sayHello() {parent::sayHello();echo 'World!';}}// 当前类,优先级最高class MyHelloWorld extends Base {use SayWorld;}$o = new MyHelloWorld();$o->sayHello();// Hello, World
当存在同名属性时,类的属性必须与 Trait 的属性兼容(相同的访问性、相同的初始值),否则会报致命错误 。<?phptrait Foo {public $same = true;public $different = false;}class Bar {use Foo;public $same = true; // 合法public $different = true; // fatal error}
Trait 与 Trait 的冲突处理当一个类包含多个 Trait 时,不同 Trait 之间可能会存在属性和方法的冲突 。当存在相同属性时,属性必须兼容,跟 Trait 与类的冲突处理类似
Trait A {public $a = 'foo';}Trait B {public $a = 'foo';}class Foo {use A;use B;}
当存在方法冲突时,需要使用 insteadof 来手动处理冲突,否则会报致命错误<?phpTrait A {public function foo(){return "A foo";}}Trait B {public function foo(){return "B foo";}}class Bar {use A, B {B::foo insteadof A;// 用 B 的 foo 方法来代替 A}}$bar = new Bar();echo $bar->foo();// B foo
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Python编程语言的核心是什么?
- 第九颗行星是否存在? 第九大行星是黑洞
- 手写微服务核心技术——负载均衡算法
- 恒品茗茶的品牌发展,共筑浮梁茶核心品牌
- windows 10 下docker布置nginx+php环境,用宿主WEB目录负载均衡
- java多线程编程的核心——AQS独占模式原理解析
- CDN的本质只是加速器?其实它才是互联网的核心精神的集大成者
- 月球的光芒 月球闪光之谜这篇文章
- 黑洞能把地球吞噬吗 黑洞的吞噬范围
- 地球和月球照片 月球拍地球的照片