论坛首页 Java企业应用论坛

基础知识: 需求!

浏览 110167 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-08-20  
logo 写道
不是静态工厂有什么问题,也不是静态工厂就不能用,也不是说公共构造函数就非用不可,关键的分歧在于对象本身是否应该控制对象创建的权利和行为:

你的主张是对象内部要控制对象创建的权利和行为,因为对象不应该信任外部调用,只有控制了才安全。所以你把构造函数设为private,不给外部组装代码以new的可能性,只允许他通过你定制的静态工厂来获得一个对象本身,换句话说你只允许外部对象以你设定的方式来使用对象,也就是说你给对象的创建提出了一个潜在的前提假设,只有满足了这个前提假设,才允许创建对象

你不信任外部组装代码,你限定了外部创建对象的行为,这样的好处就是不会有潜在的恶意代码调用带来了副作用,坏处就是由于你的定制行为给对象创建设定了前提假设,而这种潜在的前提假设一旦在某个外在环境中无法得到满足时,你就无法创建对象了,此时你必须修改对象的内部结构,也就是说你要修改你的前提假设了。这就意味着在你的对象设计中,你是通过修改对象创建的前提假设条件的方式来适应外部环境的变化的,这意味着你的对象内部是不稳定的。


而我的主张是对象本身要不控制对象创建的权利和行为,要给外部组装者以他想要的任意方式来创建对象,我信任外部组装代码,我不限定外部创建对象的行为,我给他们以创建对象的任何自由。也就是说我不给对象创建设定任何前提假设,所以对象内部是稳定的,在任何外部环境变化的情况下,外部组装代码都可以他相应的外部条件相配合的创建方式来使用对象

所以何者合理,何者不合理,我想有过实际工程项目经验的人心里都是雪亮的。

总结一下,双方的分歧在于:

对象本身是否应该控制对象被创建的权利和行为,你的观点是应该控制,保证对象可以预期到任何情况下被调用之后的结果,这样没有副作用,安全。我的观点是放弃控制,给别人自由,我不需要预期也不关心对象被调用的结果。

引用
看来我们还是对问题的前提的理解都没有统一。 你一枪,我一枪,都扎在了空气中。哎。交流真难啊。


其实你从来都避重就轻,从来没有正面回答potian的帖子,也从来没有正面回答我的帖子,而是抓住帖子中的某一个细节用词,就孜孜不倦的把讨论引向对你有优势,以你为主的方面。因此除非你开始正面回答potian和我的问题,否则这场讨论始终是没有结果的,这不是potian在回避,也不是我在回避,我一直很积极努力的正面和你讨论,为了能够得到把问题讨论清楚,你可以看到我从来不提容器,抽象工程,配置文件这些枝枝叶叶的话题,而是始终紧扣我们分歧的核心点,究竟该不该控制对象创建,反到是你喜欢顾左右而言他,喜欢把话题岔开,不去直面本质问题。


我的经验是,人的思维方式本来就不是完全一样的。所以遇到和我思维方式很不同的人,我虽然也比较不爽, 但是也只好耐下心来一点一点找出分歧点再逐渐缩小分歧范围. 我不会如potian那样不合己意就拍案而起, 并且和马路上骂大街的小将拍手相庆.

看你的说法,似乎你和potian都认为别人跟着你们的思维方式走是天经地义的,而如果别人有不同的思维方式就是大逆不道?


看看你的前面三分之二的回答。其实和potian一样,都是在一个又一个地扔出没有经过证明的结论。这是评论文的大忌。你可以扔没有根据的结论,我也可以。这有什么意义?
不是我回避,是因为我认为这样做是不对的。抛开具体问题,空对空地抽象地抛结论是得不到任何结果的。

我没有正面回答你么? 什么叫重? 什么叫轻? 我抓住的,不是细节问题,而是你立论的关键基础。我不喜欢空对空,我要具体。你自己看看,如果没有了你的“需求变化了,不一定要通过改动实现代码”这条,你的反驳还剩什么?你的反驳哪点能够接触到我的论点?我的立论你在开始的前提上就搞错了.

