首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络技术 > 网络基础 >

PHP 5.4 崭新 trait 语法

2012-10-25 
PHP 5.4 全新 trait 语法PHP 5.4 全新 trait 语法文章转载于:http://blog.makingware.com/?p97发表于 201

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 sourceprint?1// 工具类 2class ActiveRecord {} 3// 业务类 4class User extends ActiveRecord {} 5class Article extends ActiveRecord {} 6class Log extends ActiveRecord {}

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

view sourceprint?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 sourceprint?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 sourceprint?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 sourceprint?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 sourceprint?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 sourceprint?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 楼 zdk2116 2012-03-01   我觉得你说的这些可以总结为一点:就是为了解决一个多继承的问题。
你说的有点复杂了。

热点排行