`
ajoo
  • 浏览: 453121 次
社区版块
存档分类
最新评论

依赖是否可以作为一个独立的衡量软件质量的标准?

阅读更多
这个争论的背景有点复杂。我就尽量简化了说。

遗留系统有一个自制的service locator。是一个静态函数:
public static Object newObject(Class interfaceOrDefaultClass, Class[] parameterTypes, Object[] arguments);

使用起来是这样:
ImplFactory.newObject(MyInterface.class, new Class[]{int.class, String.class}, new Object[]{new Integer(1), "abc"});

这个函数会根据一个properties文件的配置来寻找一个带有制定构造函数的实现或者继承MyInterface的类。
比如,如果配置文件里面配置了
com.mycompany.MyInterface=com.mycompany.MyInterfaceImpl

而MyInterfaceImpl有这个构造函数:
public MyInterfaceImpl(int i, String s);

那么,MyInterfaceImpl就会被使用。

而如果调用的时候用了一个缺省类,那么如果配置文件没有配置,就会使用这个缺省类,比如:
ImplFactory.newObject(DefaultMyInterfaceImpl.class, new Class[]{int.class, String.class}, new Object[]{new Integer(1), "abc"});


这个自制的service locator无疑是非常原始,也是很难用的。为了准备向一个真正的ioc container过渡,我实现了一个dynamic proxy来封装ImplFactory。

使用起来如下:
MyInterface = serviceFactory.getMyInterface(1, "abc");

使用者只需要把getMyInterface这个函数声明在ServiceFactory这个接口中,就可以直接使用getMyInterface()函数了。当然,这个serviceFactory是作为倚赖被注射进客户类的。以后,如果我们切换成了ioc容器,比如spring,只要增加一个dynamic proxy就行了,客户代码基本不用动。

我的同事(ImplFactory就是他亲手做的),对这个ServiceFactory不是太感冒。他认为这样做明显的好处就是一个语法糖,语法漂亮一点而已。而因为ImplFactory本身已经是个抽象,ServiceFactory又是包在ImplFactory外面的抽象,那么"abstraction on top of abstraction"就显得多余,或者说过度设计。我对这样一个在我看来毫无争议的问题有点不知道怎么说,不过我跟他说这样以后可以轻易地转移到别的ioc container上面,才说服了他。


ImplFactoryProxy具体怎么实现的,只要懂dynamic proxy的都会,我就不赘述了。



这个实现里面,有一个问题,就是怎么处理这个“缺省实现类”。在原来的代码里面,缺省实现类是硬编码在客户程序里的。而现在的用法里面,客户不能制定缺省实现类,所以这个信息需要额外提供给这个dynamic proxy。

我的做法仍然是注射,通过注射一个java.util.Map对象到这个ImplFactoryProxy类,来给这个类提供缺省实现信息。签名如下:
public class ImplFactoryProxy {
  private final Map defaults;
  ImplFactoryProxy(Map defaults) {
    this.defaults = defaults;
  }
  ...
}


写好了ImplFactoryProxy之后,我面临的下一个问题是怎么得到这个Map。我的做法是通过ClassLoader.loadResourceAsStream()来读入一个存在当前package里面的properties文件,然后调用Properties.load(inputStream)来得到Properties,这个Properties对象自然就可以注射进ImplFactoryProxy了。

至此,希望你会说:没什么亚。大家都是这么做的。


不过,问题来了。
我的那个老资格的同事在review代码的时候看到这个ClassLoader.loadResourceAsStream,说:这个不行,我希望你改成用我们的PropFactory框架。

话说,这个遗留系统有一个相当强大(或者说复杂?)的读取property的框架,这个框架除了一般的按照key读取value,还支持树形的property,也就是说,一个key可能对应一个子property map。(当然,还有其他功能)

用法是:
PropFactory.getInstance().getProperty("property file name").getPropertyValue("key");


这样的代码充斥整个code base。


我对这个框架的态度是相当保留的。主要的原因是我认为大多的配置值应该通过注射,而不是主动地去找PropFactory框架要。这种PropFactory.getInstance()的代码使单元测试变得困难,而且加大了系统耦合,随便一个模块就要依赖于PropFactory。

而我在我的dynamic proxy中不使用PropFactory,除了上面的原因,还有以下几点:
1。我要的就是一个简单的key-value map。根本不需要PropFactory提供的那么多功能。
2。Properties, ClassLoader.loadResourceAsStream都是标准jdk的东西,用起来也不难。而且我也做了一个IOUtils类来封装这部分代码:
IOUtils:
  Properties loadProperties(ClassLoader loader, String resourceName);

3。我这个dynamic proxy相当的self contained。它基本上和现有的遗留系统除了ImplFactory没有任何其它关联。我也不希望引入任何不必要的依赖。

但是,显然,这些观点在同事那里是站不住脚的:
1。不管你需不需要额外的功能,你直接调用这个api就好了。又不需要额外写代码。
2。项目中大家都使用PropFactory来读取配置。如果大家你写一个ajoo way of reading property,他写一个bjoo way of reading property,那不是乱套了?这样做破坏了一致性,增大了团队协作的难度。
3。依赖就依赖了,有什么关系?这个几乎是公司内部的事实标准了。大家都这么用,还是头回听说有人对这个依赖有问题的。
4。如果不用PropFactory,谁能保证你的代码就在任何情况都工作?这个项目的build process, deploy process都很复杂,难以预测你这个代码在nightly build甚至在生产环境中也会工作。


对此,当然我是不同意的。我认为,ClassLoader.loadResourceAsStream几乎是工业界的标准,相比于一个公司自制的标准,我还是更倾向于相信被无数人证明工作的业界标准。
而且,试图让PropFactory包打一切也是不现实的。最起码,你用的commons logging, log4j等等开源库,都不可能依赖于你自己写的PropFactory,它们只能使用ClassLoader。所以这个一致性从一开始就不存在。
最后,我认为“一致性”在项目中被错误解读了。打个比方,项目中大家一致都是用jsp,但是我不认为当只需要一个servlet或者甚至一个静态的html的时候,我们也必须要为了一致性通过jsp来绕一圈生成这个servlet或者html.



经过争论,我还是做了妥协,只要我可以注射Map,你非要用in house framework而不是业界标准来读文件,也罢。

于是我的方案变成:
new ImplFactoryProxy(PropFactory.getInstance().getProperty("defaults.properties").toProperties())

这个toProperties()是PropFactory框架提供的一个函数,可以把我们inhouse的IProperties转换成java.util.Properties。

最终面向用户的接口(在没有采用ioc容器的情况下,只好还是允许客户代码主动取得ServiceFactory实例。)是:
public class ServiceFactoryUtil {
  public static ServiceFactory getServiceFactory();
}

这个类负责调用PropFactory.getInstance(),并且提供singleton服务以避免重复读取properties文件。


这几乎是我可以接受的底线了。


但是,同事仍然不满意。他最喜欢的是我不去注射Map,而是在ImplFactoryProxy内部直接调用PropFactory。这个在我来说是不可考虑的。

当然,他也退了一步,同意我的注射方式。但是认为我不应该注射java.util.Map,而应该注射IProperties。

同事的理由:
1。这样可以避免一个toProperties()调用。别人读代码的时候,可以不会纳闷“为什么这里要这么做?”。
2。java.util.Map这个接口太肥大。我需要的其实就是一个get(),最多加上keySet()和containsKey(),用一个java.util.Map不合适。


而我反对使用IProperties,理由是:
1。IProperties不是标准接口,我宁愿以来jdk标准接口。毕竟熟悉java.util.Map的比IProperties多多了吧?即使IProperties是在公司内部被“一致”使用。
2。没人会纳闷为什么要调用toProperties()。如果这都要纳闷,那么整个遗留系统的那数万行的代码就没法读了。
3。如果说java.util.Map不是最小接口,IProperties也不是。它也有一些我不需要的函数。
4。整个公司从来没有人用注射的方式来使用IProperties。大家都是PropFactory.getInstance()这样从头调用的。难保注射IProperties不会出什么问题。(比如,同步问题?后来,虽然同步问题没法验证,我确实发现了IProperties不支持Serializable,致使我的ServiceFactory也不能Serializable。这样类似的问题,如果真是采用了IProperties,还不知道会不会陆续向外蹦呢)。

最终,因为我是这个功能的开发者,还是以我的意见为主了。但是我并没有说服同事。在争论过程中,让我深感郁闷的是,我的“减小依赖”的论点根本不为同事接受,似乎在他们看来“依赖”并不能作为一个理由。而我也发现要把减小依赖和他们接受的DRY,unit test等原则直观地联系起来不太容易。

那么,你是怎么看这个问题的呢?
分享到:
评论
21 楼 wandou 2007-08-20  
这种情况多的是啊,ajoo的军事过硬了,政治还是不够合格啊。
强龙不压地头蛇,不是猛龙不过江。看你选那一种处理方法了。
如果有这样的机会,就用事实说话,自己做一套新的框架。然后大家比武(当然,老总必须能当裁判才行)。。。。这叫不是猛龙不过江,你不是牛吗,我比你还牛。
要不就忍着,一点点的慢慢改变。慢慢的做出自己的框架,替换现有的框架。这叫强龙不压地头蛇,慢慢把自己变成地头蛇,然后蛇咬蛇。
20 楼 JerryZheng 2007-08-16  
ajoo 写道
离开公司了。没办法,不合则去吧。

呵呵 我没实践过敏捷,不过从ajoo兄的经历来看,敏捷对员工的“EQ”要求也是蛮高的,如果buddy是一个很独裁的人,敏捷在某种程度上会伤害组织。
19 楼 ajoo 2007-08-16  
离开公司了。没办法,不合则去吧。
18 楼 ajoo 2007-08-16  
离开公司了。没办法,不合则去吧。
17 楼 ajoo 2007-08-16  
即使值要变动,也可以注入的。不要忘了,java.util.Map只是一个接口而已,真需要用一个超强的property框架,随手写一个adapter好了。注射的好处就在于保持简单,但是又不牺牲灵活性。

property框架容易扩展?误解呀误解,我看它反而是累赘,臃肿。
16 楼 myreligion 2007-08-16  
对于你注入properties值和使用那个内部框架PropertyXXX的争论,我觉得这个要看properties值的是否会发生变化。

如果这个值在系统运行期间不需要变动,注入显然是最好的;不过如果这个配置需要动态调整,好比可以让管理员在系统后台配置,每次读取的时候给propertiesXXX框架要就更加有优势了。

而且property框架可以更容易扩展,例如把配置信息集中存储到数据库中等等,如果有这种需求,注入就不能满足需求了。我们就因为这种情况而开发了一个提供property的框架。

15 楼 heartsong 2007-08-16  
做技术的人员,应该多多学习与他人沟通交流,有时最先进的技术并不能在项目里得到应用,为什么?因为从项目最终的结果来看,使用先进技术也许并没有带来多少好处。商业公司不是科研机构,讲究务实。老板可能一点技术也不懂,他只关心这个项目是否可以按时保质的完成。
14 楼 fixopen 2007-08-16  
是一pair两散呢还是离开了公司?
13 楼 ajoo 2007-08-15  
这几个帖子是集中在一段我的低潮期发的。我当时的最大问题还是和人交往经验不足,始终不知道是不是自己做的不够,还是对方太过分。也不想和别的同事沟通,毕竟人家是在公司干了十年的元老,如果我出去和别人讲我们分歧很大,很有可能是自找没趣。和lp商量,也被指责和人要搞好关系,不要出妖蛾子。所以极度郁闷了好一阵。

后来,终于忍无可忍,一拍两散。然后不再顾忌重重,和老板,同事交流后才发现,此人不仅仅是和我这样,他的权利欲,支配欲和固执,over-defensive居然是公司里面众所周知的(他和大老板,和比他高的头争执起来一样让人头疼,而且又特别能说,不过因为毕竟都是为了工作,老板人也比较好,不愿意摆出老板架子来压人,而且公司的那一摊也还离不开他,所以就一直这么下来了。),只不过大家都比较容忍,也不会象我这样在架构上和他有冲突,他怎么说就怎么做就是了。

anyway,离开了也不赖,毕竟合作这么困难总之是不爽。
12 楼 fixopen 2007-08-15  
减少依赖是一个很好的原则,只要是用来减少我对别人的依赖,而不是别人的我的依赖:)

其实无论是减少依赖,还是DRY,都是有目的的,减少依赖的目的是增加环境变化的适应性,降低维护负担,DRY是减少出现缺陷的可能性,降低开发负担。当然,它们同时还有一些次要的作用。

如果我们对环境不变充满信心,可以不需要减少依赖,如果我们对程序员大脑充满信心,我们可以不坚持DRY。

看起来你们的差异就是立场不同而已。
11 楼 fixopen 2007-08-15  
减少依赖是一个很好的原则,只要是用来减少我对别人的依赖,而不是别人的我的依赖:)

其实无论是减少依赖,还是DRY,都是有目的的,减少依赖的目的是增加环境变化的适应性,降低维护负担,DRY是减少出现缺陷的可能性,降低开发负担。当然,它们同时还有一些次要的作用。

如果我们对环境不变充满信心,可以不需要减少依赖,如果我们对程序员大脑充满信心,我们可以不坚持DRY。

看起来你们的差异就是立场不同而已。
10 楼 basicbest 2007-02-14  
aninfeel 写道
中国的程序界已经进入“大跃进”时代,“人有多大胆,地有多大产”,能复制就复制,能不写文档就不写文档,能一个月完成就十天完成,谁还管你那么多?


感情上理解这种心情,但是在实际中,我不否定这种方式,甚至偏向于使用这种方式。毕竟,“做系统”和“种田”是有本质上不同的,“大跃进”也没有什么不可以。
9 楼 basicbest 2007-02-14  
jkit 写道
为了让效果更明显,我们来看一下放大的情况
假设team里面有10000个人,系统已经有1000个类似PropFactory的共通模块。而你觉得这些模块用起来都不爽,自己实现了1000个ajoo way。
现在需求变更了,变更很大导致这些共通模块每个都得修改。
上面的需求变更反复进行了10次。
结果是必须对这些共通模块维护10000次(1000个,每个10次),平均每个人只有1次。
而1000个ajoo way你必须自己维护,同样是10000次,也有可能稍微小一点,因为你能力强,有可能提前预测到了变化,那就算5000次吧。
现在问题是,10000次共通模块的维护大家都能做,10000个人一起上几天就对应完毕,而5000次ajoo way的维护只有你一个人有能力做,需要多少时间才能对应完毕呢?虽然你能力超强,但是你还是拖了项目的后腿了。
另:老板还得额外支付你的1000个ajoo way的制作加上5000次维护的费用,如果你也使用共通模块的话本来也是不必要的。


是的。如果从技术的角度,我倾向于支持楼主,但是如果是实际项目,我认为楼主的坚持没有必要,而且,有些画蛇添足。如果PropFactory已经稳定,且能否满足你的需求的情况下,不宜再使用其它方式。“ClassLoader.loadResourceAsStream几乎是工业界的标准”这个我也没有听说过。

一般所谓的减少依赖主要是针对非常容易变化或者说不能够确定的东西,比如业务逻辑,而对于工具包/工具类,不是非常重要,有时甚至是要增加依赖。
8 楼 抛出异常的爱 2007-02-14  
aninfeel 写道
中国的程序界已经进入“大跃进”时代,“人有多大胆,地有多大产”,能复制就复制,能不写文档就不写文档,能一个月完成就十天完成,谁还管你那么多?

同意,如果这样下去三年自然灾害一来大家都饿死。。
7 楼 hax 2007-02-14  
ImplFactory.newObject这个写法非常难看,特别是要声明参数类型这样变态的事情。与其这样用,我还不如写若干个getXXXInterface。不要说语法糖衣不重要,client code要用到这样恶心的代码,就一肚子火。


Map接口肥大这是一个废话,难道Map接口不是用来干这个事情最好的接口吗?你偏要自己写一个Getter接口?这样接口数量迟早爆炸。。。

我是非常赞同减少依赖的。但是在太多公司,太多人热衷于“复用”自己的所谓内部类库和内部标准。他们的理由表面上是jkit所说的维护成本,其实本质上是为了树立自己的权威地位:诺,看到没有,我的类库是公司这个项目的核心代码……

其实完全站不住脚,难道公司的人总是一成不变的吗?如果真是这样,那为什么不是开发这个遗留系统的人继续维护,还要其他人维护?按照it公司的流动率,新人进来,你说是学习主要依赖标准类库的做法成本低,还是学习所谓内部类库的成本低??
6 楼 aninfeel 2007-02-12  
中国的程序界已经进入“大跃进”时代,“人有多大胆,地有多大产”,能复制就复制,能不写文档就不写文档,能一个月完成就十天完成,谁还管你那么多?
5 楼 hyf 2007-02-12  
jkit 写道
为了让效果更明显,我们来看一下放大的情况
假设team里面有10000个人,系统已经有1000个类似PropFactory的共通模块。而你觉得这些模块用起来都不爽,自己实现了1000个ajoo way。
现在需求变更了,变更很大导致这些共通模块每个都得修改。
上面的需求变更反复进行了10次。
结果是必须对这些共通模块维护10000次(1000个,每个10次),平均每个人只有1次。
而1000个ajoo way你必须自己维护,同样是10000次,也有可能稍微小一点,因为你能力强,有可能提前预测到了变化,那就算5000次吧。
现在问题是,10000次共通模块的维护大家都能做,10000个人一起上几天就对应完毕,而5000次ajoo way的维护只有你一个人有能力做,需要多少时间才能对应完毕呢?虽然你能力超强,但是你还是拖了项目的后腿了。
另:老板还得额外支付你的1000个ajoo way的制作加上5000次维护的费用,如果你也使用共通模块的话本来也是不必要的。


听起来很恐怖,实际上是没有的事情。
4 楼 jianfeng008cn 2007-02-12  
ls的朋友不能一概而论啊 要看是什么样的问题
就我的观点 lz遇到的问题完全应该把减少依赖作为一个很好的理由
况且依赖的东西不仅不标准,而且有潜在的问题
遇到这样的情况 我看lz还是 通过耐心的劝导+自己的不懈努力
最重要的是以实际的成果来提高自己在公司的地位
倒可以说是一个“相对外行”的人管“内行”的问题
有时候被“孤立”并不仅仅是技术的分歧哦
夸张点 没技术就是做你领导那是一种艺术 呵呵
3 楼 jkit 2007-02-12  
在有遗留系统的情况下,除非你有能力将遗留系统全部移植到你的新系统上去,否则,你的新系统无论多么先进都是额外的负担。
2 楼 jkit 2007-02-12  
为了让效果更明显,我们来看一下放大的情况
假设team里面有10000个人,系统已经有1000个类似PropFactory的共通模块。而你觉得这些模块用起来都不爽,自己实现了1000个ajoo way。
现在需求变更了,变更很大导致这些共通模块每个都得修改。
上面的需求变更反复进行了10次。
结果是必须对这些共通模块维护10000次(1000个,每个10次),平均每个人只有1次。
而1000个ajoo way你必须自己维护,同样是10000次,也有可能稍微小一点,因为你能力强,有可能提前预测到了变化,那就算5000次吧。
现在问题是,10000次共通模块的维护大家都能做,10000个人一起上几天就对应完毕,而5000次ajoo way的维护只有你一个人有能力做,需要多少时间才能对应完毕呢?虽然你能力超强,但是你还是拖了项目的后腿了。
另:老板还得额外支付你的1000个ajoo way的制作加上5000次维护的费用,如果你也使用共通模块的话本来也是不必要的。

相关推荐

    软件工程质量

    "#$%&’()”旨在帮助软件开发者、需方和独立评价者更好地理解并应用软件质量评价的原则。它不仅为软件开发者提供了一套完整的指南,也使得管理者、开发者或维护者能够有效地评估软件是否达到了预期的质量要求,并在...

    作为软件工程师,你必须知道的20个常识

    - **观察者模式**:定义了对象之间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 通过学习和应用这些设计模式,可以有效地解决软件设计中的常见问题,提高软件的...

    软件工程术语标准

    - **定义:**整体部分结构是指一个类可以包含其他类的对象作为其组成部分。 **继承:** 继承是指一个类(子类)从另一个类(父类)继承属性和方法的过程。 **服务:** 服务是指对象能够执行的操作或功能,通常...

    Open-2021年春季南开《软件工程与软件测试技术》在线作业标准答案.pdf

    软件设计中的耦合度是一个衡量软件模块间相互依赖程度的重要指标。低耦合度的设计原则有助于提高软件的可维护性和可扩展性。软件模块间联系的简化,降低了系统复杂度,进而减少了潜在的错误传播风险,这体现了软件...

    软件测试标准术语 英文原版

    国际软件测试资格委员会(ISTQB)作为全球范围内软件测试领域最具权威性的机构之一,致力于推动软件测试的标准化和专业化发展。本文将深入解析ISTQB发布的软件测试标准术语2.1版本中的关键知识点,帮助读者更好地...

    软件测试人员调研报告

    - **公司测试人员与开发人员的比例**:这个比例反映了公司在软件开发生命周期中对测试环节的重视程度,也是衡量软件质量的一个重要指标。 - **公司每年对测试人员的培训次数及类型**:培训是提高软件测试人员技能的...

    软件工程试题及答案82085.doc

    8. **软件质量模型**:McCall提出了一个包含11个软件质量特性的模型,这些特性涵盖了可修改性、可理解性、可测试性等多个方面。 9. **软件结构**:软件结构通常基于模块构建,形成一种控制层次结构,有助于组织和...

    软件工程复习题

    1. **模块独立性**:题目中的第一个选择题提到模块独立性是软件开发中的一个重要原则。模块独立性是指每个模块只完成系统要求的独立子功能,并且与其他模块的联系最少且接口简单。通常通过两个标准来衡量:耦合性和...

    嵌入式软件测试技术[QUALITY]

    2. **度量的标准**:不同的度量方法各有侧重,如何选择合适的度量标准也是一个问题。 3. **度量的意义**:度量结果的意义也需要明确,不是所有可度量的指标都有实际意义。 ##### (三)好的度量标准 1. **直观性**...

    精品标准化评分.pdf

    为了满足您的要求,我将尝试基于一个假设的、与“精品标准化评分”主题相关的知识场景来构建知识点,但请注意,这些内容并非根据您提供的文件信息而生成,而是一个独立的示例。 精品标准化评分通常涉及对产品质量的...

    软件开发工具.pdf

    模块凝聚性和耦合性是衡量软件模块质量的重要指标,凝聚性强调模块功能的单一化,耦合性关注模块间的相互依赖。 面向对象编程中的封装性是指隐藏对象的内部细节,只暴露必要的接口;遗传性允许子类继承父类的属性和...

    面向对象软件构造(第二版)1-6章

    本书前六章共同构建了一个面向对象软件开发的综合视角,涵盖了软件质量的理论基础、面向对象编程的核心原则、模块化设计的最佳实践、复用性的实现策略、对象技术的发展历程以及抽象数据类型的应用。Meyer的论述不仅...

    企业级应用软件架构开发过程与实践1

    这些早期程序虽然简单,却标志着软件作为独立概念的起源。 **传统的业务执行过程** 在信息技术广泛应用之前,传统业务的执行过程大多依赖于手工操作。例如,银行业务处理中涉及大量纸质记录,包括账户信息、交易...

    软件工程概念与术语

    - **软件可靠性**指的是软件在规定条件下和时间内完成预定功能的能力,它是衡量软件质量的重要指标之一。 - **软件安全性**关注软件抵御攻击、保护数据和隐私的能力,确保软件在面对恶意行为时仍能正常运行。 - **...

Global site tag (gtag.js) - Google Analytics