你们就那么自信自己对这个问题的理解就是对的?就没有一点兴趣花点时间仔细看看我对这个问题的反复重复,看看是不是自己理解的问题和我提出的问题有偏差?

我认为一个前提就错了的评价本身已经没有意义再作更多的批评了。
就象你自己树了一个靶子,打的很爽。可是于我何干呢?

好,无关的话说完。让我给你举一个我所谓的“抽象,空对空”的例子吧。

引用
坏处就是由于你的定制行为给对象创建设定了前提假设


学术讨论最忌讳给出了一堆名词却不给出严格的定义。
此处,你说的所谓“前提假设”是什么?举个例子也好啊。
相反,我的方案最引以为豪的就是假设最少。构造函数的方案明显假设比静态工厂要多。
你所谓的信任外界,不对外界做任何限制,似乎和封装原则背道而驰。换在其它的封装原则上, 比如成员变量都做成private的, 用你的逻辑,我也可以说:
要给外部使用者以他想要的任意方式来操作对象,我信任外部使用代码,我不限定外部使用对象的行为,我给他们以使用对象的任何自由。
所以,我不用成员函数来封装变量,所有变量都是公有。
是不是这样?
引用

这种潜在的前提假设一旦在某个外在环境中无法得到满足时,你就无法创建对象了,此时你必须修改对象的内部结构

这句话如果应用在private/public上,我也可以说,如果某个外部调用代码或者库或者容器就是需要直接使用成员变量而不是成员函数,你的封装过的对象就不能用,此时你必须修改对象的内部结构。

是这个意思吗?那么,到底什么是应该信任外部的?什么是应该封装的?这里面本来是有一个权衡的标准的。为什么要信任外部? 这里面是否要给出一个原因?如果你说不信任外部就导致业务逻辑不稳定,信任外部就不会,是否也要给出证明或者例子?难道你这个观点是不证自明的公理吗?

这些你的回答里面提到了吗?给人的感觉只是你认为任何的封装,“不信任”,“控制”都是不对的。
如此绝对的提法你让我怎么顺着你的思维方式走呢?



你说“只允许外部对象以你设定的方式来使用对象”,那么,哪个接口不是如此呢?
你能做一个模块,让别人以任意的方式调用?别人调用helloworld()是你,调用setTime(int)是你,调用任何的XXX()都是你?
可能吗?
构造函数不是只允许外界用这个类的名字来调用吗?

用学术一点的话讲,你这个结论本身就没有严格的逻辑。你自己都不能自圆其说什么叫做“前提假设”。

我也许没有真正理解你的意思,可是你和potian就喜欢在一些自己的直觉觉得舒服的假设基础上讨论问题,是否你假设我也和你们有一样的直觉,能够直接绕过具体定义感受到你们真正理解的东西呢?
我觉得如果想让别人理解一样东西,就不要假设别人有和自己一样的感觉,一步一个脚印地严格定义出你的观点。
否则,大家不免都是在原地绕圈子。
0 请登录后投票
   发表时间:2004-08-21  
引用
学术讨论最忌讳给出了一堆名词却不给出严格的定义。
此处,你说的所谓“前提假设”是什么?举个例子也好啊。
相反,我的方案最引以为豪的就是假设最少。构造函数的方案明显假设比静态工厂要多。


我的意思是指,在你的方案中,使用private和final的方式限制对象的构造和继承,然后只给外部使用者以你定制的静态工厂方法。这样的话,外部使用者只能以你限定的方式来创建对象。如果你提供的静态工厂方法不能够完全满足外部使用者的创建需求的时候,你是不是必须修改,或者增加你的静态工厂方法呢?

使用不使用静态工厂不重要,重要的是你把构造函数private了,把class final了。所以外部使用者不能通过其他途径获取对象了,只能通过你定制的工厂。

另外,我不认为这是一个学术讨论论坛,也不必追求学术的严谨性,我想在这个论坛的大多数人更加关心的是项目实施,软件过程管理,软件架构的设计方面。

