百度首页 | 百度空间
 
查看文章
 
Observer在PHP中的应用
2008年03月03日 星期一 下午 04:30
作者:老王

什么样的代码算是好代码?逻辑区分清晰的代码就是好代码。可惜总有一些难啃的骨头,让我们的代码从好变坏,下面讲一个例子。

假设我们的代码是类似CakePHP风格的,其中有一个模型Article,还有一个控制器ArticlesController。

加入一些需求:

我们在ArticlesController控制器中操作Article模型的时候,如果执行了模型的save操作,那么就执行一些动作,比如说发送一封邮件,告诉老板某个文章已经保存了(老板说不定会因为这个功能而更加欣赏你)。

需求本身并不复杂,但是在哪里实现需求却让人有些头疼。

最先想到的位置是Model,因为ActiveRecord风格的Model本身一般已经存在beforeSave这样现成的回调方法,我们只要把逻辑加入其中即可,这样确实实现了需求,但是仔细想想,就会发现,需求所涉及的逻辑其实并不属于Model,毕竟诸如发送一封邮件之类的功能属于应用逻辑的范畴,而不是领域逻辑,如果强行把应用逻辑加入Model里,会让Model的复用性大大降低。那么直接在Controller里编码实现如何呢?也不好,因为我们可能在多个控制器里都需要编写类似的代码,甚至一个控制器的多个动作也可能都需要编写类似的代码,这时硬编码就会造成重复,虽然我们可以通过提取公共类等方法来尽量避免重复代码,但是仍然感觉不爽,毕竟这样的实现太生硬,不够透明。

。。。。。。

别担心,observer驾着五彩的云朵来救我们了:

注:下面仅部分代码和observer相关,时间关系,请读者自行区分。

abstract class Model {
    protected
$observers = array();

    protected function beforeSave() {
        
// todo
    
}

    protected function
afterSave() {
        
// todo
    
}


    public function
save() {
        
$this->beforeSave();

        
$observersCount = count($this->observers);

        for (
$i = 0; $i < $observersCount; $i++) {
           
$this->observers[$i]->beforeSave($this);
        }

        // do save

        
for ($i = $observersCount
- 1; $i >= 0; $i--) {
            
$this->observers[$i]->afterSave($this);
        }

        
$this->afterSave();
    }

    public function
addObserver($observer) {
        
$this->observers = array_merge($this->observers, (array) $observer);
    }
}

class
Article extends Model {
    protected function
beforeSave() {
        
// do something
    
}

    protected function
afterSave() {
        
// do something
    
}
}

abstract class
Controller {
    protected
$observers = array();

    public function
__get($name) {
        $model = new $name;

        foreach (
$this->observers as $observer) {
            
$model->addObserver(new $observer);
        }

        return
$model;

    }
}

class
ArticlesController extends Controller {
    protected
$observers = array(
'ArticleObserver');

    public function
foobar() {
        
// do something

        
$this->Article->save();
    }
}

class
ArticleObserver extends Observer {
    public function
beforeSave($article) {
        
// do something about $article
    
}

    public function
afterSave($article) {
        
// do something about $article
    
}
}

==================================================

补充后记

经过一些思考,发现目前的Observer实现方式存在硬伤:仅适合CakePHP风格的MVC结构,但CakePHP本身又不是一个严谨的ActiveRecord实现,比如说CakePHP的find方法是对象调用,而传统意义上的ActiveRecord模式中,find方法是一个静态调用,这就造成了不可调和的矛盾,导致我们在控制器里无法使用__get对模型访问进行拦截。如果想在传统意义上的ActiveRecord模型中使用Observer,目前或许只能在控制器调用模型的时候,手动addObserver了。

类别:Php | 添加到搜藏 | 浏览() | 评论 (5)
 
最近读者:
 
网友评论:
1
2008年03月03日 星期一 下午 04:54
不错,呵呵!
老王是哪里人?我有个北京的朋友想与人合作做个股票方面的系统,如果老王是北京的那可否允许我推荐你一下?如果不是,能不能介绍一个在北京搞程序的朋友?
 
2
2008年03月03日 星期一 下午 05:01
To trooman: 谢谢啦!我的QQ:317650271
 
3
2008年03月07日 星期五 下午 01:40
典型的观察者模式。这种模式带来大量的前期潜入。
 
4
2008年05月03日 星期六 下午 09:25
这种做法已经被淘汰了,现在已经被aspectj淘汰。
 
5
2008年05月04日 星期日 上午 08:50
To 匿名网友:面向方面的确很酷,不过你能给出一个PHP的成熟解决方案么?!
 
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码:
 

     

©2008 Baidu