欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PHP 5.4 全新 trait 语法 PHPActiveRecordCacheOOP领域模型 

程序员文章站 2024-03-19 20:14:28
...

PHP 5.4 全新 trait 语法

文章转载于:http://blog.makingware.com/?p=97

<!-- .entry-meta -->
最近看 PHP 5.4 的 Changelog 时发现新增了 Trait 语法,根据 wiki 的说明,这个语法是为了解决代码的“水平复用”而诞生的。什么是“水平复用”呢?这里用一个相对简单的例子说明一下:

 

要理解水平复用,必须从 OOP 的 DRY 实践说起……
假设现在有三个业务类,它们都使用 ActiveRecord 模式实现持久化,那么代码看上去会像这样:

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

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

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

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

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

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

01 // 工具类
02 class Mixin {}
03 class ActiveRecord extends Mixin {}
04 class CacheBehavior extends Mixin {
05     // 重载源对象的查询方法
06     function find($self) {
07         // 通过传入参数获取原对象实例
08     }
09 }
10 // 业务类
11 class User extends ActiveRecord {}
12 class Article extends ActiveRecord {}
13 class 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,代码变成这样:

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

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

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

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

01 // 这样
02 class User {
03     use ActiveRecord, CacheBehavior;
04 }
05 class Manager extends User {}
06 // 或者
07 class 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 -->