引用
你所谓的信任外界,不对外界做任何限制,似乎和封装原则背道而驰。换在其它的封装原则上, 比如成员变量都做成private的, 用你的逻辑,我也可以说:
要给外部使用者以他想要的任意方式来操作对象,我信任外部使用代码,我不限定外部使用对象的行为,我给他们以使用对象的任何自由。
所以,我不用成员函数来封装变量,所有变量都是公有。


我们是在讨论是否应该将对象创建的权利交给外部,信任外部使用者无恶意的创建对象。那么讨论问题就应该限定在论题上,而不是随意的根据逻辑进行引申。例如我也可以根据你的逻辑引申一下,由于我们不应该信任外部,由于对象的封装原则,所以我们不应该信任GC,我们应该在对象内部进行对象销毁的控制,拒绝给GC以销毁对象的权利,我们凭什么信任GC?

引用
到底什么是应该信任外部的?什么是应该封装的?这里面本来是有一个权衡的标准的。为什么要信任外部? 这里面是否要给出一个原因?如果你说不信任外部就导致业务逻辑不稳定,信任外部就不会,是否也要给出证明或者例子?难道你这个观点是不证自明的公理吗?


这里面确实应该有一个权衡的标准,就是应该封装到什么程度,开放到什么程度。如何把握这个度,我也没有看过OCP,我想我也会找时间看看。但是以我目前的工作经验和项目经验来说,我倾向于封装对象的内在属性,开放对象的创建权利。并且在当今Java世界流行的框架,类似Spring,Hibernate等等都是遵循这样的原则的。

引用
用学术一点的话讲,你这个结论本身就没有严格的逻辑


我这个观点没有经过严密的数学理论推导证明,也不是公理。而是来源于我的实践经验和看到的当前流行的框架。我从来不把软件开发当做数学这样需用严谨的逻辑证明才能得到结论的学科来看待。也就是说软件开发之于我,不是自然科学范畴,而是社会科学范畴,所以我对你的那种一定需用一个严格确定的结论感到奇怪,也不认为证明一个观点一定需用某种数学推理才能够得到,就好像我从来不认为一定需用某种严密的逻辑推理过程来证明中国男篮因为拥有了姚明就不会输球一个道理。

所以我的看法是,在软件开发领域,不存在类似数学领域那种非对即错的明确的结论律。很多时候,采用各种方案都是各有利弊的,我们要做的就是权衡各种方案找出一个相对最好的实施方案。而不是由数学理论来指导你怎么做才是最好的。如果单纯从严格的逻辑证明角度来看,我承认你的观点一定是最严密的,缺陷最少的,但是这种观点放在实际的项目开发过程中的时候,就需要权衡每一步的经济代价了,因为单纯的抽象逻辑证明是排除了在真实环境中开发项目所面临到的各种社会因素加权之后得到的结论。但是放到真实的项目开发环境中的时候,这些社会因素将导致单纯的逻辑证明结论无法得以成功的实施,这个时候你必须权衡,因为社会因素的加权因子的代入而放弃数学逻辑上的美感和学术上的无懈可击。

罗嗦了那么多,我想我们的分歧在于我把软件开发做为社会科学来看待,追求项目最小代价的成功实施和最小代价的维护成本,采用社会科学的方式来证明我的结论正确(例如经验,成功的范例,流行的框架),而你把软件开发做为自然科学来看待,追求的是学术意义上的无懈可击和逻辑美感,采用自然科学的方式来证明你的结论正确。

既然大家彼此采用的证明方式都不同,无法用同一套思维方式来交流,那么看来这场争论是注定无疾而终的。
0 请登录后投票
   发表时间:2004-08-21  
再看看你这个推理
引用
这就意味着在你的对象设计中,你是通过修改对象创建的前提假设条件的方式来适应外部环境的变化的,这意味着你的对象内部是不稳定的

你真觉得你这个推论成立?

控制了对象创建就只能"通过修改对象创建的前提假设条件的方式来适应外部环境的变化"?

那么是否控制了成员存取也只能"通过修改成员存取的前提假设条件的方式来适应外部环境的变化"?

我不是很理解这里面的逻辑在哪里?


