百度空间 | 百度首页 
 
查看文章
 
半透明的装饰模式
2008年03月24日 星期一 下午 09:20
作者:老王

前些天我写了一篇《PHP实现透明化事务处理》,讨论了如何利用装饰模式实现事务处理。如果具体点说,它是一个透明的装饰模式,因为装饰类和被装饰类实现了共同的接口。可惜当梦想照进现实的时候,我们猛然发现纯粹透明的装饰模式并不多,因为既然我们的类需要被“装饰”,很多时候就不可避免的要引入一些新方法,这些方法在原有的接口里是没有定义过的,对于这种情况,我们称之为半透明的装饰模式。

啥也不说了,直接上干货:

######################################################################

abstract class Behavior {
    protected
$chain;

    public function
__construct($chain) {
        
$this->chain = $chain;
    }

    public function
getChain() {
        return
$this->chain;
    }

    public function
__call($name, $param) {
        return
call_user_func_array(array($this->getChain(), $name), $param);
    }
}

abstract class
Model {
    protected
$wrappers;

    public function
getWrappers() {
        return
$this->wrappers;
    }
}

######################################################################

class FooBehavior extends Behavior {
    public function
foo() {
        echo
"FooBehavior::foo\r\n<br />";
    }

    public function
foobar() {
        echo
"FooBehavior::foobar begin\r\n<br />";
        
$this->getChain()->foobar();
        echo
"FooBehavior::foobar end\r\n<br />";
    }
}

class
BarBehavior extends Behavior {
    public function
bar() {
        echo
"BarBehavior::bar\r\n<br />";
    }

    public function
foobar() {
        echo
"BarBehavior::foobar begin\r\n<br />";
        
$this->getChain()->foobar();
        echo
"BarBehavior::foobar end\r\n<br />";
    }
}

######################################################################

class SomeModel extends Model {
    protected
$wrappers = array('foo', 'bar');

    public function
some() {
        echo
"SomeModel::some\r\n<br />";
    }

    public function
foobar() {
        echo
"SomeModel::foobar\r\n<br />";
    }
}

######################################################################

$model = new SomeModel();

$wrappers = $model->getWrappers();

for (
$i = count($wrappers) - 1; $i >= 0; $i--) {
    
$class = ucfirst($wrappers[$i] . 'Behavior');

    
$model = new $class($model);
}

$model->foo();
$model->bar();
$model->foobar();
$model->some();

######################################################################


注释:
FooBehavior,BarBehavior是装饰类
SomeModel是被装饰类
foo, bar是装饰类新添加的方法
foobar是装饰类和被装饰类共同实现的方法
some是被装饰类原有的方法


运行上面的代码,你会得到如下结果:

FooBehavior::foo

BarBehavior::bar

FooBehavior::foobar begin
BarBehavior::foobar begin
SomeModel::foobar
BarBehavior::foobar end
FooBehavior::foobar end


SomeModel::some

你能看到我们既可以通过装饰添加新方法,而且还可以修饰已有的方法,实现类似before/after这样的回调函数的效果,甚至你可以通过装饰类置换掉被装饰类的某个方法,至于置换的方法,在上面代码里并没有演示,但其实方法很简单,只要在装饰类里不再传递chain即可。

装饰模式的作用那是相当强啊!

类别:Php | | 添加到搜藏 | 分享到i贴吧 | 浏览() | 评论 (5)
 
网友评论:
1
2008年03月25日 星期二 上午 09:47 | 回复
有两点建议: 1. 在抽象类Model中就应该定义foobar()抽象方法,这是满足包装模式的一条原则吧? 2. 既然用面向对象的方法,为什么不让他们更对象些,那个for循环,看起来真的不舒服,如果再增加一个类来处理,就不需要在Model中定义wrappers属性了,wrappers属性本身并不稳定,用外部控制更好,类似这样: class Wrapper { public static function wrap($model, array $wrappers) { for ($i = count($wrappers) - 1; $i >= 0; $i--) { $class = ucfirst($wrappers[$i] . 'Behavior'); $model = new $class($model); } return $model; } } $model = new SomeModel(); $model = Wrapper::wrap($model, array('foo', 'bar')); echo $model->foobar();
 
4
2008年03月25日 星期二 上午 10:46 | 回复
To trooman: 1. 我这段代码确实不是严谨的装饰模式。代码仅出于演示目的。 2. 实际应用中,你说的那段代码肯定会存在与一个类中,可能是工厂,亦可能是别的某个类。 对于你给出的代码: $model = new SomeModel(); $model = Wrapper::wrap($model, array('foo', 'bar')); echo $model->foobar(); 我觉得有Bad smell。因为使用装饰模式,一般都要求尽可能透明,这样才能减少对客户端代码的侵入。
 
5
2008年03月25日 星期二 上午 11:21 | 回复
何谓Bad smell?如果这里是“不透明”引起的话,我觉得在Model中定义wrappers属性本身就不很透明,wrappers属性是不稳定的,首先,其他编程员不一定要使用wrappers属性,所以wrappers属性本就不应该放在Model中,其次,如果哪天你不想使用FooBehavior和BarBehavior,你要换成别的,你就必须修改Model,实际上这才是真正的Bad smell! 对于使用者(调用者)来说,不仅要知道运行的代码是怎么工作的,更要知道内部做了什么,这才算真正透明吧?
 
6
2008年03月25日 星期二 上午 11:38 | 回复
To trooman: 所谓Bad smell,是指在你的代码中,调用者要指定类应该有什么样的行为,这本身太不透明,过于生硬,因为很多时候,从业务逻辑应该所处的位置讲,调用者是没有资格决定类的行为的,比如CakePHP中的Model可以指定Tree行为,调用者可以像使用一个一般的类那样去使用被装饰的类,但是装饰类被在内部进行Tree的转换,这一切对调用者来讲是透明的,他并不知道我们把原来的对象进行了装饰。文中所谓的半透明的装饰模式中的半透明是沿用了强类型语言的习惯叫法,因为在强类型语言如Java中,我们在声明的时候要指定类型,一旦加入新方法,类型声明就会导致半透明,但是弱类型的PHP不存在这样的问题。至于你说的在Model中定义wrappers本身不够透明,可能吧。我的本意是希望使用一个static类型的wrappers属性,不过5.3之前的PHP还不能延迟绑定,所以换成了文中的写法。在我看来,这个属性就是被装饰对象的一个配置选项,等5.3来了,就可以声明为static变量,那样,动态增加或者删除wrapper没有任何问题。
 
7
2008年03月27日 星期四 下午 04:58 | 回复
http://scruffylookingcatherder.com/archive/2007/08/07/dependency-injection.aspx
 
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码: 请点击后输入四位验证码,字母不区分大小写
      

     

©2010 Baidu