论坛首页 Java企业应用论坛

复杂还是不复杂?

浏览 13873 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-02-07  
ResponseManipulator是原来就存在的,那么为什么会有这个机制呢?你确定你现在的情况不属于ResponseManipulator原来设计要处理的问题吗?
0 请登录后投票
   发表时间:2007-02-07  
BirdGu 写道
ResponseManipulator是原来就存在的,那么为什么会有这个机制呢?你确定你现在的情况不属于ResponseManipulator原来设计要处理的问题吗?

Manipulator在我看来本来就是个过度设计。

它的用法一直都是:
void f(){
  ...
  response = getManipulator().manipulate(response);
  ...
}
ResponseManipulator getManipulator(){
  return new ChainManipulator(new ResponseManipulator[]{
    new Manipulator1(), new Manipulator2()
  });
}


而这个设计相比于下面的代码好处非常有限:
void f(){
  ...
  response = manipulate(response);
  ...
}
Response manipulate(Response response){
  response = Manipulator1.doSomething(response);
  response = Manipulator2.doSomethingElse(response);
}

就为了节省敲“manipulate(response)”在我看来不是一个引入一个接口和ChainManipulator的理由。

0 请登录后投票
   发表时间:2007-02-07  
hermitte 写道
这种情况很经常遇到,一般是根据项目的规模和时间来决定

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

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

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

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

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



不过现在我觉得OO这套有点过时了,有时候OO到成了代码复用的障碍,那个第二个应该用的是IOC的思路在里面,不过问题是由于JAVA接口方面的限制,反而导致耦合的加强,这个应该是JAVA语法的原因,不是编程的原因,毕竟依赖的只是接口而已。


感觉说得很好,这才是真正的理由!
0 请登录后投票
   发表时间:2007-02-08  
The original design is very sound. Don't see this kind of coding often.

The original design fits the open-close principle.

The design is just a template pattern, as those interceptors in web apps, in a chain. It's a post processing of a complex method.

The very naive, and commonly seen,  way to implement is through nested if-else blocks. This completely violates the OO principles.

The root reason is to make maintenance simple. If we started from nested if-else, when we add more if-else blocks, it's just so easy to break existing logic(especially those if-without-else blocks because they have implicit no-code default behaviors). One way to replace them is using interceptors. The interceptors, in a certain degree confine the individual post processing to their own classes so that we can change them independently.

In general, if we have less than 3(the magic number 3) postprocessings, we could hardcode them. But if there are 3 or more similar code patterns then we should definitely avoid it.

The nice thing about this design is that when you add even a simple requirement like this, you touch minimal original code(only the hook portion), plus the new code. A simple requirement is simple, but not 10 simple requirements, not when they interact each other, not when you need to apply them in 10 different places, not when they have heavy dependencies, not when 15 of them combines together.

If you think this is simple, it's because you are benefiting from the sound design. What if you are in a 15-level if-else block and you need to add another if-else there?

Even for a simple salary calculation, there could be many postprocessings, truncate to unit, currency conversion, add bonus, minus stock options, minus health insurrance, pay income tax, etc.

Even we use OO, this is still tough. First, there is an order in the sequence of postprocessings, who does first. For instance, health insurrance should be before income tax because it's "before tax". Then, there could be combination effect, e.g., you can't combine certain set of them. This makes testing very tedious because you need to test all combinations, make sure you get correct results or exceptions

OO is not just a textbook concept, it simplifies coding *and* maintenance. If you think it's overengineering, try inline them to see whether it's good or not. In this case, if you don't follow the original design, then what do you think the guy after you says?

There are several improvements to this design as well. If the creation of these postprocessors is complex, e.g., they have very heavy dependencies, then use a factory dedicated to the creation so that the dependencies won't spread everywhere(Spring is a nice candidate). Furthermore, the interface can be improved too, have two more methods:
initWith(response) - where we get values from response and inject all other dependencies, like get exchange rate for currencies.
isApplicable() - use the above method to determine whether we should apply this processor, based on the values from initWith() and others.

So not only this is a good approach, but we can make it more "OO", not because we like OO, instead because we want to separate things(like dependencies) out.

There are many similar examples in the real world, from web app interceptors for i18n conversion, currency conversion, validation, security, audit, logging, to simple spreadsheets(think about a data matrix as the response here and what we can do for columns and rows, scaling, number crunching, sum and %, etc)

In my experience, I had a case where I had 17 interceptors in 2 years(not knowing all of them upfront), sometimes I had only 10 minutes to add one under heavy pressure. Their combinations are very tricky, the first output(like response) could come from 3 different paths, each of them has heavy dependencies. This design stands well.

When we code, we have to think about how to maintain them. This is not college student homework, which can be thrown away after tests, :-). We may do simple things once or twice, but not more than that.

Just my experience, no point fingers.
0 请登录后投票
   发表时间:2007-02-11  
我还是觉得简单地直接写出来比较好,容易理解,容易调试,效率似乎也更高。

题外话:看到过有一些component的所谓“data-driven”把什么都写到XML里面,最后XML里面可以配置Condition, And, Or, Not,Op,感觉就是在用XML写代码。写起来不方便不说,调试起来更是累人。
0 请登录后投票
   发表时间:2007-02-25  
MyResponse runService()现在的实现逻辑是:
1. 调用Web Service得到MyResponse
2. 对MyResponse做一系列后续处理

如果后续处理是可能发生变化的,那么你pair的方案就灵活了,但不要自己查找ResponseMinipulator,而是DI进来,由组装代码负责这个变化。

如果后续处理是一定的,那么hardcode更直观和简洁。
0 请登录后投票
   发表时间:2007-02-26  
intolong 写道
MyResponse runService()现在的实现逻辑是:
1. 调用Web Service得到MyResponse
2. 对MyResponse做一系列后续处理

如果后续处理是可能发生变化的,那么你pair的方案就灵活了,但不要自己查找ResponseMinipulator,而是DI进来,由组装代码负责这个变化。

如果后续处理是一定的,那么hardcode更直观和简洁。

这话正点。如果manipulator是注射进来的话,我一点意见没有。

问题是绕了半天还要直接new出来,一点灵活性也没得到。这就像要计算1+2+3,不直接写,非要写成一个数组加循环:
int[] arr = {1,2,3};
int sum = 0;
for(int i=0; i<arr.length; i++){
  sum += arr[i];
}


这循环的好处是被计算的数据可以动态决定。可是如果你的数据本身是静态写死的,那么就不如直接1+2+3。
0 请登录后投票
   发表时间:2007-02-26  
这里需要区分是概念和实现。 我们所采取的办法是保持很直接很方便的方式直接调用实现代码,在需要的时候为实现代码增加概念包装类。实际上如果在系统架构上不存在通用的Manipulator的支持(例如动态配置,管理等),采用chain结构等并不是什么好主意。 很多情况下我们需要的是统一实现而不是概念依赖。
0 请登录后投票
   发表时间:2007-02-27  
没有一定之规
0 请登录后投票
论坛首页 Java企业应用版

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