单例模式可以说是GOF设计模式中最为简单的模式,也是背负骂名最多的模式。不过一直以来,我对许多类似关于它是最烂和反模式的评价却不尽以为然。
模式本身很简单,除了牵涉多线程安全问题引起的一点罗唆外。所以,我不太想讨论GOF提供的关于该模式的参考实现。我所关注的是它背后带来的对问题的看法和思考方式。
我们先看看单例模式出现的背景很存在的意义。单例模式通俗来讲就是确保类只有一个实例。那么好,我们问一下,为什么要确保类只有一个实例呢?其实无非是两个方面的作用:一、想控制资源的使用,它又体现在两个方面,其中之一是控制实例数目的产生来节约资源,其二通过线程同步控制资源的并发访问;二、想作为一种通信媒介,在不建立直接关联的条件下让不相关的两个程序进行通信,尤其是多线程。第二点,我说的有些抽象,简单讲吧,就类似于大家基于一个黑板讨论东西,而不关心参与讨论的对象。单例就取了黑板的作用。有人会说,为什么不将同一实例传递给每个使用者就是啊!这样的话,会带来一个问题,就是总是需要一个媒介将该对象传递给所有的使用者。
简单描述了一下单例模式出现的背景,我们再来看看,网络中对它嗤之以鼻的一些重要的观点:
1. 使用单例模式有一个很重要的必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。
2.singleton是邪恶的,是反模式(http://www.jdon.com/article/17578.html),为什么说邪恶,因为它有陷井,或者很虚伪,容易诱骗初学者上当。
3.如果在J2EE的范围内做一次正式的投票,我敢打赌Factory Method、Prototype、Singleton和Bridge绝对是在被淘汰之列,因为它们做的事情已经完全被支持Dependency Injection的容器包办了,程序员再也不需要知道这些模式。所以说呢,“Singleton是邪恶”的或者是一个非伪命题,不过更可能是一个伪问题,因为……谁还需要Singleton呢?你只需要PicoContainer或者Spring Core。
4.在分布式环境下,比如集群、负载均衡或者多路应用的情况下,单实例是不存在也无法做到的。因为静态方法只能控制在一个环境下的有效。
说实话,我倒不想去翻案,或者去挑战啥的,不过总是觉得许多言论都存在一些误区或者说隐含的前提。我们依次看一下这些观点吧。第一个观点强调了一个所谓的必要条件,呵呵,这就是它的问题所在了,谁说单例模式有这样的必要条件呢?一个类只有一个实例的适合才应当使用单例模式,呵呵,这话听起来根本就没法驳,有多个实例又怎么去使用单例模式呢?其实啊,这就是最简单的本末倒置了。如果我都知道了我只能有一个实例,我自然不能考虑多例模式。那么考虑使用这个模式的基点究竟在哪个方面呢?其次,什么是有必要,什么是没必要?如果有更好的替代,比如说通过第三方的控制,比如池或者容器来控制实例更合理,那么我们是不是具备什么情况下都可以使用他们呢?必要与否的判定并不是从模式本身出发的,而是从模式应用的场景出发的。
继续,我们来看第二个问题,singleton is evil,是种反模式,其实来说,是有蛮多道理的,但是问题的本身也似乎不是从模式本身出发,而是从那些不熟悉模式的人盲目使用模式的角度来评说的。看完整篇的文章,我都不清楚这个所谓的陷阱在哪里?singleton不会有线程安全问题的么?GOF的文中从来没有讨论个这个问题的。其实道理很简单啊,线程同步问题归根到底是编程的问题,跟单例又有何关系,多实例就不会有了?问题的关键只是多实例的同步问题比较难以发现,单实例的比较容易发现而已。只是许多应用者根本就没有考虑过所谓的同步问题,所以被发现时怨气都撒在singleton身上了。它,的确有些冤。
再继续,J2EE做投票,singleton模式将被容器包办,所以该被淘汰。这里其实有两个语境,其一,强调了J2EE和给予所谓Dependency Injection的容器,但是试问一下,singleton模式强调过这样的应用场景么?模式本身只是面向对象的设计思路,跟所谓的语言环境又哪来那么大关联啊?其二,容器真能处理所有的事情么?即使是再强的spring,它的容器也一般用来维护business 的bean实例吧?难道所有应用程序实例的产生都是靠spring的BeanContext或者BeanFactory不成?难道要spring整个侵入业务系统,解决了所谓的问题,才是正道呢?
最后,第四个问题,有一定的道理,从GOF参考实现的角度,如果我们使用这样的方式去实现我们的应用系统,那么它们是不能在多通路、集群或者负载均衡的情况下应用。这样一来也就是限制了应用系统。但是,提出这样一个问题的人是否又思考过,你对这个模式的探讨是否局限在了GOF提供的实现上呢?模式是解决一类问题的解决方法和实践总结,的确它原始的出处受限于当时的环境,但是这种思路是否依然可以得到扩展和移植呢?一个immutable的实例是否可以做成这样的单实例呢,或者一个由独立系统提供的全局访问控制的mutable实例是否科研做成呢?当然,它在不同的环境会存在多个影子,但是对于每个应用的环境来说,它不是一样作为单个实例出现的么?我们来好好看看单例模式的意图吧: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。我们所做的只是将对意图的理解放在了独立的系统环境中,而不是整个应用系统中。
有些意思了,好,我们再看看另外一篇帖子,指出为什么singleton is evil。http://blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx,整的来说,四个观点,我们大致了解一下:
1.singleton是全局的,它和全局变量又有什么区别呢?全局是不好的,应该通过接口去访问而不是了解类本身。(哈,有些误区,其实是许多人对设计模式的误区:设计模式究竟是运用在何种层次?每个设计模式都是单独出现解决一切问题的么?使用singleton可以选择延迟装载,也完全可以被隔离,对于使用者来说不可知。)
2.单例模式限制对象的创建,这是不应该的,即使限制也应该通过工厂等其他模式。(呵呵,又是有趣的一点,使用了工厂后的单例模式就不是单例模式?它无法是单例模式和工厂模式的结合罢了。怎么感觉老是拿使用单例模式就都必须直接访问该类一样呢?)
3.单例将提升类之间的耦合关系。唉,又是这样的论点,隔离一下不就好了么?
4.只要程序存在多久,单例的状态就维持多久。持久的状态是单元测试的敌人。说实话,没怎么搞明白,第一,单例的状态又不是不能改变,怎么就不能测试。第二难道单元测试还去测试实例的产生和消亡?即使是一个永不消亡的对象又怎么就是单元测试的敌人了。冤!
看到这里,相信好多人准备拍砖了,又是一个死板教条主义的维护者。赶紧澄清啊!不是,真的不是。我从来没有去为单例模式辩护,更不是打死不认帐的角色。我只是在旁,冷静的看看这些,我从来不会因为它是GOF的23种设计模式而乱用,更不会因为某些人的言论而去排斥它。我所知道的,模式只是解决某类特定问题的一种解决方案而已。
我所想,所说,所做的只是希望会有一些朋友冷静的,思考的去看待它......
模式本身很简单,除了牵涉多线程安全问题引起的一点罗唆外。所以,我不太想讨论GOF提供的关于该模式的参考实现。我所关注的是它背后带来的对问题的看法和思考方式。
我们先看看单例模式出现的背景很存在的意义。单例模式通俗来讲就是确保类只有一个实例。那么好,我们问一下,为什么要确保类只有一个实例呢?其实无非是两个方面的作用:一、想控制资源的使用,它又体现在两个方面,其中之一是控制实例数目的产生来节约资源,其二通过线程同步控制资源的并发访问;二、想作为一种通信媒介,在不建立直接关联的条件下让不相关的两个程序进行通信,尤其是多线程。第二点,我说的有些抽象,简单讲吧,就类似于大家基于一个黑板讨论东西,而不关心参与讨论的对象。单例就取了黑板的作用。有人会说,为什么不将同一实例传递给每个使用者就是啊!这样的话,会带来一个问题,就是总是需要一个媒介将该对象传递给所有的使用者。
简单描述了一下单例模式出现的背景,我们再来看看,网络中对它嗤之以鼻的一些重要的观点:
1. 使用单例模式有一个很重要的必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来说,如果一个类可以有几个实例共存,那么就没有必要使用单例类。
2.singleton是邪恶的,是反模式(http://www.jdon.com/article/17578.html),为什么说邪恶,因为它有陷井,或者很虚伪,容易诱骗初学者上当。
3.如果在J2EE的范围内做一次正式的投票,我敢打赌Factory Method、Prototype、Singleton和Bridge绝对是在被淘汰之列,因为它们做的事情已经完全被支持Dependency Injection的容器包办了,程序员再也不需要知道这些模式。所以说呢,“Singleton是邪恶”的或者是一个非伪命题,不过更可能是一个伪问题,因为……谁还需要Singleton呢?你只需要PicoContainer或者Spring Core。
4.在分布式环境下,比如集群、负载均衡或者多路应用的情况下,单实例是不存在也无法做到的。因为静态方法只能控制在一个环境下的有效。
说实话,我倒不想去翻案,或者去挑战啥的,不过总是觉得许多言论都存在一些误区或者说隐含的前提。我们依次看一下这些观点吧。第一个观点强调了一个所谓的必要条件,呵呵,这就是它的问题所在了,谁说单例模式有这样的必要条件呢?一个类只有一个实例的适合才应当使用单例模式,呵呵,这话听起来根本就没法驳,有多个实例又怎么去使用单例模式呢?其实啊,这就是最简单的本末倒置了。如果我都知道了我只能有一个实例,我自然不能考虑多例模式。那么考虑使用这个模式的基点究竟在哪个方面呢?其次,什么是有必要,什么是没必要?如果有更好的替代,比如说通过第三方的控制,比如池或者容器来控制实例更合理,那么我们是不是具备什么情况下都可以使用他们呢?必要与否的判定并不是从模式本身出发的,而是从模式应用的场景出发的。
继续,我们来看第二个问题,singleton is evil,是种反模式,其实来说,是有蛮多道理的,但是问题的本身也似乎不是从模式本身出发,而是从那些不熟悉模式的人盲目使用模式的角度来评说的。看完整篇的文章,我都不清楚这个所谓的陷阱在哪里?singleton不会有线程安全问题的么?GOF的文中从来没有讨论个这个问题的。其实道理很简单啊,线程同步问题归根到底是编程的问题,跟单例又有何关系,多实例就不会有了?问题的关键只是多实例的同步问题比较难以发现,单实例的比较容易发现而已。只是许多应用者根本就没有考虑过所谓的同步问题,所以被发现时怨气都撒在singleton身上了。它,的确有些冤。
再继续,J2EE做投票,singleton模式将被容器包办,所以该被淘汰。这里其实有两个语境,其一,强调了J2EE和给予所谓Dependency Injection的容器,但是试问一下,singleton模式强调过这样的应用场景么?模式本身只是面向对象的设计思路,跟所谓的语言环境又哪来那么大关联啊?其二,容器真能处理所有的事情么?即使是再强的spring,它的容器也一般用来维护business 的bean实例吧?难道所有应用程序实例的产生都是靠spring的BeanContext或者BeanFactory不成?难道要spring整个侵入业务系统,解决了所谓的问题,才是正道呢?
最后,第四个问题,有一定的道理,从GOF参考实现的角度,如果我们使用这样的方式去实现我们的应用系统,那么它们是不能在多通路、集群或者负载均衡的情况下应用。这样一来也就是限制了应用系统。但是,提出这样一个问题的人是否又思考过,你对这个模式的探讨是否局限在了GOF提供的实现上呢?模式是解决一类问题的解决方法和实践总结,的确它原始的出处受限于当时的环境,但是这种思路是否依然可以得到扩展和移植呢?一个immutable的实例是否可以做成这样的单实例呢,或者一个由独立系统提供的全局访问控制的mutable实例是否科研做成呢?当然,它在不同的环境会存在多个影子,但是对于每个应用的环境来说,它不是一样作为单个实例出现的么?我们来好好看看单例模式的意图吧: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。我们所做的只是将对意图的理解放在了独立的系统环境中,而不是整个应用系统中。
有些意思了,好,我们再看看另外一篇帖子,指出为什么singleton is evil。http://blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx,整的来说,四个观点,我们大致了解一下:
1.singleton是全局的,它和全局变量又有什么区别呢?全局是不好的,应该通过接口去访问而不是了解类本身。(哈,有些误区,其实是许多人对设计模式的误区:设计模式究竟是运用在何种层次?每个设计模式都是单独出现解决一切问题的么?使用singleton可以选择延迟装载,也完全可以被隔离,对于使用者来说不可知。)
2.单例模式限制对象的创建,这是不应该的,即使限制也应该通过工厂等其他模式。(呵呵,又是有趣的一点,使用了工厂后的单例模式就不是单例模式?它无法是单例模式和工厂模式的结合罢了。怎么感觉老是拿使用单例模式就都必须直接访问该类一样呢?)
3.单例将提升类之间的耦合关系。唉,又是这样的论点,隔离一下不就好了么?
4.只要程序存在多久,单例的状态就维持多久。持久的状态是单元测试的敌人。说实话,没怎么搞明白,第一,单例的状态又不是不能改变,怎么就不能测试。第二难道单元测试还去测试实例的产生和消亡?即使是一个永不消亡的对象又怎么就是单元测试的敌人了。冤!
看到这里,相信好多人准备拍砖了,又是一个死板教条主义的维护者。赶紧澄清啊!不是,真的不是。我从来没有去为单例模式辩护,更不是打死不认帐的角色。我只是在旁,冷静的看看这些,我从来不会因为它是GOF的23种设计模式而乱用,更不会因为某些人的言论而去排斥它。我所知道的,模式只是解决某类特定问题的一种解决方案而已。
我所想,所说,所做的只是希望会有一些朋友冷静的,思考的去看待它......