设计模式之装饰模式(修饰模式)

装饰模式也叫修饰模式,维基上对修饰模式的描述是:

修饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。

本文将以一个具体的例子说明为什么装饰模式比生成子类的方式更为灵活。

假设有一款城堡游戏,每个城堡有它的价值,我们有普通城堡,拥有钻石的城堡,被污染的城堡,假设普通的城堡价值为 10,拥有钻石的城堡比普通的城堡价值多 5,被污染的城堡比普通的城堡价值少 6,因此我们建了如下的类:

<?php
/**
 * 公用接口,所有物品都是有价值的
 */
abstract class Stuff
{
    abstract function getWealth();
}

class Castle extends Stuff
{
    public function getWealth()
    {
        return 10;
    }
}

class DiamondCastle extends Castle
{
    public function getWealth()
    {
        return parent::getWealth() + 5;
    }
}

class PollutedCastle extends Castle
{
    public function getWealth()
    {
        return parent::getWealth() - 6;
    }
}

看起来还不错,我们可以很方便获得钻石城堡对象也可以获得被污染的城堡对象,但是如果我们想获得即拥有钻石又被污染了的城堡对象该怎么做呢。当然可以新建一个类 DiamondPollutedCastle,但实现起来并不优雅,尤其是当后期又增加一些带有不同属性的城堡时,这种组合会更加的多,显然为每一种组合都创建一个类会使得整个系统类过多。这个时候使用装饰模式就比较合适了。

装饰模式使用组合和委托而不是只使用集成来解决功能变化的问题。Decorator 类会持有另外一个类的实例。Decorator 对象会实现与被调用对象的方法的队友的泪方法。用这种方法可以在运行是创建一系列的 Decorator 对象(《PHP 面向对象模式与实践》p167)。

下面是使用装饰模式重写的城堡游戏类:

<?php
/**
 * 公用接口,所有物品都是有价值的
 */
abstract class Stuff
{
    abstract function getWealth();
}

class Castle extends Stuff
{
    public function getWealth()
    {
        return 10;
    }
}

abstract class Decorator extends Stuff
{
    /**
     * @var Stuff
     */
    protected $stuff;
    public function __construct(Stuff $stuff)
    {
        $this->stuff = $stuff;
    }
}

class DiamondDecorator extends Decorator
{
    public function getWealth()
    {
        return $this->stuff->getWealth() + 5;
    }
}

class PollutedDecorator extends Decorator
{
    public function getWealth()
    {
        return $this->stuff->getWealth() - 6;
    }
}

我们这样来实例化城堡对象:

<?php
// 普通城堡
$generalCastle = new Castle();
// 钻石城堡
$diamondCastle = new DiamondDecorator(new Castle());
// 被污染的城堡
$pollutedCastle = new PollutedDecorator(new Castle());
// 被污染的钻石城堡
$diamondPollutedCastle = new PollutedDecorator(new DiamondDecorator(new Castle()));

通过像这样使用组合和委托,可以在运行时轻松地合并对象,对于初始化类调用的代码来说,并不需要内部是如何合并的,因为每个 Decorator 类都有 getWealth() 方法,所以无论是一个装饰器对象还是真正的 Castle 对象,调用端都可以使用 getWealth() 方法来获取一个装饰器城堡或者普通城堡的价值。

 

PHP 实现单例模式的两种方式

第一种方式是由类维护一个静态属性,该属性是对象实例的引用,示例代码如下:

class Singleton {
        private static $_instance = null;
        public static function getInstance() {
                is_null(self::$_instance) && self::$_instance = new self();
                return self::$_instance;
        }
        private function __construct() { // 构造函数 private,防止类在外部被 new 出来
                ;
        }
}

 

第二种方式是由静态方法里面的一个静态变量返回对象实例的引用,示例代码如下:

class Singleton {
        public static function getInstance() {
                static $_instance = null;
                is_null($_instance) && $_instance = new self();
                return $_instance;
        }
        private function __construct() { // 构造函数 private,防止类在外部被 new 出来
                ;
        }
}

 

这两种实现有什么区别呢?效果是一样的吧~

今天才知道原来 PHP 5.3.0 之前版本是不支持延迟绑定的,赶紧把前两天写的类改过来,因为公司线上环境还是用的 5.2 系列版本。关于 PHP 延迟绑定(官网称呼其为:后期静态绑定)请看官网说明:http://www.php.net/manual/zh/language.oop5.late-static-bindings.php