【问题标题】:How to refactor this method if interface cannot be changed?如果接口无法更改,如何重构此方法?
【发布时间】:2013-07-05 08:59:41
【问题描述】:

如果无法修改Event接口,如何重构以下方法? PMD 报告太复杂,findbugs 报告 ITC_INHERITANCE_TYPE_CHECKING。还有3、4、5等幻数。

 public int getEventCode(Event event) {
        if (event instanceof OneEvent) {
            return 1;
    }
        if (event instanceof TwoEvent) {
            return 2;
        }
        if (event instanceof ThreeEvent) {
            return 3;
        }
        if (event instanceof FourEvent) {
            return 4;
        }
        if (event instanceof FiveEvent) {
            return 5;
        }
        if (event instanceof SixEvent) {
            return 6;
        }
        if (event instanceof SevenEvent) {
            return 7;
        }
        if (event instanceof EightEvent) {
            return 8;
        }
        if (event instanceof NineEvent) {
            return 9;
        }
        if (event instanceof TenEvent) {
            return 10;
        }
        return event.getClass().hashCode() + 10;
    }

【问题讨论】:

    标签: java refactoring findbugs pmd


    【解决方案1】:

    例如,您可以使用List<Class<?>>

    private static final List<Class<? extends Event>> EVENT_CLASSES
        = Arrays.asList(OneEvent.class, ...);
    

    然后:

    public int getEventCode(final Event event)
    {
        final Class<? extends Event> c = event.getClass();
        final int index = EVENT_CLASSES.indexOf(c);
        return index != -1 ? index + 1 : c.hashCode() + 10;
    }
    

    注意:要求事件属于确切的类,而不是派生的(即OneEvent 而不是OneDerivedEvent)。否则测试会稍微复杂一些,但仍然可行。

    至于:

    查找错误报告 ITC_INHERITANCE_TYPE_CHECKING

    是的,这是因为 instanceof 检查。

    但是:一开始的代码存在一个根本性缺陷。 不能保证.hashCode() 在两个不同的 JVM 执行之间返回相同的值。更重要的是,它允许返回负值。这意味着它可以返回例如 -4 作为值;这意味着“其他事件”将返回 6,因此与 SixEvent 发生冲突。

    考虑重构!

    【讨论】:

    • 谢谢你。你的回答很好,很有帮助。
    • 您好,我收到一条警告“类型安全:为可变参数参数创建了 Class extends Event> 的通用数组”,用于私有静态最终 List> EVENT_CLASSES = Arrays.asList(OneEvent.class, ...);是虚惊一场吧?删除此警告的任何最佳做法?
    • 嗯,是的,这个警告很痛苦......在变量声明之前放一个@SuppressWarnings("unchecked");或者如果您不喜欢它,请使用静态初始化块。
    • 但是同样,由于我提到的.hashCode() 的问题,你应该认真考虑重构......有一天你会有一个非常令人讨厌的惊喜!
    • 感谢 fge,重构正在进行中。
    【解决方案2】:
    public int getEventCode(OneEvent event) {
        return 1;
    }
    public int getEventCode(TwoEvent event) {
        return 2;
    }
    // etc.
    

    这不好 - 但如果您不能更改 Event 类,这可能是满足您要求的最面向对象的方式。这是replacing conditional with polymorphism,无需更改相关类

    【讨论】:

      【解决方案3】:

      代码库中是否存在一些此类类型块?如果是这样,用多态性替换条件http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html 可能会有所帮助。

      【讨论】:

        【解决方案4】:

        好吧,使用instanceof不好,但如果你不能改变事件接口,甚至添加一个“int getType()”或类似的,至少你可以重构你的代码:

        使用“else if”结构而不是just if结构。我建议将继承更深的事件类型放在首位,最后考虑更通用的事件声明(如果您测试事件 instanceof Event,您将始终为真)。

        这样至少可以降低复杂性。

        假设您的所有事件都直接实现事件接口并且没有其他继承问题需要检查:

        public int getEventCode(Event event) {
            int result = event.getClass().hashCode() + 10;
            if (event instanceof OneEvent) {
                result = 1;
            }
            else if (event instanceof TwoEvent) {
                result = 2;
            }
            else if (event instanceof ThreeEvent) {
                result = 3;
            }
            else if (event instanceof FourEvent) {
                result = 4;
            }
            else if (event instanceof FiveEvent) {
                result = 5;
            }
            else if (event instanceof SixEvent) {
                result = 6;
            }
            else if (event instanceof SevenEvent) {
                result = 7;
            }
            else if (event instanceof EightEvent) {
                result = 8;
            }
            else if (event instanceof NineEvent) {
                result = 9;
            }
            else if (event instanceof TenEvent) {
                result = 10;
            }
            return result;
        }
        

        我还更改了您的方法,使其仅声明一个返回变量,这通常被认为是一种更好的编码实践,因为它还有助于降低复杂性,即使在这种特殊情况下它没有真正的技术优势。

        但正如我所说,如果 TenEvent 扩展了 FiveEvent,您也会遇到问题。在这种情况下,我建议使用您收到的事件实例的 Class,如下所示:

        public int getEventCode(Event event) {
            int result = event.getClass().hashCode() + 10;
            String eventClass = event.getClass().getSimpleName();
            if ("OneEvent".equals(eventClass) {
                result = 1;
            }
            else if ("TwoEvent".equals(eventClass)) {
                result = 2;
            }
            return result;
        }
        

        我很懒,只写了示例中的前两个,所以你明白了......

        甚至更好:

        public int getEventCode(Event event) {
            int result = event.getClass().hashCode() + 10;
            Class eventClass = event.getClass();
            if (OneEvent.class.equals(eventClass) {
                result = 1;
            }
            else if (TwoEvent.class.equals(eventClass)) {
                result = 2;
            }
            return result;
        }
        

        【讨论】:

        • 谢谢马丁,但您的解决方案中仍然有很多“if/else”
        • 类对象是单例的:你可以使用==来比较它们,不需要.equals()(是的,这是一个罕见的事件,你可以在Java中与==进行比较!)。
        • @mailzyok,你是对的,但它的圈复杂度将低于唯一的 if 版本,因为所有 if 都是互斥的,从而提高任何代码质量指标......在第一个版本中,他们可能是互斥的,因为您知道它们是互斥的,但是对于代码质量工具,您可能会在这些 if 中组合任何命中,从而增加圈复杂度。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-11
        • 1970-01-01
        • 2015-09-01
        • 2015-03-20
        • 2023-03-19
        • 2014-02-19
        • 1970-01-01
        相关资源
        最近更新 更多