|
该帖已经被评为精华帖
|
|
|---|---|
| 作者 | 正文 |
|
时间:2004-09-24
yhc0125的那个帖子本来只是讨论OCP的,所以我还是另外开一贴,专门邀请ajoo讨论interface在java编程中的作用问题。
先说说你的那个回贴。 firebody直接就晕过去了 我还是认真的看了看,然后决定不一条一条的回复你的回复了。这样反而不利于深入的讨论,Trustno1说这样的讨论甚是无趣,我也觉得,但是讨论这个问题的意义,还是要比“拿着尺子测量杆盘刀叉与餐桌距离”要大一些,所以我还是继续。 我们讨论可以归结为这样几个方面: 1、接口的定义是由谁来定义的,但是这样的思路其实存在以下几个问题: 1)按照先定义接口,在进行实现的思路,那么肯定是先有interface,才有class的,对吧。那么咱们讨论一个真实的系统时:这就必然导致一个至顶向下的开发过程,那么我就需要问你了,当你用这样一种思路进行开发时,对于一个Web项目,你是不是会先从jsp或者servlet写起呢? 2)还是按照先定义接口,再进行的实现的思路,那么一个class实现多个interface的情况如何才会出现呢? 3)当接口是我定义的,而实现是别人做的这样情况出现的时候,你的下面这句话就很难理解了“接口是你自己定义的。反映你自己的需求。什么叫没有完成? ”如果按照你的说法,那么需求怎么会变的呢?当需求变的时候,从哪里开始变起呢? 2、关于单元测试的问题,我始终认为单元测试并不能等于100%测试。假设A要B一起工作的,你通过单元测试,可以确定的,是A和B可以各自独立工作。那么我们应该通过什么方法,来确认A和B可以协同工作呢? 按照我的思路,假设A需要使用到B的功能,那么我会先测试B,在保证B通过了所有测试之后,我在测试A,假设出了问题,我就知道问题只会存在于A。就像一层一层打基础一样,当我的底层基础得到确认之后,我才能够再向上搭一层。 ajoo,你现在应该可以看出来了,我们俩的思路是相反的,一个是至顶向下,一个是至底向上。但是按照TDD这样的思路,我相信本质上他就是支持至底向上的。当然,TDD蕴含至底向上大家可以讨论一下,看看我是不是理解正确。 3、关于工作量的问题,我本来希望你通过代码来说明“什么情况下,才会出现,只需要改类,不需要改接口的情况呢?” 而你的回答甚是标准“again, 接口IB围绕你的需求定义,并不依赖于B。抽象不依赖具体。B改了, 只要你的需求没改,接口就不用改。” 那还是我来举例子吧。 第一种,使用接口的情况 [code:1] //接口 public interface IA{ public void doSomething(); } //实现 public class A implementation IA { public void doSomething(){ System.out.println("doSomething"); } } //调用 IA a=new A(); a.doSomething(); [/code:1] 第二种,不使用接口的情况 [code:1] //直接实现 public class A{ public void doSomething(){ System.out.println("doSomething"); } } //调用 A a=new A(); a.doSomething(); [/code:1] 无论以上的哪一种情况,只要doSomething这个函数的接受参数等等属性没有改变,那么接口和具体的对象都是不用改的。而如果doSomething的接收参数发生改变,第一种使用接口的例子,就需要改两个文件,而第二种则只需要改一个文件。当然,在调用者那里,都是需要改的。 这就是工作量,ajoo说:“对你使用接口耦合的bonus啊。反正是免费的。”我可不认为是免费的,在需求尚未固化之前,我每次如果能少改一个文件,那也是很划算的。 麻烦ajoo举一个使用了接口,能够减少工作量的例子。 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
时间:2004-09-24
庄表伟 写道 1)按照先定义接口,在进行实现的思路,那么肯定是先有interface,才有class的,对吧。那么咱们讨论一个真实的系统时:这就必然导致一个至顶向下的开发过程,那么我就需要问你了,当你用这样一种思路进行开发时,对于一个Web项目,你是不是会先从jsp或者servlet写起呢? 偶先写Domain Object, 然后定义Component/Service Interface, 然后是测试代码, 然后把interface逐步实现完整, 让测试代码通过. jsp?servlet? 偶已经有好久没有用这些东东了...... 庄表伟 写道 ajoo,你现在应该可以看出来了,我们俩的思路是相反的,一个是至顶向下,一个是至底向上。但是按照TDD这样的思路,我相信本质上他就是支持至底向上的。当然,TDD蕴含至底向上大家可以讨论一下,看看我是不是理解正确。 faint......真不知道你的TDD实践是怎么做的....... 庄表伟 写道 2)还是按照先定义接口,再进行的实现的思路,那么一个class实现多个interface的情况如何才会出现呢? 对于Component/Service来说, 即便在50年内, 也只有一个Class实现, 偶还是会毫不犹豫地定义出interface来. 偶为什么要关心这种情况什么时候会出现? 庄表伟 写道 2、关于单元测试的问题,我始终认为单元测试并不能等于100%测试。假设A要B一起工作的,你通过单元测试,可以确定的,是A和B可以各自独立工作。那么我们应该通过什么方法,来确认A和B可以协同工作呢? 如果A和B都是一个接口, 偶可以用MockObject来保证, 接口就是协议, 有了协议你就可以隔离测试了, 满足了协议就能保证协同工作了. 有机会偶来讲讲一个TDD在开发摄像头图形识别功能的例子. |
|
| 返回顶楼 | |
|
时间:2004-09-24
1。接口不一定会减少工作量,因为接口不是为了减少工作量才出现的。但是到了
后期,一个设计良好的系统一定会减少工作量; 2。接口不一定已开始就会明显的浮现出来,有可能在开发或者系统演化的过程中 根据涌现的需求提炼出来的; 3。设计并非一成不变的从上到下或者从下到上,可能在开发的过程中会在两个方 向上不断地来回移动,以寻求最佳的观察视点; 4。接口是自己定义的,但是有可能/有能力的话请尽量为别人考虑一下,提供一 个扩展性更好的接口; |
|
| 返回顶楼 | |
|
时间:2004-09-24
其他的不想说,buttom up还是top down,一条道走到黑是不可能的。
好莱坞的垃圾电影<The Core>里面有句话我非常的佩服。当他们出发拯救地球之前有人问如果地心密度不是我们想象的那样怎么办。那个貌似Karl Sagan的地质学教授说:"Science is just guess"。 通常的设计总是先猜测一个模型,然后尝试,如果错了,就开始归纳经历过的事实对原先的模型进行修改。Top down->fail->buttom up->.......。 (猜测,归纳),(buttome up,top down)这两对东西是设计或者说任何理性分析所必需的。 |
|
| 返回顶楼 | |
|
时间:2004-09-24
工程实践可以剔除理论中很多似是而非的东西
|
|
| 返回顶楼 | |
|
时间:2004-09-24
youngS 写道 1。接口不一定会减少工作量,因为接口不是为了减少工作量才出现的。但是到了
&&&&后期,一个设计良好的系统一定会减少工作量; 2。接口不一定已开始就会明显的浮现出来,有可能在开发或者系统演化的过程中 &&&&根据涌现的需求提炼出来的; 3。设计并非一成不变的从上到下或者从下到上,可能在开发的过程中会在两个方 &&&&向上不断地来回移动,以寻求最佳的观察视点; 4。接口是自己定义的,但是有可能/有能力的话请尽量为别人考虑一下,提供一 &&&&个扩展性更好的接口; 接口和未来的需求演化没有逻辑上的联系,接口是归纳一系列事实的结果。如果未来的事实还没有发生,你的设计的接口怎么可能对他们产生作用?即使有也只不过是一种赌博,也就是你碰巧了撞好运而已。只有当你整个系统的需求稳定在你所归纳的接口范围之内,他们才能帮助你减少工作量。当然系统需求最后总是否收敛,这就很难说,最关键的是时间这个因素与这个软件需求的关系。 |
|
| 返回顶楼 | |
|
时间:2004-09-24
老庄
本来我不好意思来跟你争论的,可是我对于Interface上和你的争论,我还是保留我原来的观点。(使用了Java,就应该使用Interface。) 别的依据我不说了,ajoo会冲过来跟你“拼命”,到那时,我等着看热闹! 我只说一点,Interface是一个功能接口,它远远比Abstract class来得轻便得多。但它同样具备了Abstract class所定义的功能约束(method name,return type,parameters)。我觉得,这正是interface能够出现在java的原因吧。 interface是可以伴随着功能的分析得出来的。当然也可以在coding中refactor出来。 既然你说Abstract class出来了,那么为什么Interface就不能直接从Abstract class抽取也能得出来呢。class 和 interface在定义功能需求上是等价的,interface又何尝不能直接使用呢?它来得比class轻便,所以定义功能,我首选Interface。 另外,Interface在实现OCP上是最彻底的,这点也是我喜欢使用它的缘故。 |
|
| 返回顶楼 | |
|
时间:2004-09-24
Trustno1 写道 接口和未来的需求演化没有逻辑上的联系,接口是归纳一系列事实的结果。如果未来的事实还没有发生,你的设计的接口怎么可能对他们产生作用?即使有也只不过是一种赌博,也就是你碰巧了撞好运而已。只有当你整个系统的需求稳定在你所归纳的接口范围之内,他们才能帮助你减少工作量。当然系统需求最后总是否收敛,这就很难说,最关键的是时间这个因素与这个软件需求的关系。 这是我的回答: Trustno1 写道 通常的设计总是先猜测一个模型,然后尝试,如果错了,就开始归纳经历过的事实对原先的模型进行修改。Top down->fail->buttom up->.......。 (猜测,归纳),(buttome up,top down)这两对东西是设计或者说任何理性分析所必需的。 区别在于经验的多寡。很多时候做项目只是考虑眼前的利益,所以对接口的归纳可能不是那么彻底,只是说这个时候的接口对目前的项目非常够用,但是如果经验丰富点,或者说看得远一点,也许就会再多花点力气作更好的接口抽象,做个更灵活的抽象体系。 |
|
| 返回顶楼 | |
|
时间:2004-09-24
接口和抽象类是不矛盾的。它们在语义上有不可互换的地方。
firebody 写道 class 和 interface在定义功能需求上是等价的,interface又何尝不能直接使用呢?它来得比class轻便,所以定义功能,我首选Interface。 如上所说,从语义功能上来讲,class和interface在定义功能需求上不一定就是等价的。interface比抽象类更抽象。抽象类不管怎么抽象,始终是个类,但是接口不一定是个类,它定义的规则可能会贯穿多个互不相干的抽象类。这就是interface和abstract class不一定可以互换的原因。当然,如果你定义的interface和abstract class在语义上属于同一范畴时,对于它们之间的互换性我个人是没有什么意见的。 |
|
| 返回顶楼 | |
|
时间:2004-09-24
庄表伟 写道 无论以上的哪一种情况,只要doSomething这个函数的接受参数等等属性没有改变,那么接口和具体的对象都是不用改的。而如果doSomething的接收参数发生改变,第一种使用接口的例子,就需要改两个文件,而第二种则只需要改一个文件。当然,在调用者那里,都是需要改的。
这就是工作量,ajoo说:“对你使用接口耦合的bonus啊。反正是免费的。”我可不认为是免费的,在需求尚未固化之前,我每次如果能少改一个文件,那也是很划算的。 接口和抽象类除了支持抽象外,还有个很重要的功能,就是支持多态性。 很抱歉,昨天发到这就回家了,暂存了一下。 对庄老的精益求精表示一下敬意先!以后多学习。 针对上面具体的例子,首先感觉到有问题的地方就是服务的调用。 通常我们对服务提供者的实例化都不是在客户方直接进行的,而是给他传入一个参数,从而达到屏蔽的目的。而这一点却没有体现出来。interface 典型的用例是这样的。 [code:1]public interface IA { void doSomeThing(); } public class A0 implements IA { public void doSomeThing() { //concret implements } } public class A1 implements IA { public void doSomeThing() { //concret implements } } public class Logic { public void main(String[] args) { Client client = new Client(); A a = new A0(); Client.dispose(a); } } public class Client { public void dispose(A a) { a.doSomeThing(); } }[/code:1] 这个例子说明了一点,就是当业务逻辑改变的时候,不是改动已有类,而是提供新的实现。我想现实中也大多数人都是如此吧,直接改类的情况一般都是发生在业务逻辑确定,但实现需要改变的情况,是短期内开发的行为。但当业务逻辑需要变化的时候,却是一个相对长期的过程,采用的往往是添加而不是修改的策略。概念清楚了吗?庄老谈的是“改现有类”,而我们需要的往往是“实现新的业务逻辑”。后一种情况下,直接用类和加入接口是在同一起跑线上的。 但在更复杂的情况下,引入接口的优越性就会显现出来。 把昨天的话说完,我先看看后面的回复 |
|
| 返回顶楼 | |











