PC的blog

Finding... Thinking... Solving...

BlogJava 首页 新随笔 联系 聚合 管理
  9 Posts :: 0 Stories :: 54 Comments :: 0 Trackbacks
本文紧接使用重构移除丑陋的if else代码(4)

上篇文章谈到如何能够彻底把这个switch也移除掉呢?很简单,我们只需要在getSystemStatePerformer()方法被调用之前先创建所有 performer匿名类的实例,然后在该方法被调用时直接返回对应的实力。 如何具体实现呢? 用Map, 请看代码:

package de.jingge.refactoring;

 

import static de.jingge.refactoring.SystemState.*;

import java.awt.Image;

import java.awt.image.BufferedImage;

import java.lang.reflect.Method;

import java.util.Collections;

import java.util.HashMap;

import java.util.Map;

 

/**

 *

 * 
@author gejing@gmail.com

 
*/

public class SystemStatePerformerFactory {

 

private static SystemStatePerformerFactory INSTANCE = new SystemStatePerformerFactory();

   

    
private Map<SystemState, SystemStatePerformer> performers;

 

    
private SystemStatePerformerFactory() {

}

 

    
public static SystemStatePerformerFactory getInstance() {

        
return INSTANCE;

    }

   

    
private synchronized Map<SystemState, SystemStatePerformer> getPerformers()

            
throws Exception {

        
if (performers == null) {

            performers 
= new HashMap<SystemState, SystemStatePerformer>();

            
// call all @FactoryMethod using reflection

            
for (Method m : getClass().getDeclaredMethods()) {

                
if (m.getAnnotation(FactoryMethod.class!= null) {

                    SystemStatePerformer p 
= (SystemStatePerformer) m.invoke(

                            
thisnew Object[]{});

                    performers.put(p.getState(), p);

                }

            }

            
// make it readonly

            performers 
= Collections.unmodifiableMap(performers);

        }

        
return performers;

    }

 

    
public SystemStatePerformer getSystemStatePerformer(SystemState state) throws Exception{

        
return getPerformers().get(state);

    }

 

@FactoryMethod

    
private SystemStatePerformer createLoggedInPerformer() {

        
return new SystemStatePerformer(LOGGEDIN, getImage("loggedin.gif")) {

 

            @Override

            
public void perform() {

                
// do something after logging in is successful,

                
// for example: show welcome dialog, open the last edit document, etc.

            }

        };

    }

 

@FactoryMethod

    
private SystemStatePerformer createLoggedOutPerformer() {

        
return new SystemStatePerformer(LOGGEDOUT, getImage("loggedout.gif")) {

 

            @Override

            
public void perform() {

                
// do something after logging out is successful,

                
// for example: free used resource, dispose GUI components, etc.            }

            }

        };

    }

 

@FactoryMethod

    
private SystemStatePerformer createIdlePerformer() {

        
return new SystemStatePerformer(IDLE, getImage("idle.gif")) {

 

            @Override

            
public void perform() {

                
// do something after the user is idle,

                
// for example: save the application state temporarily, lock the application, etc.

            }

        };

    }

 

    
private Image getImage(String string) {

        
return new BufferedImage(1010, BufferedImage.TYPE_4BYTE_ABGR);

    }

}

从代码中可以看出,当getPerformers()方法被第一次调用时,我们会为每一个performer匿名类创建一个实例,并且将它们纳入Map的管 理之中,以后每次调用的时候,直接从Map里面提取对应某个状态的performer就可以了, switch可以舍弃了。 @FactoryMethod这个注释是我自己写的,使用它主要是为了避免每次新增加一个create***Performer()方法后,都必须修改 getSystemStatePerformer()。

@FactoryMethod的代码如下:

package de.jingge.refactoring;

 

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 


@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface FactoryMethod {


}

到这里整个重构已经结束了, 我们已经将if else, switch完全从代码里剔除了。

读过Refactoring to Patterns这本书的朋友可能会觉得,这里所作的一些和书中第七章最后一节Replace Conditional Dispatcher with Command完全一样。 Well,第一眼看上去确实很像,但是看完我写的所有代码后,再仔细想一想,两者还是有区别的(Refactoring to Patterns这本书写的非常好,对此书,我可以说是爱不释手,还曾经写过一篇书评。事实上,我这篇文章正式基于这本书的):

1. Factory + annonymous类而不是每一个状态一个具体的实体类。

