手机浏览 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: 单例, 模式

HeadFirst设计模式学习笔记(C#版):鸭子与策略(Strategy)模式

总是有一些奇怪的事情发生,我刚刚买了<HeadFirst 设计模式>,就有人写了如题的一篇文章。
我是用PHP的,而书上讲的例子是用伪代码(以java为蓝本)的,我也没有看多少呢,也没有想过要来实现一下,因为有些东西使用PHP的话,只会效率更低,解释型的,IO能节约还是要节约一下。
总想等看完再实现这些(第一大章还没有看完呢。)

不过,既然有人写了,总要宣扬一下的吧,代码虽然不一样,但思想总是一样的。
我也没有仔细看,就权当收藏吧。。

链接如下:

HeadFirst设计模式学习笔记(C#版):鸭子与策略(Strategy)模式

讲了什么呢?
摘要如下:


    策略模式的设计原则如下:
    1.   将应用中需要经常变化的代码独立出来,应和那些不需要经常变化的代码分开。
    2.   应针对接口,而不是类进行编程。
    3.  在类中应多用组合,少用继承。

    例子:

    我们要实现一个鸭子模拟器,这个鸭子模拟器由Duck类描述,而Duck类有如下4个行为:
    1.  display
    2.  swim
    3.  fly(飞)
    4.  quack(叫)

    其中swim是所有鸭子都具有的特性,而且所有鸭子的这些特性都相同,因此,这个方法可以直接在Duck类中实现。display方法也是所有鸭子具有的 特性,但随着鸭子的种类不同,display也有所不同,因此,display方法应为Duck类的抽象方法。fly和quack并不是所有鸭子的特性, 如橡皮鸭子即不会飞,也不会叫。因此,可以将这两个方法看作是两个行为,可将每一个行为设计成一个接口。这样可以和Duck类完全脱离。因为,fly和 quack与Duck一点关系都没有(别的东西也可能fly和quack),然后不同的fly和quack分别用实现相应接口的类表示。

Tags: headfirst, 模式, 策略模式, strategy

观察者模式(感谢mpeg提供源码)

  代码深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对程序中的接口或抽象类应用有更深的理解.设计模式在大中型系统中应用十分广泛,遵循了一定的编程模式,可以使代码便于理解,易于交流,而Observer(观察者)模式则是比较常用的一个模式,它尤其在界面设计、LOG处理中应用广泛。

代码请看全文

» 阅读全文

Tags: 模式

策略模式(感谢mpeg提供代码)

Strategy策略模式是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类.

Stratrgy应用比较广泛,比如, 公司经营业务变化图, 可能有两种实现方式,一个是线条曲线,一个是框图(bar),这是两种算法,可以使用Strategy实现.

实际整个Strategy的核心部分就是抽象类的使用,使用Strategy模式可以在用户需要变化时,修改量很少,而且快速.

Strategy和Factory有一定的类似,Strategy相对简单容易理解,并且可以在运行时刻自由切换。Factory重点是用来创建对象。

Strategy适合下列场合:

1.以不同的格式保存文件;

2.以不同的算法压缩文件;

3.以不同的算法截获图象;

4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等

代码请看详细内容......

» 阅读全文

Tags: 模式