你真的认为不能在静态工厂上面通过其它的抽象啦, 配置啦来应对外界环境变化?
说到这里, 我忽然有点害怕, 你指的"环境变化"是什么? 也许对这个词我们的理解又出现了偏差呢.
我的理解是说:"外部的业务逻辑需求发生了变化"
这是你的意思吗?

还是说"外部使用的容器发生了变化", 有的容器支持工厂, 而有的不支持. 所以如果容器不支持工厂, 你的静态工厂就用不了了, 是这个意思吗?
0 请登录后投票
   发表时间:2004-08-21  
引用
采用各种方案都是各有利弊的,我们要做的就是权衡各种方案找出一个相对最好的实施方案


bingo! 这难道不是我要做的吗? 我没有给出利弊吗? 那么反对我的各位是什么论点呢?
readonly说static factory根本就不能用. 没有利,只有弊.
charon说用static factory的地方都应该重构成抽象工厂.
potian说你的static factory举的利益不存在.因为ocp. (我确实没有明白他把ocp引进来的意义在哪里)

哪位在论点里提出了"权衡"的意思? 不都是要一棒子打死static factory?


引用
使用不使用静态工厂不重要,重要的是你把构造函数private了,把class final了。所以外部使用者不能通过其他途径获取对象了,只能通过你定制的工厂

好! 这又是一个非常具体的反驳. 我喜欢!
有一点还有请你明确的, 你说的"其它途径", 是指继承吗? 还是说外界偏偏想调用构造函数? 还是两者都有?
关于继承, 我前面已经在利弊分析里给出了. 它确实是作为一个static factory的缺点存在的. 我无意否认. Effective Java里的分析也同样指出了这点.
是否继承很重要就是另一个问题了. 是否模块的作者有权利决定自己的模块是否允许继承也是另外一个问题.
其实, 关于继承是否重要, 是否应该用接口组合代替, 是否赢该避免, 这是一个深不见底的坑啊. 我们还是不要往里跳了吧.
让我们暂时圆滑一点, 认为用继承还是不用继承都有道理吧.

这里, 我们只列举利弊, 不做结论好吗?

关于构造函数, 你调用构造函数也是调用, 调用工厂仍然是调用. 如果我变态做了一个只能调用静态工厂而不能调用构造函数的容器呢? 是否你的批评就要反过来了?


当然, 事实中容器都是支持构造函数的, 因为这是一个约定. Bloch也提出, 静态工厂的一个问题是, 它不是约定俗成的. 外面不能简单地把它和构造函数区分开来. 必须要读文档.

同样是一个缺点.

引用
另外,我不认为这是一个学术讨论论坛,也不必追求学术的严谨性,我想在这个论坛的大多数人更加关心的是项目实施,软件过程管理,软件架构的设计方面

呵呵. 如果双方在对问题的理解上有所偏差. 不通过严谨的科学方法, 仅仅靠着各自的直觉和经验你觉得能讨论出结果来?
如果讨论了半天我们发现我们说的根本就是两回事呢?
丰富的项目经验, 软件过程管理能够保证交流不出现误会?


引用
我们是在讨论是否应该将对象创建的权利交给外部,信任外部使用者无恶意的创建对象。那么讨论问题就应该限定在论题上,而不是随意的根据逻辑进行引申。例如我也可以根据你的逻辑引申一下,由于我们不应该信任外部,由于对象的封装原则,所以我们不应该信任GC,我们应该在对象内部进行对象销毁的控制,拒绝给GC以销毁对象的权利,我们凭什么信任GC?

反例举得好!
那么, 我们是否可以从这些看似矛盾的反例中得出一个不那么矛盾的结论:
是否信任外部, 是否控制, 是否封装是要看上下文的. 要看控制的是什么, 封装的是什么, 以及这样作的结果是什么. 简单说不信任或者信任都是没有直接意义的呢?

引用
并且在当今Java世界流行的框架,类似Spring,Hibernate等等都是遵循这样的原则的

readonly刚刚告诉我, pico/spring都是支持静态工厂的了.
而且, 如果我的原文是: 框架不支持工厂是个缺点. 你就不能绕回来用框架不支持来说明工厂不好, 进而说明框架不支持是优点. 这里有个循环论证的陷阱的.