    这样处理问题, 类的数量大大减少,类关联的复杂程度也大大减少,维护起来很方便。

2. performer并不单单是一个command,它拥有状态,并且可以处理更多的逻辑。


全文完。




声明:本文版权归作者所有,如需转载请注明出处。

posted on 2008-08-04 03:48 polygoncell 阅读(4345) 评论(37)  编辑  收藏

Feedback

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 09:12 HiMagic!
ifelse -> switch -> map
其实代码无所谓丑陋与否,只不过是心理作祟。关键在于怎样最符合业务逻辑,怎样最适应业务场景。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 09:37 kkktop
我们写程序的本质是解决问题,如果就是为了OO而OO,反而陷入了OO的泥潭,如果一个简单的逻辑只是因为用多了ifelse,就觉得不好,不够OO,我想就有点钻牛角尖的味道,而不是code的badsmell了.
本人的感觉(粗略看过)如果只看这个问题,是把简单问题复杂化了.呵呵  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 09:42 kkktop
这就像打蚊子,不能只选用大炮的,只要是合适的武器能解决问题就好.
  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 09:42 LINQ
平时写这些只会让效率更加的低下,业务逻辑封装的时候倒是可以考虑这样的方法,写一个统一调用的接口!就像楼上说的不能为了OO而OO,OO的目的是为了提高效率和减少维护的成本!博主写不的不错,赞一个!  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 09:53 路过
呵呵,一个业务实现的方法是多种多样的,但,当一个业务有简单变的复杂时,回头来看以前写的方法就知道优劣了,平时多锻炼oo也无可厚非,集沙成塔。支持一下。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5)[未登录] 2008-08-04 09:55 Brian
一篇文章就能写清楚的事情,不需要写5篇吧?简单事情复杂化!  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 11:44 霉干菜
这么写了如果以后代码交给别人维护
要交待得太多了把  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 13:25 leekiang
别连放5篇到首页,以免形成视觉污染。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 13:52 zhuxing
如果简单针对楼主的代码,个人觉得这么重构有点过了。

我提出置疑,针对的是:你用工厂方法模式的理由充分吗?

我想强调两点:
1、再仔细揣摩一下你的需求。是不是用个静态方法(creation method)的方式更合理一些?
2、看一下你周围的工作伙伴,这么写会不会增大代码的负责度?(这个问题的标准是有你的同伴来决定,不能自己判断)


最后强调一下:
在学习模式导向重构的时候,看看最基本的重构技巧是否已经熟悉了。我看你那个getPerformers()方法,就有点难受。为什么要把if语句嵌套的那么深呢???

  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 14:41 polygoncell
呵呵,大家的反应很激烈啊!

我这里只是使用一个简单的例子来解释如何使用重构来移除if else,实际应用逻辑当然要复杂很多。

的确有一些程序员觉得一个方法里面使用一大堆if else很方便,其实这只是对他自己方便,别人阅读他的这一大堆if else会很头疼。

我这样重构看似增加了代码量,实则封装了大量的技术细节。

建议大家去读读refactoring to patterns这本书,书中就讲到了一个结对重构(该书的作者和一个程序员)的例子,最开始那个程序员也觉得重构完后,代码量明显增加,他很不爽,但是后来他熟悉了那些模式后才发现他以前的做法是错误的,应该进行这样的重构。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 14:49 ANTI CPC
我支持楼主,他只是用一个简单的例子来说明如何进行重构。对于实际的case,往往复杂的多,一个好的架构开始的时候貌似增加了代码,实际上后面维护起来就舒服很多了,切身体会。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 15:04 badqiu
避免过度设计  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 17:29 Unmi
@Brian
写成五篇,可以投放的广告就能多多了。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 18:04 千里冰封
@Unmi
确实,我开始还以为是系统的广告呢,原来是作者自己加的,有点那个了,我想做俯卧撑了  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 18:33 polygoncell
@千里冰封

呵呵,可别超过3个。 对了,你的那个音乐播放器挺不错的。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 18:34 polygoncell
@Unmi

我也就是这次试验一下这么写,效果不好的话会考虑下次换个方式。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 18:46 polygoncell
@zhuxing

理论上来说,creation method也是可以的,不过这样一来就导致Performer类和过多的其他类产生耦合(因为处理每一个状态需要用到完全不同的类),我用factory就是为了保持performer干净。要是一定要用creation method的话,performer都可以省了,直接写一个复杂的enum,而每一个enum实例正好就是creation method。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 21:13 kkktop
对于这个简单的问题,觉得ifelse加上应有的注释,好过这么大堆复杂的实现,加上注释的好处也是便于维护和后人的理解,并不比这样实现下来差很多  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 22:07 BeanSoft
难道 Map 内部实现不是 if-esle 嘛?

计算原理三要素: 顺序、循环与分支

OO 只不过是重新封装了一把.  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 22:22 polygoncell
@BeanSoft

map没有使用一大团if else,HashMap的代码如下:

public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}

而且Map已经封装好了,对于我们使用者来说是没有if else的。 现在编码强调的是粒度适度,便于测试,便于阅读。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-05 00:24 隔叶黄莺
@polygoncell
一、二、三、四、五连着五篇这么投放广告还能是试验呀,俯卧撑超过三个就会死人的,你这样 5*4=20,哎呀,要快差不多7个人了。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-05 02:21 polygoncell
@隔叶黄莺

这位兄弟挺逗的!呵呵。那个4是怎么得来的?

兄弟没必要这么针对我吧,我没招惹你吧。真的是做个实验,你可以看看我以前的文章。

哦,对了,楼上众位兄弟哪位能够帮我分析一下:我应该有如何处理自己原创的文章的自由吧? 而且看与不看全凭自愿,为什么有些朋友的反应会这么激烈?最好那些反应过激的朋友能站出来说说你们的想法,大家交换一下意见,谢谢。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-05 09:43 zhuxing
# re: 使用重构移除丑陋的if else代码(5) 2008-08-04 18:46 polygoncell
@zhuxing

理论上来说,creation method也是可以的,不过这样一来就导致Performer类和过多的其他类产生耦合(因为处理每一个状态需要用到完全不同的类),我用factory就是为了保持performer干净。要是一定要用creation method的话,performer都可以省了,直接写一个复杂的enum,而每一个enum实例正好就是creation method。


大哥:只能说,俺长见识了! ~_~  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-05 17:03 polygoncell
@zhuxing

我又仔细的考虑了一下,觉得静态方法在这里并不适用,因为我们需要针对不同的状态写出完全不同的逻辑来处理状态。静态方法通常适用于参数实例繁多而逻辑相同的情况。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-05 22:49 onlykeke
其实我觉得 if else 很漂亮。不过我也不否认模式应用的好处。只是不要过度设计。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-06 18:04 zhuxing
@polygoncell
我也只是和你打个比喻

继承封装变化....静态能继承吗?
---------简单工厂(静态工厂方法)和工厂方法的最本质区别

如果你真的有封装变化的需求,那你用工厂方法问题不大。如果现有变化比较少,而且能够预想到的扩展需求不大,就别用工厂方法了...


当然你可能有你特定的需求,而且也没法三言两句说的很清楚。说实在的,你的那个反射...什么什么的... 有点乱~_~


你的代码是在使用工厂方法,但是这个创建过程有点烦琐...不需要搞成这样  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-07 19:59 polygoncell
@zhuxing

那个反射主要是为了分析@FactoryMethod注释,用意很清晰:减少不必要的编码。状态增加后,程序员只需要增加一个带@FactoryMethod的方法就行了。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-08 09:31 zhuxing
@polygoncell
不知道你写了几年代码了
  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-08 15:33 polygoncell
@zhuxing

有些年头了,不过写java代码还没超过10年,你呢?

你觉得这样做繁琐,请问如何做才简单?能不能贴出你认为简单的代码?有时候旁观和自己动手做的感觉是完全不一样的。还有,别忘了要把简单留给使用你代码的人,而把繁琐留给自己。

有msn么?咱俩好好聊聊。:-)  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-08-13 23:33 小高
这个代码比if 更丑陋 ..... 看了本书 就强用书上的东西 .... 很有问题 .......
博主有团队工作的经历吗 ...每个人都这样写(小提大作)...... 项目就真乱了....
自己研究还差不多 ... 我打赌 你要是真的 遇到了 你还是用 if   回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-09-01 12:36 mikelij
这样重构法不具普遍意义. 因为它只适合于enumeration. 如果是复杂条件呢. 我个人认为这是design pattern的错误应用.兄弟你中毒了.  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-09-25 11:08 iridiumcao
5篇有点多,合为一篇看起来比较好。
博主能整理下,把文档和代码打个包,提供下载,更好。~我很懒。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2008-09-25 14:42 iridiumcao
HiMagic!
ifelse -> switch -> map

演了一遍,就是HiMagic总结的过程。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2009-08-25 12:30 zhuxinyu
我也同样认为if .... else .. 过多应该进行重构。 我这里没有那么复杂,仅仅是将
判断的集中状态也写出枚举。 语法通过switch....case.... 去判断。 如果再复杂的话我会写出lz第5片的模式。

第三篇中,我有个疑问public abstract class SystemStatePerformer
这里的SystemStatePerformer是抽象类,lz怎么将其实例化了呢
new SystemStatePerformer(LOGGEDIN, getImage("loggedin.gif"))。 是否应该单独写个类继承此类?  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2009-11-20 23:28 rockallite
@mikelij
“这样重构法不具普遍意义. 因为它只适合于enumeration. 如果是复杂条件呢. 我个人认为这是design pattern的错误应用.兄弟你中毒了. ”

同意 mikelij。

polygoncell 的代码重构,核心就是从 if-else 改到 switch 再改到 Map,其必要条件是:业务判断条件只包含简单的枚举。假设发生变化的恰恰是判断条件呢?polygoncell 的重构把这种判断条件可变的灵活性舍弃了,所以遇到以上假设的情况时,代码就无可避免的需要作大量更改。而用原来的 if-else,代码就很灵活,更改的地方也很少。  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2010-06-30 17:46 virus
@霉干菜
不这么做的代码,如果要交给别人,需要讲更多都不会明白  回复  更多评论
  

# re: 使用重构移除丑陋的if else代码(5) 2012-01-05 11:06 sagt
其实if else的丑陋说白了是不利于变化,难以修改。比如说你的if else里原来有50种情况,现在要增加两种,则你恐怕要去所有if else里面小心的去看,很容易弄出bug来。而用继承和多态的方法只需要在一个地方增加就可以了。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: