论坛首页 Java企业应用论坛

Singleton和Static的缺点(重发)

浏览 18730 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-04-17  
femto 写道
Singleton和Static的最重要缺点就是失去了面向对象的多态特性。

在项目中,一个用户在操作一条纪录时需要上锁,防止别人同时操作。
然后我们写了一个LockManager,在数据库里一个表写上某条纪录被某个人锁。
由于这个东西相当于Service一样,也不需要多件,所以我们把它写成了
Singleton 类,调用LockManager.lock() 和LockManger.unlock() 方法。
后来经过考虑,如果Server不是Cluster,只是一个JVM的话
那么只需要在Server维护一个内存锁,而不需要访问数据库。
这个实际上是一个不同的锁策略,两种方法是锁策略的不同实现,
然而由于我们已经把Lock作成Singleton,调用代码已经与LockManger 这个class耦合,无法透明替换不同实现。
所以这就是Spring的一个好处,从ApplicationContext取回来的Bean既可以是Singleton,也可以是Prototype,而该Bean是正常的类,具有正常的类的特性。
(虽然重构一下,LockManager delegate到不同的LockStragety实现就可以了,
不过从这个例子确实体会到Singelton不是什么好东东:),一用就耦合与具体的类了,
还是从Spring取比较好,在Runtime再决定到底是不是Singleton。
   发表时间:2004-04-17  
对于 femto 最初发的这个帖子:
http://forum.iteye.com/viewtopic.php?t=4379
唯一言之有物的只有 femto 最初说的一段话。但是 femto 的这段话并不足以证明在任何时候都不应该使用 Singleton,只证明了在某些场合使用 Singleton 会产生问题。所以这个话题正常讨论下去应该是 Singleton 的适用场合和不适用场合,以及在它不适用的时候如何用相似的模式(例如 Prototype)来替代。我们知道 GOF 也是很重视描述这些前置条件的,可能他们早就想到了呢?(时间有限,没有翻 GOF《设计模式》,只能泛泛地说说,见谅)
0 请登录后投票
   发表时间:2004-04-17  
dlee 写道
对于 femto 最初发的这个帖子:
http://forum.iteye.com/viewtopic.php?t=4379
唯一言之有物的只有 femto 最初说的一段话。但是 femto 的这段话并不足以证明在任何时候都不应该使用 Singleton,只证明了在某些场合使用 Singleton 会产生问题。所以这个话题正常讨论下去应该是 Singleton 的适用场合和不适用场合,以及在它不适用的时候如何用相似的模式(例如 Prototype)来替代。我们知道 GOF 也是很重视描述这些前置条件的,可能他们早就想到了呢?(时间有限,没有翻 GOF《设计模式》,只能泛泛地说说,见谅)

这就是我想说的,为什么"具体问题,具体分析"是一句废话,因为那不是科学的态度.
我们应该说清楚,那些场合适用Singleton那些场合不适合,不适合的场合应该怎么去做.如果总是说"具体问题,具体分析",意义又在哪里呢?
就如,我们老祖宗几千年前就知道八卦,可惜计算机并不是我们发明的.
0 请登录后投票
   发表时间:2004-04-17  
楼上的那段文字,真正的问题在于.我们应该割裂的看待问题,而不是用发展的眼光看待问题.换一句话说,cluster中的锁,和Same VM的内存锁,以及锁策略是2个不同的问题.而不是一个问题.
我们可以对这2个问题明确的做出表达
1.一个对象如果采用了Singleton,那么他是全局唯一的,在运行时是不可变得.
  2.如果某一系列的对象,具有相同的interface和behavior,那么我们就需要采用
Policy.

的确,我们面对了同样一个Application,但是我们却面对了不同的问题.就如我们不能说newton thereory 具有局限性,而应该说低速运动,和高速运动是两种截然不同的问题.
另外一个建议是,不要去试图考虑扩展,因为扩展就是Gamble,除非你确切知道应该扩展什么.
0 请登录后投票
   发表时间:2004-04-17  