引用
罗嗦了那么多,我想我们的分歧在于我把软件开发做为社会科学来看待,追求项目最小代价的成功实施和最小代价的维护成本,采用社会科学的方式来证明我的结论正确(例如经验,成功的范例,流行的框架),而你把软件开发做为自然科学来看待,追求的是学术意义上的无懈可击和逻辑美感,采用自然科学的方式来证明你的结论正确。

你说的有道理. 我确实有这个倾向.

不过如果你倒退回到讨论的开始, 我提出问题的方式本来就是我一贯的科学分析的方式: 列举优缺点.

那么, 如果各位对这个研究方式本身就不感冒, 直接就说我们不在乎你那些优缺点就是了. 洋洋洒洒地用一个领域的标准来评判另一个不同领域的标准是否本身就显得没什么意义了呢?
0 请登录后投票
   发表时间:2004-08-21  
另外, 本文开宗明义就说了: 一切围绕需求.

而原谅我稍微形而上一下啊:
面向接口的核心就是分析需求, 在接口上表达需求, 剔除需求里没有的假设. 这是接口的最小化原则吧?

下面看你这个反驳

引用
如果你提供的静态工厂方法不能够完全满足外部使用者的创建需求的时候,你是不是必须修改,或者增加你的静态工厂方法呢?


当然, 任何接口设计都是如此. (这个接口就泛化了, 指的是任何的两个模块之间的打交道方式)
所以我们要认真设计接口.
如果你分析需求发现静态工厂不能满足, 自然就需要有所改进.
而如果你开始分析需求的时候认为可以满足, 以后需求变化后, 不可以了. (对这点, 我还是很怀疑是否存在这种情况, 能否请你举个例子呢? 这应该也是重视实际工程的人喜欢的吧?), 自然就要改变接口.

不只是静态工厂云云, 你设计的的任何interface, 都有一个如果它的设计不满足需求后的变化问题.


而我理解的你的意思似乎是说:
如果我不用静态工厂, 直接公开构造函数, 就可以在需求变化的时候不用修改我的内部代码.

是这样吗?

能否举个实例呢? 然后给我一个机会看看是否用静态工厂也仍然是不用改变内部代码的呢?


对我这个不喜欢形而上的人, 用实例说服我会有效很多.
0 请登录后投票
   发表时间:2004-08-21  
我服了你了。讨论这个问题,我建议我们必须抛开容器了,配置文件了,Bloch之类的旁枝,否则就扯个没完了,所以回到主题上来,我发现potian的一段话很好的表达了我的意思:

