论坛首页 Java企业应用论坛

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

浏览 29666 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2012-10-08  
抛出异常的爱 写道
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方法

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


大十一的写代码?想过劳死么?

1.这代码最大的问题在于contex的管理。。。。
太分散了。
但你引入的东西没有解决这个问题

2.Enum 的生成方式有无数种
if (flow.getTag() == entryType) {
这是最差的一种
create可以写在FLOW中 返回一个 public staitc flow getInstents(int type) 自己实现一下吧

3.操作区分应该放到enum中而不是把不同处抽象后让其它人来实现


把逻辑放到enum里面?java的enum能干的事儿太多了,缺乏简单性。不过套路也不见得错,代码把==改为valueOf立马就解决了。
0 请登录后投票
   发表时间:2012-10-08  
1、我还是停留在java 1.4版本,enum 这个关键字还不知道干嘛的呢。
2、楼主,学习重构是不断实践的,虽然你现在做的不好,也许这是你实践的必经过程,即使你是生搬硬套设计模式也要去试试;
3、你重构的目的是什么?让代码好看?为了方面维护?
4、从你代码来看,这个重构是必要的,也许说不定那天要从某个指定的页面进入呢?
5、你的代码,多了个for循环、还有if语句,这两个都可以省的,上面有人提示,你试试看。
5、关于并发的问题,要看看你的内存(java中的对象)是否同时被多个线程修改!在spring中bean默认是单例的,所以bean的属性不能被多个线程修改,否则就有并发问题。也就是对象的有状态无状态问题了。
0 请登录后投票
   发表时间:2012-10-08  
我觉得这个重构并不是很好,其实只是if的另一种写法而已(哪种更好暂且不论)。如果第一个参数增加了一个值呢?重构后的代码依然要修改,原本if的写法也一样

如果用多态来重构它会更好
0 请登录后投票
   发表时间:2012-10-08  
嗯,这个是不怎么好。一般用多态来代替if。
好坏得先有个标准来判断,你先给个判断标准。
0 请登录后投票
   发表时间:2012-10-08  
devroller2 写道
嗯,这个是不怎么好。一般用多态来代替if。
好坏得先有个标准来判断,你先给个判断标准。


好的标准我认为是以下几点:
1.逻辑上使用较少的if,3个以下;
2.在新增type时,不影响(修改)原有代码;
3.程序可读性强;
0 请登录后投票
   发表时间:2012-10-08  
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方法

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



   用swtich就能解决的问题,为什么非得弄得那么复杂呢?性能比你们这两个都要快啊
0 请登录后投票
   发表时间:2012-10-09   最后修改:2012-10-09
看了大家的回复收获蛮多的。

1.首先在work中的for循环的确是需要抽取到Flow类内部,修改完的方法如下:

public static  Flow getInstance(Integer entryType){
           for (Flow flow : Flow.values()) {
               if (flow .getTag()==entryType) {
                   return flow ;
               }
           }
       }

然后是说到的是怎么获取枚举实例的问题,我这里用的是判断枚举对象中tag的值是否与参数entryType的值相同,相同的话就返回该对象。如果设定的参数是Integer类型的话,我能想到的获取枚举对象的方法最好也就是以上的写法了。(当然你想把==改成equals()也是问题的)thinking in java 中枚举一章有那么一句话"我们只能在enum定义的内部使用其构造器创建enum实例,一旦enum的定义结束,编译器就不允许我们再使用其构造器来创建任何实例了"。倒是如果把entryType改为传入String类型的话,可以使用
Flow.valueOf(entryType);
来获取实例,这样更简洁。

2.还有位朋友说到表驱动,其实策略枚举就是一种表驱动的表现。

3.另外就是Context的管理过于分散的问题,我的想法是可以先把Flow中的handle()方法设计为Result handle()把需要的数据用一个封装的类Result来返回。然后在外层的work方法中再统一用Context的处理。不过这样做的话,会显得更复杂,需要权衡。

4.有些朋友说到该不该重构这个类的问题,我谈谈我自己看法,重构,目的是让程序的层次更加的清晰,让别人或者自己看着更舒服,如果该程序的代码要被复用或者增加修改的概率比较大,那么需要更多的去考虑可扩展性问题。但是有时候凭空想象的话,很难判断重构完之后的代码是否能达到以上的标准。所以不妨先把代码按自己的想法重构了,再和原来的代码对比。即使最后不采用,中间的过程也能促进自己的思考。:)
0 请登录后投票
   发表时间:2012-10-09  
公司给你放假,还不出去玩,闲来无事你咋不去把妹子去呢,活脱一个屌丝
0 请登录后投票
   发表时间:2012-10-09  
夜神月 写道
公司给你放假,还不出去玩,闲来无事你咋不去把妹子去呢,活脱一个屌丝

屌丝的命,没办法。
0 请登录后投票
   发表时间:2012-10-09  
还不如扔个MAP里呢,通过传入不同的KEY,取出不同的处理策略。
0 请登录后投票
论坛首页 Java企业应用版

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