Trustno1,我实在觉得你很搞笑了。如果我说天下乌鸦没有白的,难道我要把天下的乌鸦全部都找出来,一个个验证么?证明一个东西只有穷举么???
   你强调只能使用类继承,好,我就现在举个例子。
   GOF的Template模式,楼上不知知道否,我现在大概说明一下。
   有一个抽象类A,它定义了业务逻辑,有一个抽象方法build()
     public abstract class A {
         public void defineFacade() {
             ......
             build() ;
             .....
         }
        
         protected abstract void build() ;
     }
     它的作用是封装业务逻辑,而让子类去多态实现build方法(比如说制造汽车,每个子类只需要负责怎么做零件,而不需要考虑怎么组装)。
    好,这个时候,我们看看用对象的组合(也就是接口实现)怎么做。
    首先我们要有一个接口定义build()方法,但是这个就带来了很多困难,我给大家指出来:
   1. 首先接口的方法不能定义成protected,那么它就只有两种可能,一种是默认的无修饰符,这个接口就只能被本包内的类实现,呵呵,如果做成封装的jar,大家就都用不了它了。另外一种定义成public,好,那么实现它的类的方法也就必须是public,这样,就放大了它的范围(因为我并不想别的不在同一包或非子类直接使用它),那么怎么办?再封装一把?
   2.简单点,用类之间的组合实现Template模式,其实也就是使用了Proxy模式,增加一层代理来封装接口的实现。这样访问接口的是代理而不是具体实现。
   大家可以看到了,一个简单的例子,用对象组合会增加数倍的类,结构也相当复杂。这难道是OO的初衷??
    上述例子反应了OO思想中的两个方面,过程抽象和封装,另外还有数据抽象没有说,我倒是想问楼上,接口实现怎么做到数据抽象???楼上极端地把OO片面理解,把稍微不符合OO的就视为犯天下之大不韪,实在可笑!OO只是看待问题的一种方法,OP,AO都是方法之一!按照楼主的思维基本数据类型都不需要要了。
     我想告诉楼主的一个最基本的软件设计思想,是让问题简单化,而不是完全抽象化!我们大家都知道goto可以被三种基本程序逻辑(选择、循环、顺序)代替,递归也可以被代替。但是并没有完全替代,为什么??其实很简单,因为在有的时候,用goto(在数值计算),用递归可以降低问题的复杂度,是程序更加容易理解,也更加容易维护。楼主,我不是欺负你,你有本事就不用递归写一个汉诺塔的算法!即使你写得出,99%的程序员也看不懂!
    有上述理解是我今天爬山的时候,发现有两条路,一条大路,一条不好走的路,而且公园建议走大路。但是我们要去一个地方,发现走大路是走小路举例的几倍,并且去这个地方走小路不会给自己带来任何损害。那为什么不走小路呢?
    楼主还有一个非常荒谬的问题,就是拿经典对付经典。从每本书里找符合自己意思的话语证明自己去打败另外一本书,你可以那effective c++,我为什么不能拿think in java,在think in java中提到的是建议(advice),而不是替代(replace)。再说了,你不是对C++那么多批驳么?
    楼主把Java批驳成商业化,我倒是奇怪了,哪个计算机语言不是商业化?软件诞生的目的是什么???还不是为了商业服务?再说了,谁都知道,在计算机语言中,java是最不商业化,也是最开发,最开源的,这也是它为什么受到全世界大部分程序员喜欢的原因之一。我想问楼主,你倒是帮我们找一个不商业化的语言??
    楼主另外动不动就要求别人穷举,我举个搞笑的例子:我们说过马路要小心,小心被车撞,楼主就要问,告诉我,哪条马路会被车撞?哪条不会,说清楚!
    昨天还以为楼主是高深的学者,呵呵,遗憾,今天我不这么看了。楼主,你该坐下来,好好冷静思考了~~~~~~~~~
0 请登录后投票
   发表时间:2004-04-17  
错了错了,dlee,我不是指你,是楼上!
   (欢迎板砖和炮火~~~~~~~~~)
0 请登录后投票
   发表时间:2004-04-17  
to 凤舞凰扬:
现在我又要批评你了,本来我重发这个帖子,就是因为各种设计模式都有其适用场合和不适用场合,大师对这些前置条件是非常重视的。某些人学会了一种设计模式就去机械地套用,不重视前置条件,也不注重研究所要解决的业务问题。自以为很牛,其实完全是过度设计和卖弄。因此这个讨论是很有意义的,我们讨论清楚了 Singleton 的适用和不适用场合,以后就可以以这种方式讨论清楚其它设计模式的适用和不适用场合。我们的讨论应该是针对问题本身,而不要加上人身攻击的内容。所以你的帖子不符合论坛的规定,本来应该删除的。难道你希望新开的这个线索又重新被锁掉吗?

建议 Trustno1 不要理会他的人身攻击,避免升级为恶性的意气之争。

希望大家尽量开展干净的讨论,不要夹杂这些人身攻击的内容。
0 请登录后投票
   发表时间:2004-04-17  