potian 写道
如果在不同情况下,例如需要同步或者需要lazy等等的话,你必须针对修改你的代码,实际上我们最关心的是能够在不同场合使用同一个对象的业务逻辑,而你这样做的话会紧紧因为我们需要不同的对象创建方法而修改对象的代码,这是非常不智的,这也是singleton被视作evil的重要原因之一。(例如singleton,有时候我们希望使用超类的实例,有时候希望使用子类的实例,有的时候我们希望产生单个,有的时候需要产生多个[取singleton单控制点的含义],有的时候需要同步,有的时候不需要同步,在集群的情况我们甚至可能需要数据库来实现唯一化控制,有时候希望缓存,有的时候不需要缓存,有的时候希望增强,有的时候希望采用动态代理产生。


potian 写道
相对而言,我们往往不在乎组装代码的重用性,而是追求业务代码本身可以被按照不同的组装方式使用,例如既可以在EJB下,也可以在普通的Java应用程序中,既可以作为远程传输的对象,因此,我们往往不会去强制对象构造和产生的方式(例如由EJB容器产生,由IOC容器产生、有抽象工厂产生,或者直接由new产生),相反,是把对象的产生交给外部负责,这样才能达到在不同场合下对象最大的可重用性。说个简单的,如果对象的构造方法是私有的,那么现在的很多框架(例如hibernate,JavaBean,EJB等等你就根本不能使用了).这也是现在认为POJO比有特殊要求的对象更好的原因,因为任何一种框架和技术都可以自由选择自己的方式来创建对象。


ajoo 写道
而我理解的你的意思似乎是说:
如果我不用静态工厂, 直接公开构造函数, 就可以在需求变化的时候不用修改我的内部代码.

是这样吗?

能否举个实例呢? 然后给我一个机会看看是否用静态工厂也仍然是不用改变内部代码的呢?


对我这个不喜欢形而上的人, 用实例说服我会有效很多.


其实不是需求变化,而是外在的调用环境发生变化,举例来说,就是EJB的环境调用你的对象,换成了Hibernate调用你的对象,又换成了Spring调用你的对象。每种调用环境都有他自己特定的创建你的对象的方式,例如Spring喜欢用配置文件,Hibernate用动态增强,EJB用静态增强加上JNDI。可能还有n种不同的调用环境。

如果在对象内部你不限定对象创建的方式的话,各种调用环境都可以成功的调用你的对象。如果你限定了对象创建的方式的话,你就必须为每个外部调用环境来适配你内部的工厂方法。

可能真的只有当举出来具体的代码例子才能够让这场争论划一个句号。但是我还没有那么深的造诣,没有分别研究过各种环境是遵循什么规律来创建对象的。
0 请登录后投票
   发表时间:2004-08-21  
好吧. 避免误解的最好方式, 我想就是复述一遍你的观点先.

你是说, 因为ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求, 所以如果接口上只给了一个instance()函数,这些外部环境就很难调用了.

是否如此?

我们先弄清楚了前提再说吧.
0 请登录后投票
   发表时间:2004-08-21  
ajoo 写道
好吧. 避免误解的最好方式, 我想就是复述一遍你的观点先.

你是说, 因为ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求, 所以如果接口上只给了一个instance()函数,这些外部环境就很难调用了.

是否如此?

我们先弄清楚了前提再说吧.

不是“ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求”,是ejb、hibernate、spring(当然,这个列表可以无限制长下去)这些使用对象的框架或其他代码希望对象本身的构造没有任何特殊限制和要求
0 请登录后投票
   发表时间:2004-08-21  
potian 写道
ajoo 写道
好吧. 避免误解的最好方式, 我想就是复述一遍你的观点先.

你是说, 因为ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求, 所以如果接口上只给了一个instance()函数,这些外部环境就很难调用了.

是否如此?

我们先弄清楚了前提再说吧.

不是“ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求”,是ejb、hibernate、spring(当然,这个列表可以无限制长下去)这些使用对象的框架或其他代码希望对象本身的构造没有任何特殊限制和要求


我怎么觉得像是一回事的两种说法啊?
如果这些东西不对对象构造的方法有自己的要求, 如果它们做到足够小的侵入性, 你还在乎这个组件没有针对自己组件本身的"任何特殊限制和要求"吗?


你们的意思不是说这些东西都希望组件提供公共构造函数吗? 是这样吗?

确认一下, 我再继续.
0 请登录后投票
   发表时间:2004-08-21  
ajoo 写道
potian 写道
ajoo 写道
好吧. 避免误解的最好方式, 我想就是复述一遍你的观点先.

你是说, 因为ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求, 所以如果接口上只给了一个instance()函数,这些外部环境就很难调用了.

是否如此?

我们先弄清楚了前提再说吧.

不是“ejb的调用方式, 或者hibernate, 或者spring这些外部环境可能都对调用方式有些特殊的要求”,是ejb、hibernate、spring(当然,这个列表可以无限制长下去)这些使用对象的框架或其他代码希望对象本身的构造没有任何特殊限制和要求


我怎么觉得像是一回事的两种说法啊?
如果这些东西不对对象构造的方法有自己的要求, 如果它们做到足够小的侵入性, 你还在乎这个组件没有针对自己的"任何特殊限制和要求"吗?


你们的意思不是说这些东西都希望组件提供公共构造函数吗? 是这样吗?

确认一下, 我再继续.


如果语言本身提供的就是静态函数,那么这些东西就希望这个对象可以从静态函数创建出来,如果语言本身提供的是公共构造函数,这些东西希望这个对象可以从公共构造函数创建出来,你认为这是特殊要求吗?
0 请登录后投票
论坛首页 Java企业应用版

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