论坛首页 Java企业应用论坛

用enum代替if.这个设计大家怎么看

浏览 29667 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-10-10  
albeter 写道
十一闲来无事到公司改了段代码,大家看看这个修改适合不。首先放出修改前的原始的代码(经过简化,改了名字,名字随便改的),逻辑很简单,就是前端传一个参数进来,后台根据参数的不同进行不同的逻辑处理。
public class HandleSomething  {

	private Manager manager;

	private  Logger subLogger = LoggerFactory.getLogger(this.getClass());

	/**
	 * entryType ,进入的参数,根据该参数来判断逻辑。
	 * 
	 */
	public static final int VIP_ENTRY_FLOW = 1; // 特殊页面进入

	public static final int INDEX_ENTRY_FLOW = 2; // 首页进入

	public static final int OTHERS_ENTRY_FLOW = 3; // 其他页面进来	

    protected void work( int entryType, long userId,Context context)  {

		if (entryType == VIP_ENTRY_FLOW) {
			context.put("type", vipFlow(userId,context));
			return;
		}
		if (entryType == INDEX_ENTRY_FLOW) {
			context.put("type", indexFlow(userId, context));
			return;
		}
		if (entryType == OTHERS_ENTRY_FLOW) {
			context.put("type", othersFlow(userId,context));
		}
	}

   private int vipFlow(long userId, Context context) {/*这里的逻辑用到manager和subLogger*/}
   private int indexFlow(long userId, Context context) {/*这里的逻辑用到manager和subLogger*/}
   private int othersFlow(long userId, Context context) {/*这里的逻辑用到manager和subLogger*/}
}


修改后:
public class HandleSomething  {

	private static Manager manager;

	private static Logger subLogger = LoggerFactory.getLogger(HandleSomething.class);

    protected void work( int entryType, long userId,Context context)  {
        for (Flow flow : Flow.values()) {
            if (flow.getTag() == entryType) {
                context.put("type", flow.handle(userId, context));
                break;
            }
        }
		
	}

