fumingxia 发表于 2017-4-3 11:14:53

PHP 5.4 全新 trait 语法

PHP 5.4 全新 trait 语法
文章转载于:http://blog.makingware.com/?p=97
发表于 2011 年 06 月 08 日 由 Jeffrey Au
<!-- .entry-meta -->最近看 PHP 5.4 的 Changelog 时发现新增了 Trait 语法,根据 wiki 的说明,这个语法是为了解决代码的“水平复用”而诞生的。什么是“水平复用”呢?这里用一个相对简单的例子说明一下:  要理解水平复用,必须从 OOP 的 DRY 实践说起……
假设现在有三个业务类,它们都使用 ActiveRecord 模式实现持久化,那么代码看上去会像这样:
view source
print?

1// 工具类
2class ActiveRecord {}
3// 业务类
4class User extends ActiveRecord {}
5class Article extends ActiveRecord {}
6class Log extends ActiveRecord {}


  然后,你发现 User 和 Article 的内容需要经常被读取,为了加快读取速度要用上 Cache,而 Log 大部分时候都是写入,很少读取,不需要使用 Cache。传统方法上,为了实现 Cache 读取,会把代码写成这样:
view source
print?

01// 工具类
02class ActiveRecord {}
03// 业务类
04class User extends ActiveRecord {
05    // 重载查询方法
06    function find() {
07        // 判断 Cache 在不在,读 Cache 然后返回
08    }
09}
10class Article extends ActiveRecord {
11    // 重载查询方法
12    function find() {
13        // 判断 Cache 在不在,读 Cache 然后返回
14    }
15}
16class Log extends ActiveRecord {}


  这样明显有个问题,User 和 Article 使用 Cache 的代码是重复的!这时你可能想到把可以被 Cache 的类继承到另外一个叫 CachedActiveRecord 里面,把代码写成这样:
view source
print?

1// 工具类
2class ActiveRecord {}
3class CachedActiveRecord extends ActiveRecord {}
4// 业务类
5class User extends CachedActiveRecord {}
6class Article extends CachedActiveRecord {}
7class Log extends ActiveRecord {}


  这样的确解决了重复 Cache 代码的问题,但另外一个问题又来了:如果我要在 User 和 Article 实现不同的 Cache 策略呢?
这种方式只能实现固定的 Cache 策略,虽然可以通过 if..else../switch 来实现,但这样显然不符合 OOP 的原则。
于是有人实现了 Mixin 模式,把需要重用的代码抽到一个独立的类里面,然后令所有类继承都一个 Mixin 工具类,工具类通过 PHP 特有的 __set / __get / __call 等方式把需要重用的类塞到当前类里面,代码会变成这样子:
view source
print?

01// 工具类
02class Mixin {}
03class ActiveRecord extends Mixin {}
04class CacheBehavior extends Mixin {
05    // 重载源对象的查询方法
06    function find($self) {
07        // 通过传入参数获取原对象实例
08    }
09}
10// 业务类
11class User extends ActiveRecord {}
12class Article extends ActiveRecord {}
13class Log extends ActiveRecord {}
14  
15// 调用方式
16$user = new User();
17$user->attach(new CacheBehavior());
18$user->find(); // 此时调用的是 CacheBehavior 的 find 方法


  这样貌似解决了代码重用的问题了……且慢!你没发现这样的话所有类都必须继承 Mixin 工具类吗?如果某天需要用上一个不 Mixin 的 ActiveRecord 那岂不是需要把它复制一份?
于是这里就需要 trait 语法的粉墨登场了!先把 CacheBehavior 变成 trait,代码变成这样:
view source
print?

01// 工具 trait
02trait CacheBehavior {
03    function find() {}
04}
05// 工具类
06class ActiveRecord {}
07// 业务类
08class User extends ActiveRecord {
09    use CacheBehavior;
10}
11class Article extends ActiveRecord {
12    use CacheBehavior;
13}
14class Log extends ActiveRecord {}


  这样就实现了刚才使用了 Mixin 工具类才能实现的效果,代码量更少更简洁!
进一步思考,其实 ActiveRecord 也属于工具类而不是业务类(领域模型),那实际上也是应该拿出来的,于是最后代码写成了:
view source
print?

01// 工具 trait
02trait ActiveRecord {}
03trait CacheBehavior {
04    function find() {}
05}
06// 业务类
07class User {
08    use ActiveRecord, CacheBehavior;
09}
10class Article {
11    use ActiveRecord, CacheBehavior;
12}
13class Log {
14    use ActiveRecord;
15}
16  
17// 调用方式
18$user = new User();
19$user->find(); // 此时调用的是 CacheBehavior 的 find 方法,省略了 attach 步骤


  代码终于和谐了!当然,ActiveRecord 和 CacheBehavior 之间的关联性还有待解决,不过这里就不纠结这个问题了=v=b
使用 trait 之后,某天出现了一个新的业务类 Manager 也只需轻松继承或 use 一下即可:
view source
print?

01// 这样
02class User {
03    use ActiveRecord, CacheBehavior;
04}
05class Manager extends User {}
06// 或者
07class Manager {
08    use ActiveRecord, CacheBehavior;
09}
10  
11// 直接调用
12$manager = new Manager();
13$manager->find(); // 同样是使用 CacheBehavior 的 find 方法


  从此对“水平复用”代码表示毫无鸭梨!
更多说明请查看官方 wiki:https://wiki.php.net/rfc/horizontalreuse


<!-- .entry-content -->
页: [1]
查看完整版本: PHP 5.4 全新 trait 语法