dlee兄,实在惭愧,我无意进行个人攻击,也对楼上Trustno1没有任何个人的意见。
   我是赞成你的观点的,对于Trustno1的整体上的观点我也是赞成的,只不过我和你一样,我希望不要绝对地去看待任何一样东西。如果说具体问题具体分析不明确,我们可以就某一种具体的问题进行讨论。
   我也是强调任何一种模式,一种思想都有其适应的范围,没有任何真正通用的东西,也没有任何放之四海成立的标准。
   在这里,我向Trustno1表示道歉,我无意将技术的讨论变成一种漫骂或者个人攻击,无意义的。
   这样的讨论也希望就此打止,也希望能够给各位网友提供一些参考和思考,它的确给我们带来了很多东西。
0 请登录后投票
   发表时间:2004-04-17  
引用

     它的作用是封装业务逻辑,而让子类去多态实现build方法(比如说制造汽车,每个子类只需要负责怎么做零件,而不需要考虑怎么组装)。
    好,这个时候,我们看看用对象的组合(也就是接口实现)怎么做。
    首先我们要有一个接口定义build()方法,但是这个就带来了很多困难,我给大家指出来:
   1. 首先接口的方法不能定义成protected,那么它就只有两种可能,一种是默认的无修饰符,这个接口就只能被本包内的类实现,呵呵,如果做成封装的jar,大家就都用不了它了。另外一种定义成public,好,那么实现它的类的方法也就必须是public,这样,就放大了它的范围(因为我并不想别的不在同一包或非子类直接使用它),那么怎么办?再封装一把?
   2.简单点,用类之间的组合实现Template模式,其实也就是使用了Proxy模式,增加一层代理来封装接口的实现。这样访问接口的是代理而不是具体实现。

我没有明白你说的第一种方式是怎么样的
关于第二个问题,如果说类多.那么自然有减少类的方法

public class A<T> {
public void defineFacade() {
......
T t=new T()
t.build() ;
.....
}

还本归原template 模式应该是这样的.类多是因为面向对象并不强调强类型。虽然c++, java号称强类型,但并不是铁板一块,很多时候类型系统的困难不得不导致难看,不安全的downcast. 这不是面向对象的过错,但确实不是面向对象所要解决的问题。
引用

    我想告诉楼主的一个最基本的软件设计思想,是让问题简单化,而不是完全抽象化!我们大家都知道goto可以被三种基本程序逻辑(选择、循环、顺序)代替,递归也可以被代替。但是并没有完全替代,为什么??其实很简单,因为在有的时候,用goto(在数值计算),用递归可以降低问题的复杂度,是程序更加容易理解,也更加容易维护。楼主,我不是欺负你,你有本事就不用递归写一个汉诺塔的算法!即使你写得出,99%的程序员也看不懂!

不是递归可以被替代,而是恰恰相反,迭代可以被递归完全取代.
   递归是人类思维最自然的方式.取消迭代的语言完全可以生存.只不过你习惯了迭代而已.递归语言多的是,shcema,lisp都是如此.至于递归的效率问题,只要解决掉尾递归就可以解决.现在甚至有用递归语言书写的操作系统.在vc那个版子里面,我就发过两个关于递归的贴子.

引用

    楼主另外动不动就要求别人穷举,我举个搞笑的例子:我们说过马路要小心,小心被车撞,楼主就要问,告诉我,哪条马路会被车撞?哪条不会,说清楚!

穷举?我想你可能没有理解我在说什么.我想的是,一个命题,一个观点必须要具有
约束的条件.一个观点,一个命题必须是要可证伪的.
"你出门可能被车撞也可能不被车撞"就不可证伪,而"你出门一定会被车撞"
是可证伪的.只有可证伪的命题才是属于科学的研究方法.
为什么这样说,因为可证伪的命题的价值并不在于这个问题的本身的对错和价值,而是可证伪的命题具有向前发展的动力.而"具体问题,具体分析",这种问题没有约束
不可能证伪那么便是没有任何信息量,不具备指导意义的空话.
也许你不同意这样的观点,没有关系我们的价值观不同.你可以说你的,我可以说我的.你的处理方式我照样会反驳,你的反驳只要与我的价值观不同我也不会接受.
0 请登录后投票
   发表时间:2004-04-17  
说来说去离题越来越远了。今天在上班,晚些时候把 GOF 原创的《设计模式》中关于 Singleton 的段落找来大家讨论一下。
大家讨论问题都需要这样汪洋恣肆吗?是不是惟有这样才能显示出自己掌握了“人间正道”呢?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics