【问题标题】:Geting rid of switch-case block摆脱 switch-case 块
【发布时间】:2017-12-11 18:42:15
【问题描述】:

如何将这个 switch-case 块转换成好的代码?

private static void agentFieldContructor(Agent agent, String nodeName, String value) {

    switch (nodeName) {

        case "Description":
            agent.setDescription(value);
            break;

        case "Model":
            agent.setModel(value);
            break;

        (... +18)

    }

}

agent 是一个对象,我根据指定的nodeNamevalue 参数填充它。
每个 nodeName 都引用一个不同的代理属性,但我像字符串一样接收它,我无法更改它。我搜索了一些设计模式,但找不到任何可以帮助我的东西。

【问题讨论】:

  • 是什么让您认为 switch...case 不是好代码?您必须使用您所拥有的,如果您将其作为字符串接收,则 switch...case 与任何方法一样有效。
  • 您的开关不一定是坏的......但是,请查看 BeanUtils。 commons.apache.org/proper/commons-beanutils/apidocs/org/apache/…
  • 我在面向对象语言中学到的是:如果你有一个巨大的 if-else 块或 switch-case,那么你就做错了。
  • 明确一点:您正试图通过value 设置代理实例的属性?
  • 切换大小写本身不一定是坏代码。在这种特殊情况下,您应该在 Agent 类中编写适当的构造函数,或者要求直接调用 setter 而不是通过此方法。

标签: java if-statement design-patterns switch-statement object-oriented-analysis


【解决方案1】:

实现此目的的一种紧凑方法是通过反射。您可以访问 setter 方法并使用您的值调用它。

public static void agentFieldConstructor(Agent agent, String nodeName, String value) {
    try {
        agent.getClass().getDeclaredMethod("set" + nodeName, value.getClass()).invoke(agent, value);
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
        e.printStackTrace();
    }
}

请注意,您将不得不处理:

  • 如果他们插入无效的节点名称会发生​​什么
  • 如果 value 是意外值(如 null)会发生什么
  • 处理混合大小写的节点名称(现在,如果第一个字母大写,则可以使用)
  • 处理非标准节点名称(这将假定大部分 JavaBean 命名约定)

...所有这些都是读者的练习。

【讨论】:

  • 是的,这是对 OP 问题的回答。但与 switch-construct 相比:它慢得多,可读性差,它将一些错误检查从编译时转移到运行时,像 Eclipse 的“调用层次结构”这样的 IDE 工具看不到例如的隐式调用。 setModel()。所以我建议尽可能远离反射。
  • @MarcusRigonati 请注意,尽管这可行,但这并不是反射的预期用途。您诉诸于此的事实意味着 Agent 的原始设计和使用它的代码存在问题。
  • @Code-Apprentice:考虑到反射的预期用途是允许这样的元编程,我不确定我是否同意你的评估。我不同意它很慢而且这完全是非标准的,但这确实是一个有效的反射用例。
  • @RalfKleberhoff:OP 发现自己处于无法完全重新构建该对象的构建方式的位置。 会推荐什么其他方法?
  • @Makoto 如果没有更多信息,我无法给出其他建议。特别是,了解nodeName 的来源会很有帮助。
【解决方案2】:

不使用 switch-case 或反射的另一种可能性是使用复杂的枚举。这里的枚举是 translators 从 String 键到 setter 调用:

public enum AgentFields {
    DESCRIPTION {
        @Override
        public void setInAgent(Agent agent, String value) {
            agent.setDescription(value);
        }
    },

    MODEL {
        @Override
        public void setInAgent(Agent agent, String value) {
            agent.setModel(value);
        }
    };

    public abstract void setInAgent(Agent agent, String value);

    // Call this method to set a named field's value
    public static void agentFieldSetter(Agent agent, String nodeName, String value) {
        AgentFields.valueOf(nodeName.toUpperCase()).setInAgent(agent, value);
    }

}

这声明了一个枚举,每个字段都有一个“值”,每个值都带有一个单独的设置器。调度是通过所有枚举自动提供的 valueOf() 方法完成的。我用 toUpperCase 修饰它以允许通常的大写枚举值约定,并使其不区分大小写。

【讨论】:

  • 我确实考虑过使用枚举类,但我遇到了一个困难:我不知道如何使用。我试图学习,但看起来很复杂,所以我现在放弃了。无论如何,我正在处理 String,如果我的 String 是“Model”,我将调用 setModel(),所以 Enum 可能没有多大帮助。
  • 现在我明白了,它真的可以帮到我,因为其他解决方案就像一个 jerry-rig(我不知道如何用英语说“gambiarra”)。如果有帮助,我会在这里进行测试并回来!谢谢。
【解决方案3】:

如果你被允许使用反射。

private static void agentFieldContructor(Agent agent, String nodeName, String value) {
    Method setMethod =  Agent.class.getMethod("set"+nodeName,String.class)
    setMethod.invoke(Agent,value)
}

【讨论】:

    【解决方案4】:

    这里的问题不在于 switch-case 语句本身。相反,它与 Agent 类本身的设计有关。首先,Agent 应该有一个适当的构造函数,您可以在其中传递属性值。如果这些值可以改变,那么 Agent 的用户应该直接调用 setter,而不是使用这个辅助函数。

    更重要的是,具有 20 个属性的类很可能是重构的候选对象。尝试找出这些属性的子集之间的关系,这些子集可以分解为不同的类。

    【讨论】:

    • 构造函数可能是一个不错的选择,如果我可以一次获得每个值,但现在我一次只能获得一个。
    • 代理类是用来保存在数据库中的,所以我不能重构这个。
    • "但现在我一次只得到一个" 然后直接调用设置器。我认为您在原始问题中发布的方法没有充分的理由。当然,我的答案和 cmets 仅基于您在此处发布的内容。我不知道nodeName 来自哪里,所以使用前面建议的反射可能是更好的解决方案。
    • 是的,我在调用 setter,我的逻辑是:如果 nodeNade 是“Model”,那么调用 setModel。但是我有 20 个属性,所以我在 20 个案例中执行此操作。现在我正在使用反射,我不知道它,它有帮助! :)
    • @MarcusRigonati nodeName 来自哪里?
    【解决方案5】:

    我完全同意 cmets。但是,如果您真的不想使用 switch case 并且您的方法名称和 case 字符串几乎相同,则可以使用反射。 Here 是可以帮助您的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-15
      • 2016-11-23
      • 2012-12-26
      相关资源
      最近更新 更多