手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表Tag:单例

老王:单例模式是邪恶的么

老王的看法,不过,事实上就是这样,现在很多开发人员还是很少在运行时候把对象作为参数传给某个方法或者class

原文:http://hi.baidu.com/thinkinginlamp/blog/item/ea586b63472627680c33fa38.html

正文:

写在前面的话:PHP每次运行重建环境,这样的运行方式本身就决定了PHP没有传统意义上的单例。

单例模式(singleton)也被叫做单件模式。使用它的目的是为了确保某个类只有一个实例。因为它的实现方式很简单,所以多数模式书籍都把单例模式作 为入门模式来介绍,可以说对很多模式爱好者来说,单例模式是他们最熟悉的设计模式。可惜这间接造成了单例模式使用的泛滥,以至于到了无所不在的地步,比如 说,ZendFramework里连前端控制器都是单例的。那么,单例模式是邪恶的么?

下面看一个实际编程时很多人愿意采用的单例模式例子:

注意:正常的单例模式应该设置一个私有化的__construct,但是出于下文的需要,我省略的它,至于限制__clone的行为,因为我嫌它占地方, 所以也没写,这两点你自己注意一下就好了,和文本讨论的问题关系不大。完整的例子可以参考:http://www.php.net/manual/en /language.oop5.patterns.php。

class Database
{
    private static $instance;

    public static function getInstance()
    {
        if (!isset(self::$instance)) {
            $class = __CLASS__;
            self::$instance = new $class;
        }

        return self::$instance;
    }
}


如此的单例模式通常是这样调用的:

class Article
{
    private $dasebase;

    public function __construct()
    {
        $this->database = Database::getInstance();
    }
}

$article = new Article();


下面我们就来讨论一下这样使用单例模式好还是不好。

文章在构造函数内通过单例模式的方式得到数据库实例,这看起来很正常,但是对于文章这样的领域概念来说,数据库实例属于纯粹的技术实现手段,明显属于外来 物,应该尽可能隔离才对,这样才符合单一原则,在测试驱动开发的领域,这被称作MOCK,但是在上例中,由于我们是在构造函数里通过单例模式静态方法调用 得到的数据库对象,这就造成了没有办法从外部替换掉这个数据库对象。

为了克服这样的问题,按照如下的方式获取数据实例更好一些:

class Article
{
    private $dasebase;

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

$database = Database::getInstance();
$article = new Article($database);


一旦我们把数据库实例化的过程放到了外面,便可以随意替换掉其具体的实现方式,这很棒,特别是在测试的时候很有用,因为数据库环境是一个很大的不安定因 素,替换掉它有助于让测试用例更高效的运行。你甚至可以把程序搞得再智能化一点,一切外部对象依赖都通过容器注入的方式挂接。

此时再看看上面代码里的$database = Database::getInstance($database);突然又感觉有点莫名其妙,因为实在看不到在这里使用单例模式有什么额外的好处,直接按照最平常的实例化方式并没有什么不妥:

$database = new Database();
$article = new Article($database);

外部数据库对象通过构造函数的方式注入到文章里,保存在$this->database属性中,至于文章级联的评论 ($article->comments)也需要数据库实例这样的情况,可以在实例化评论的时候把文章的$this->database属性 通过评论构造函数注入的方式传递过去,当然,还有其它的方法,总而言之,你不再需要一个单例模式的数据库实例。

再回到单例模式的本意上:为了确保某个类只有一个实例。比如说公司只有一个CEO(如果有一个公司有多个CEO,那么这个公司必然会犯很多逻辑错误)。但 是反思现在很多人对单例模式的使用,往往并没有按照单例模式的本意来使用,拿上面的数据库例子来说,如果文章有一个数据库实例,评论有另一个数据库实例。 这有什么逻辑问题么?答案是没有。既然不必确保数据库只有一个实例,那么在这里使用单例模式的动机本身就值得推敲。当然,有人会说文章和评论使用不同的数 据库实例本身就低效。但如果仅仅是出于效率的考虑来使用单例模式,那么本质上不符合单例模式的定义,更何况即便不使用单例模式,我们也可以像文中的例子一 样通过注入的方式来解决所谓的低效问题。

写在后面的话:单例模式本身并不邪恶,邪恶的是在错误的环境下使用单例模式。不要为了单例而单例。

Tags: 单例, 模式