论坛首页 Java企业应用论坛

复杂还是不复杂?

浏览 14252 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-02-04  
OO
问题是这样的。

一个MyService类里面,有一个MyResponse runService()函数。这个runService函数会调用一个web service来得到MyResponse对象。这个MyResponse对象在runService()函数中被缓存,然后返回。

现在的目标是,在runService返回以前,先把MyResponse clone一下,然后如果MyResponse.getCensus().getSalary()返回的带有几毛几分的零钱,就把这个salary truncate成整数。

需求不难,一个直观的解决方案是:

java 代码
 
  1. MyResponse runService() {  
  2.   MyResponse response = call web service;  
  3.   cache response;  
  4.   response = (MyResponse)CloneUtil.cloneSerializable(response);  
  5.   SalaryTruncationUtil.truncateSalaryIfNecessary(response);  
  6.   return response;  
  7. }  


CloneUtil是现成的。只要写一个SalaryTruncationUtil类就算完工了。

可是问题出现在,这个系统存在着一个ResponseManipulator接口(在另外一个package里面)。
ResponseManipulator的接口如下:

java 代码
 
  1. public interface ResponseManipulator {  
  2.   MyResponse manipulate(MyResponse response);  
  3. }  

从签名上,可以看出一个ResponseManipulator负责操作一个MyResponse,然后返回一个新的(或者原来的MyResponse)对象。
同时,另外还现存一个ChainResponseManipulator类,它负责把若干个ResponseManipulator对象串起来:
java 代码
 
  1. public class ChainResponseManipulator implements ResponseManipulator {  
  2.   private ResponseManipulator[] manipulators;  
  3.   public MyResponse manipulate(MyResponse response){  
  4.     for(ResponseManipulator manipulator : manipulators) {  
  5.       response = manipulator.manipulate(response);  
  6.     }  
  7.   }  
  8. }  
以及一个已经写好的DeepCloneManipulator类来负责对一个Response对象的clone。

于是,我的pair决定这样写:
java 代码
 
  1. MyResponse runService() {  
  2.   MyResponse response = call web service;  
  3.   cache response;  
  4.   response = getManipulator().manipulate(response);  
  5. }  
  6. ResponseMinipulator getManipulator() {  
  7.     return new ChainResponseManipulator(new ResponseManipulator[]{  
  8.       new DeepCloneManipulator(),  
  9.       new TruncateSalaryManipulator();  
  10.     });  
  11. }  


当然,还要实现一个TruncateSalaryManipulator。实现起来非常简单,就不写了。

我反对这个设计。虽然两者在代码量上不相伯仲,但是我认为这个设计无谓地增加复杂性,有一种绕着弯子解决简单问题的感觉。一个简单的顺序操作非要用一个特殊的接口和一个特殊的ChainResponseManipulator来实现。
一般来说,用接口是为了得到多态,降低耦合。可是在pair的代码里,该有的依赖一个没少,这个接口就显得意义寥寥。
而且这样做其实增大了MyService的依赖,因为凭空多了对ResponseManipulator和ChainResponseManipulator的依赖。

另外,ChainResponseManipulator对debug也不是非常友好,你单步运行每一个manipulator只能在ChainResponseManipulator的代码中,而不是MyService的代码中。

pair的观点有三:
1。看不出我的方案比用Manipulator有什么简单性的优势。
2。ChainResponseManipulator这一套设施已经存在,又不需要从头写。而且别人都是这么干的。
3。debug可以在每个不同的Manipulator类里面设置断点。


总而言之,没有达成一致意见。因为是pair主导键盘,所以最终pair的方案获胜。



今天在想怎么说服pair的时候,想了这么一个例子:

假设已经有一个StringAppender类:
java 代码
 
  1. class StringAppender {  
  2.   private Object[] objects;  
  3.   public String toString(){  
  4.     StringBuffer buf = new StringBuffer();  
  5.     for(Object obj : objects) {  
  6.       buf.append(obj);  
  7.     }  
  8.     return buf.toString();  
  9.   }  
  10. }  
那么在面对把两个对象连接成一个对象的时候,我们是:obj1.toString()+obj2.toString()呢?还是:new StringAppender(new Object[]{obj1, obj2}).toString()呢?

是不是已经有了的设施,为了保持一致性就必须要使用呢?即使有更简便的方法?


关于这个问题,你怎么看?
   发表时间:2007-02-04  
我觉得两种做法是半斤八两。如果系统没有现成的Manipulator的,那当然用简单的。现在已经有了Manipulator,少用一次多用一次并不改变依赖性。无可无不可的事,当然就是谁主导就听谁的,否则一起共事会很难过的。
0 请登录后投票
   发表时间:2007-02-04  
我同意 pojo 这一观点
不过我觉得从代码的维护角度讲, 简单的那种很容易让人理解.
而采用现成的 Manipulator , 写出来的代码, 让人看起来有点点 "玄".
0 请登录后投票
   发表时间:2007-02-04  
已经有轮子了,嫌老的轮子磨损太严重!那么可以重新造一只!
成本论,如果在赛场上,你领先其他人很多圈,那么有时间造一只全新的,也无可厚非!
但是如果你没时间,还是跑完全程再说吧!

复杂程度来说,前者肯定复杂!
0 请登录后投票
   发表时间:2007-02-04  
如果从编码风格一致性来讲,既然已经有现成的基础设施,那使用既有代码无可厚非,而且从维护者角度考虑,一致的写法更为友好。
0 请登录后投票
   发表时间:2007-02-04  
这种情况很经常遇到,一般是根据项目的规模和时间来决定

刚开始编程的时候,我是吸收的J道那里的banq的思路。
就是在自己的业务层中,只调用更下层的代码,而不是复用已经有了的同层代码。

如果业务简单,就自己写一遍。

如果必须引用已经有了的同层代码,因为度量和减少代码冗余以便维护的缘故,我倾向于用Adapter或者Mediater模式,用接口封装和重构同层的代码。而不是直接引用其它人写的包的东西。

引用之前,把别人的类给包装下先。因为不知道其它人在编写代码的时候,会不会修改已经编写好的类,如果你依赖了他,然而在不知情的情况下,其它人又对你依赖的类进行了修改,就会非常麻烦。所以用结构型设计模式进行下解耦,是面向对象编程的一个基本功。

如果是原来,我倾向你的解决方案。因为那个符合“好莱坞原则”还有Banq的无为的道的思想。那个思想对我影响比较深刻。而且你的代码是高聚合,低耦合的。毕竟复用和耦合,在某种情况下是存在矛盾的。



不过现在我觉得OO这套有点过时了,有时候OO到成了代码复用的障碍,那个第二个应该用的是IOC的思路在里面,不过问题是由于JAVA接口方面的限制,反而导致耦合的加强,这个应该是JAVA语法的原因,不是编程的原因,毕竟依赖的只是接口而已。
0 请登录后投票
   发表时间:2007-02-04  
引用
那么在面对把两个对象连接成一个对象的时候,我们是:obj1.toString()+obj2.toString()呢?还是:new StringAppender(new Object[]{obj1, obj2}).toString()呢


感觉还是用StringAppender好些。
并非是因为有了StringAppender才想到要用这个,而是因为有了
obj1.toString()+obj2.toString()+obj3.toString()+...+objn.toString()

这样的表达式,才会想到要写一个StringAppender类的。
0 请登录后投票
   发表时间:2007-02-05  
光从这里的代码来看,看不出两个方案的优劣来,不过你的pair的方案确实存在“误用”的嫌疑:  代码依赖于ChainResponseManipulator这个类。

但是,如果考虑别的
Service也需要你写的 功能的话,别人可能会这样调用:

  1. MyResponse runService() {  
  2.   MyResponse response = call web service;  
  3.   cache response;  
  4.   response = ManupilatorFactory.getManupulate(ResponseManipulator .A,ResponseManipulator .B) .manipulate(response);  


这样的话,可能会发挥一点 ResponseManipulator 的作用。

不过光从代码来看,可能也不会比你的方法少到哪里去。

一种比较实在的写法可以这样:



  1. MyResponse runService() {  
  2.   MyResponse response = call web service;  
  3.   cache response; 
  4. //first ly
  5.   response = ManupuateSupport.manipuate1(response);
          //secondly
  1.  response = ManupuateSupport.manipuate2(response);
  2.   
  3. }

这样的写法一样可以达到代码复用的好处。

上面一种写法,把这种Response manipulate的设计上升到一个架构的高度,但是后一种是比较聪明的实用的角度。

两者,放在不同的环境里面,或许有各自的用处把。
0 请登录后投票
   发表时间:2007-02-05  

firebody 写道:
光从这里的代码来看,看不出两个方案的优劣来,不过你的pair的方案确实存在“误用”的嫌疑:  代码依赖于ChainResponseManipulator这个类。

但是,如果考虑别的
Service也需要你写的 功能的话,别人可能会这样调用:

  1. MyResponse runService() {  
  2.   MyResponse response = call web service;  
  3.   cache response;  
  4.   response = ManupilatorFactory.getManupulate(ResponseManipulator .A,ResponseManipulator .B) .manipulate(response);  


这样的话,可能会发挥一点 ResponseManipulator 的作用。

不过光从代码来看,可能也不会比你的方法少到哪里去。

一种比较实在的写法可以这样:



  1. MyResponse runService() {  
  2.   MyResponse response = call web service;  
  3.   cache response; 
  4. //first ly
  5.   response = ManupuateSupport.manipuate1(response);
          //secondly
  1.  response = ManupuateSupport.manipuate2(response);
  2.   
  3. }

这样的写法一样可以达到代码复用的好处。

上面一种写法,把这种Response manipulate的设计上升到一个架构的高度,但是后一种是比较聪明的实用的角度。

两者,放在不同的环境里面,或许有各自的用处把。





我选择第一种,

第一种也可以有灵活度,

如: ManupuateSupport.add(MyResponse...);

ManupuateSupport.addAndManipulate(MyResponse...);

还可以有batch或readConfig等等

0 请登录后投票
   发表时间:2007-02-07  
灵活度看怎么说了。

就在我们要完成这个功能的时候,我忽然想到:如果不需要truncate,那么根本就没必要clone。

跟同事一说,他愣了半天。因为要实现这个功能ChainManipulator就不好使了。最后只好说,先这么放着,如果真有效率问题再说。

其实这个功能如果不用Manipulator,非常简单。不过就是:
if(needsTruncation(response)){
  response = CloneUtils.cloneSerializable(response);
  truncateSalary(response);
}


Manipulator不过就是所谓的“高阶逻辑”。不过高阶逻辑虽然有抽象上的优势,也有不够直观,难于debug的问题。面对这么一个简单的需求,何必大炮打蚊子呢?(虽然,据说大炮和炮弹有现成的,不用额外买)

话说轮子,我感觉这个问题就是,本来要去隔壁,迈腿就到的,何必非要弄四个轮子开车?即使轮子白给,这车白开,不用你花钱加油。
0 请登录后投票
论坛首页 Java企业应用版

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