  enum Flow{
        VIP_ENTRY_FLOW (1) {// 特殊页面进入
            @Override
            int handle(long userId, Context context) {
               /*这里的逻辑用到manager和subLogger*/
        },

        INDEX_ENTRY_FLOW (2) {// 首页进入
            @Override
            int handle(long userId, Context context) {
               /*这里的逻辑用到manager和subLogger*/
        },
        OTHERS_ENTRY_FLOW (3){ // 其他页面进来	
            @Override
            int handle(long userId, Context context) {
              /*这里的逻辑用到manager和subLogger*/
            }
        };
        private final int tag;

        Flow(Integer tag){
          this.tag=tag;
        }
        public int getTag() {
            return tag;
        }
        
        public abstract int handle(long userId, Context context);
    }
}




修改后通过使用枚举类型间接地去掉了if.

我觉得修改之后有以下特点:
优点:1.execute方法的代码更简洁并且减少重复代码
      2.如果要增加新的处理流程只需要在枚举类中增加一个参数类型即可,不用修改execute方法

但是我有个问题:不清楚这样写是否会带来其他问题。例如性能问题和内存问题。



不懂之一:

int entryType 调用者需要知道具体数字对应的意思``

不懂之二:
WORK除了作了个再分类调用函数啥用处都没有
protected void flow( int entryType, long userId,Context context) 
``共用代码
再if 调用不同代码或者函数

想法之三
如果不考虑性能,获得枚举名称字符串直接反射调用函数,用不着IF

想法之四
WORK里面连续调用三个函数
每个函数里面自己判断是不是自己的类型,也用不着IF
0 请登录后投票
   发表时间:2012-10-10  
模式最重要的一点就是不要为了模式而模式,
我觉得原来的代码的可读性和扩展的问题都不是特别大。
目前的重构确实没有多大的必要。如果到时候真的一下子超过5种情况了再重构也不迟。当让你用了enum我第一想到的是用switch去替换if,结果用了for。。。那还不如原来的。要是真重构,最好还是根据value值来自动生成不同对象。比较合理。
0 请登录后投票
   发表时间:2012-10-10  
albeter 写道
evanzzy 写道
albeter 写道
evanzzy 写道
if else是程序中避免不了的写法,为什么非要去掉呢?而且从理论上讲也去不掉啊。

这个修改我看没什么实际意义,而且策略模式也不是这样用的。真要把if else去掉,还是map来的实惠,和spring搭配,能够直接往map里面注入行为实现类,比这个写法好看得多。


我可没说一定要把if else去掉。并不是说把if去掉代码就多好了,但是有时候if/else过多会影响代码的可读性,也就是在特定的情况下过多的if能反应出一些问题.你说的策略模式不是这样用的,那请问你的意思是不应该使用枚举策略呢?还是在这个情景下不应该这样使用?以及原因?


if else太多了当然不好,不过不是你这种解决办法。你这种办法只不过把if else的形式变了一下,翻译成汇编语言,还是一堆if else。你想想,如果有300个if else,那么你这个办法也要枚举300次,不对的。太多的分支,在java里面,就用map处理比较好,策略的实现类作为value注入到map里面,别说300个,300万个都可以的。

另外恕我技能拙劣,还真不知道有个叫枚举策略的写法,switch case倒是用过。策略模式是行为模式的一种,是以实现接口为基础的,不是你这样用的。

如果我们写程序的时候,第一反应是这个情况我应该用一种什么模式,那么证明你对设计模式还不熟悉,使用的时候就要慎重。


关于枚举策略的写法你可以参照effective java第二版的30条,另外策略模式通俗的理解可以理解为传递方法,接口实现只是实现该模式的一种具体方法,你可以比较下有指针的语言在实现策略模式的不同。另外也可以参照effective java第二版的21条。


Effective java2存在大量蛋疼的例子,在实践中根本遇不到,比如注释里面有\uXXXX会报错。比原来effective C++的水平差多了
0 请登录后投票
   发表时间:2012-10-10  
cpszgy 写道
模式最重要的一点就是不要为了模式而模式,
我觉得原来的代码的可读性和扩展的问题都不是特别大。
目前的重构确实没有多大的必要。如果到时候真的一下子超过5种情况了再重构也不迟。当让你用了enum我第一想到的是用switch去替换if,结果用了for。。。那还不如原来的。要是真重构,最好还是根据value值来自动生成不同对象。比较合理。

为了模式而模式有何不好?没有人凭空想象就能学到东西,这也许是一些人实践模式的学习方法。总之,楼主有想法,敢做,其中的体会只有做了的人才知道。而且比原来的代码要强一些。


不过话说回来,模式不过是实现OO目标的具体方法、途径而已,请抛开模式!设计时心中多想想你的代码如何做到符合OO的目标就可以了,如果达到OO的目标,你的代码自然接近于那些模式。





0 请登录后投票
   发表时间:2012-10-10  
evanzzy 写道
albeter 写道
evanzzy 写道
albeter 写道
evanzzy 写道
if else是程序中避免不了的写法,为什么非要去掉呢?而且从理论上讲也去不掉啊。

这个修改我看没什么实际意义,而且策略模式也不是这样用的。真要把if else去掉,还是map来的实惠,和spring搭配,能够直接往map里面注入行为实现类,比这个写法好看得多。


我可没说一定要把if else去掉。并不是说把if去掉代码就多好了,但是有时候if/else过多会影响代码的可读性,也就是在特定的情况下过多的if能反应出一些问题.你说的策略模式不是这样用的,那请问你的意思是不应该使用枚举策略呢?还是在这个情景下不应该这样使用?以及原因?


if else太多了当然不好,不过不是你这种解决办法。你这种办法只不过把if else的形式变了一下,翻译成汇编语言,还是一堆if else。你想想,如果有300个if else,那么你这个办法也要枚举300次,不对的。太多的分支,在java里面,就用map处理比较好,策略的实现类作为value注入到map里面,别说300个,300万个都可以的。

另外恕我技能拙劣,还真不知道有个叫枚举策略的写法,switch case倒是用过。策略模式是行为模式的一种,是以实现接口为基础的,不是你这样用的。

如果我们写程序的时候,第一反应是这个情况我应该用一种什么模式,那么证明你对设计模式还不熟悉,使用的时候就要慎重。


关于枚举策略的写法你可以参照effective java第二版的30条,另外策略模式通俗的理解可以理解为传递方法,接口实现只是实现该模式的一种具体方法,你可以比较下有指针的语言在实现策略模式的不同。另外也可以参照effective java第二版的21条。


Effective java2存在大量蛋疼的例子,在实践中根本遇不到,比如注释里面有\uXXXX会报错。比原来effective C++的水平差多了


就枚举策略这个用法来说thinking in java 中也有列举。既然两位大师都在自己的书中提到这个设计,我想这个设计会有些值得我们参考的地方。
0 请登录后投票
   发表时间:2012-10-10  
yippees 写道
albeter 写道
十一闲来无事到公司改了段代码,大家看看这个修改适合不。首先放出修改前的原始的代码(经过简化,改了名字,名字随便改的),逻辑很简单,就是前端传一个参数进来,后台根据参数的不同进行不同的逻辑处理。
public class HandleSomething  {

	private Manager manager;

	private  Logger subLogger = LoggerFactory.getLogger(this.getClass());

	/**
	 * entryType ,进入的参数,根据该参数来判断逻辑。
	 * 
	 */
	public static final int VIP_ENTRY_FLOW = 1; // 特殊页面进入

	public static final int INDEX_ENTRY_FLOW = 2; // 首页进入

	public static final int OTHERS_ENTRY_FLOW = 3; // 其他页面进来	

    protected void work( int entryType, long userId,Context context)  {

		if (entryType == VIP_ENTRY_FLOW) {
			context.put("type", vipFlow(userId,context));
			return;
		}
		if (entryType == INDEX_ENTRY_FLOW) {
			context.put("type", indexFlow(userId, context));
			return;
		}
		if (entryType == OTHERS_ENTRY_FLOW) {
			context.put("type", othersFlow(userId,context));
		}
	}

   private int vipFlow(long userId, Context context) {/*这里的逻辑用到manager和subLogger*/}
   private int indexFlow(long userId, Context context) {/*这里的逻辑用到manager和subLogger*/}
   private int othersFlow(long userId, Context context) {/*这里的逻辑用到manager和subLogger*/}
}


修改后:
public class HandleSomething  {

	private static Manager manager;

	private static Logger subLogger = LoggerFactory.getLogger(HandleSomething.class);

    protected void work( int entryType, long userId,Context context)  {
        for (Flow flow : Flow.values()) {
            if (flow.getTag() == entryType) {
                context.put("type", flow.handle(userId, context));
                break;
            }
        }
		
	}

  enum Flow{
        VIP_ENTRY_FLOW (1) {// 特殊页面进入
            @Override
            int handle(long userId, Context context) {
               /*这里的逻辑用到manager和subLogger*/
        },

        INDEX_ENTRY_FLOW (2) {// 首页进入
            @Override
            int handle(long userId, Context context) {
               /*这里的逻辑用到manager和subLogger*/
        },
        OTHERS_ENTRY_FLOW (3){ // 其他页面进来	
            @Override
            int handle(long userId, Context context) {
              /*这里的逻辑用到manager和subLogger*/
            }
        };
        private final int tag;

        Flow(Integer tag){
          this.tag=tag;
        }
        public int getTag() {
            return tag;
        }
        
        public abstract int handle(long userId, Context context);
    }
}




修改后通过使用枚举类型间接地去掉了if.

我觉得修改之后有以下特点:
优点:1.execute方法的代码更简洁并且减少重复代码
      2.如果要增加新的处理流程只需要在枚举类中增加一个参数类型即可,不用修改execute方法

但是我有个问题:不清楚这样写是否会带来其他问题。例如性能问题和内存问题。



不懂之一:

int entryType 调用者需要知道具体数字对应的意思``

不懂之二:
WORK除了作了个再分类调用函数啥用处都没有
protected void flow( int entryType, long userId,Context context) 
``共用代码
再if 调用不同代码或者函数

想法之三
如果不考虑性能,获得枚举名称字符串直接反射调用函数,用不着IF

想法之四
WORK里面连续调用三个函数
每个函数里面自己判断是不是自己的类型,也用不着IF


想法三能不能给出具体的代码?想法四不失为一个设计的方法,但是这样设计感觉方法的责任太大了。每个方法应该只负责单独的业务处理。
0 请登录后投票
   发表时间:2012-10-10  
package com.test;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class Tests {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Flow f=Flow.INDEX_ENTRY_FLOW;
System.out.println(f.toString());
Tests tt=new Tests();
tt.all(f.INDEX_ENTRY_FLOW,"aaaaaaaaa");
tt.all(f.VIP_ENTRY_FLOW,"aaaaaaaaa");
}
enum Flow{ 
        VIP_ENTRY_FLOW, INDEX_ENTRY_FLOW
}
public void all(Flow f,String ss)
{
String s=f.toString();
Class clazz = this.getClass();
        try {
Method m2 = clazz.getDeclaredMethod(s, String.class);
try {
m2.invoke(this, ss);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
private void VIP_ENTRY_FLOW(String s)
{
System.out.println("VIP_ENTRY_FLOW"+s);
}
private void INDEX_ENTRY_FLOW(String s)
{
System.out.println("INDEX_ENTRY_FLOW"+s);
}
}

输出:
INDEX_ENTRY_FLOWaaaaaaaaa
VIP_ENTRY_FLOWaaaaaaaaa


乱写的一个例子 以前C#的时候用过 硬写了下 参考

你的方法本来就在类里面,真单独业务,那调用者直接调用子FLOW函数就行了``
0 请登录后投票
   发表时间:2012-10-10  
devroller2 写道
cpszgy 写道
模式最重要的一点就是不要为了模式而模式,
我觉得原来的代码的可读性和扩展的问题都不是特别大。
目前的重构确实没有多大的必要。如果到时候真的一下子超过5种情况了再重构也不迟。当让你用了enum我第一想到的是用switch去替换if,结果用了for。。。那还不如原来的。要是真重构,最好还是根据value值来自动生成不同对象。比较合理。

为了模式而模式有何不好?没有人凭空想象就能学到东西,这也许是一些人实践模式的学习方法。总之,楼主有想法,敢做,其中的体会只有做了的人才知道。而且比原来的代码要强一些。


不过话说回来,模式不过是实现OO目标的具体方法、途径而已,请抛开模式!设计时心中多想想你的代码如何做到符合OO的目标就可以了,如果达到OO的目标,你的代码自然接近于那些模式。






简单的东西能解决,就用简单的。没必要一个3个if判断就能解决的问题非要用个设计模式。而且用了模式后会会增加类层次和理解的复杂度。再说这里还没到非要用到需要重构的时候。目前这么使用if判断我觉得是丝毫没有问题。
0 请登录后投票
   发表时间:2012-10-10  
cpszgy 写道
devroller2 写道
cpszgy 写道
模式最重要的一点就是不要为了模式而模式,
我觉得原来的代码的可读性和扩展的问题都不是特别大。
目前的重构确实没有多大的必要。如果到时候真的一下子超过5种情况了再重构也不迟。当让你用了enum我第一想到的是用switch去替换if,结果用了for。。。那还不如原来的。要是真重构,最好还是根据value值来自动生成不同对象。比较合理。

为了模式而模式有何不好?没有人凭空想象就能学到东西,这也许是一些人实践模式的学习方法。总之,楼主有想法,敢做,其中的体会只有做了的人才知道。而且比原来的代码要强一些。


不过话说回来,模式不过是实现OO目标的具体方法、途径而已,请抛开模式!设计时心中多想想你的代码如何做到符合OO的目标就可以了,如果达到OO的目标,你的代码自然接近于那些模式。






简单的东西能解决,就用简单的。没必要一个3个if判断就能解决的问题非要用个设计模式。而且用了模式后会会增加类层次和理解的复杂度。再说这里还没到非要用到需要重构的时候。目前这么使用if判断我觉得是丝毫没有问题。


所谓上梁不正下梁歪,不好的东西会传染的,好的东西也会传染。这样的设计如果一个刚接手的人被分配任务在此段代码上处理,多数情况是这样:晕啊,我不熟悉业务,如果我要重构有担心出问题而且工作量大,所以最好的方法再加一个if。

结果if越来越多,我们项目就是这样。
0 请登录后投票
   发表时间:2012-10-10  
devroller2 写道
cpszgy 写道
devroller2 写道
cpszgy 写道
模式最重要的一点就是不要为了模式而模式,
我觉得原来的代码的可读性和扩展的问题都不是特别大。
目前的重构确实没有多大的必要。如果到时候真的一下子超过5种情况了再重构也不迟。当让你用了enum我第一想到的是用switch去替换if,结果用了for。。。那还不如原来的。要是真重构,最好还是根据value值来自动生成不同对象。比较合理。

为了模式而模式有何不好?没有人凭空想象就能学到东西,这也许是一些人实践模式的学习方法。总之,楼主有想法,敢做,其中的体会只有做了的人才知道。而且比原来的代码要强一些。


不过话说回来,模式不过是实现OO目标的具体方法、途径而已,请抛开模式!设计时心中多想想你的代码如何做到符合OO的目标就可以了,如果达到OO的目标,你的代码自然接近于那些模式。






简单的东西能解决,就用简单的。没必要一个3个if判断就能解决的问题非要用个设计模式。而且用了模式后会会增加类层次和理解的复杂度。再说这里还没到非要用到需要重构的时候。目前这么使用if判断我觉得是丝毫没有问题。


所谓上梁不正下梁歪,不好的东西会传染的,好的东西也会传染。这样的设计如果一个刚接手的人被分配任务在此段代码上处理,多数情况是这样:晕啊,我不熟悉业务,如果我要重构有担心出问题而且工作量大,所以最好的方法再加一个if。

结果if越来越多,我们项目就是这样。

我只是就这段代码而言,那如果现实就是这么3种情况呢,以后又不会扩展了呢。那目前所做的工作的实用性有多大呢。所以说未来的情况未来再说,在没必要做修改的情况下做出的修改带来的效果又不是很明显,我觉得实在不必。那后面接手的人不愿意修改难道会怪原来的人写的考虑不周全么。如果当时的情况就只有2到3个if判断,我非弄个3 ,4个类出来,再来个继承什么的。代码是不是写的过于复杂了呢。
0 请登录后投票
论坛首页 Java企业应用版

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