【问题标题】:Refactoring many nested ifs or chained if statements重构许多嵌套 if 或链式 if 语句
【发布时间】:2011-02-01 13:23:58
【问题描述】:

我有一个包含大量相似字段(比如超过 10 个)的对象,我必须从可变长度的数组中为它们分配值。解决方案可能是基于每次检查数组长度并分配每个字段的大量嵌套 ifs

一个 ifs 链检查长度是否超出范围,并在检查后每次分配。

两者似乎都是重复的。有更好的解决方案吗?

【问题讨论】:

    标签: refactoring


    【解决方案1】:

    如果你的语言有 switch/case 和 fallthrough,你可以这样做:

    switch(array.length){
        case 15: field14 = array[14];
        case 14: field13 = array[13];
        case 13: field12 = array[12];
        // etc.
        case 1: field0 = array[0];
        case 0: break;
        default: throw Exception("array too long!");
    }
    

    【讨论】:

    • 如果是 case 15,我需要从 15 到 0...
    • @Icarus:由于 switch/case 的失败,当数组长度为 15 时,上面的代码分配所有 15 个字段
    • 我使用的是 c#,但在它中失败了。我必须使用案例来实现这一点。此外,曾尝试使用 var args 但 C# 不允许使用带有 ref 的参数。
    【解决方案2】:
    for (int i = 0; i < fieldCount; i++)
        fields[i].value = array[i];
    

    也就是说,维护一个与你的值数组对应的字段数组。

    【讨论】:

    • 我真的不认为这回答了这个问题。重点是他试图将这些值应用于类/对象的字段。使所有字段具有自己的value 属性的特殊类型并使它们可索引(并且安全)正是他试图解决的问题,只是重新定义为更复杂的东西。
    • 但是如果你已经创建了字段数组——通过任何方式——那么你可以简单地应用它,就像我在上面所做的那样。与在代码中的许多点解决相同的问题相比。做一次使它们可索引和安全的工作,然后使用该产品来简化代码。这就是我要说的。
    • 如果您将其定义为创建索引器属性或类似的特定解决方案,我可能会同意。尽管如此,您建议通过创建另一个数组来解决将数组映射到字段的问题......然后必须将其映射到完全相同的字段。
    • 这里的问题是我没有字段数组。
    【解决方案3】:

    如果您的语言支持委托、匿名函数等,您可以使用它们来清理它。例如,在 C# 中你可以这样写:

    string[] values = GetValues();
    SomeObject result = new SomeObject();
    Apply(values, 0, v => result.ID = v);
    Apply(values, 1, v => result.FirstName = v);
    Apply(values, 2, v => result.LastName = v);
    // etc.
    

    apply 方法如下所示:

    void Apply(string[] values, int index, Action<string> action)
    {
        if (index < values.Length)
            action(values[index]);
    }
    

    这显然取决于语言,但无论如何都需要考虑。


    另一个我们可能会忽略的非常简单的选项是,如果您实际上尝试从这个值数组中初始化一个对象(而不是更新一个现有对象) , 如果数组不够大,只接受默认值。

    C# 示例:

    void CreateMyObject(object[] values)
    {
        MyObject o = new MyObject();
        o.ID = GetValueOrDefault<int>(values, 0);
        o.FirstName = GetValueOrDefault<string>(values, 0);
        o.LastName = GetValueOrDefault<string>(values, 0);
        // etc.
    }
    
    void GetValueOrDefault<T>(object[] values, int index)
    {
        if (index < values.Length)
            return (T)values[index];
        return default(T);
    }
    

    有时愚蠢的解决方案是最明智的选择。

    【讨论】:

    • 这可以通过创建一个代表数组并循环遍历它们来变得更简洁。
    • @Hosam Aly:不是真的,你只需要编写我已经编写的相同代码来创建数组。除非您在多个地方执行此操作,否则不会保存任何内容。
    • 除了用于跟踪索引的变量之外,至少可以省去多次编写函数调用的麻烦。如果更新了对象结构,那么更新它也会更容易,恕我直言。
    • @Hosam Aly:除非此代码在多个地方使用,否则没有任何事情会变得更容易,而问题没有说明。如果只执行一次,或者每个类/数组对执行一次,那么您最终会编写 more 代码来初始化一个不必要的数组。
    【解决方案4】:

    如果您的字段以数组元素的相同顺序声明,您可以使用反射(如果在您的语言中可用)来设置这些值。这是一个如何在 Java 中执行此操作的示例:

    // obj is your object, values is the array of values
    Field[] fields = obj.getClass().getFields();
    for (int i = 0; i < fields.length && i < values.length; ++i) {
        fields[i].set(obj, values[i]);
    }
    

    【讨论】:

    • 这似乎不太可能,即使这是真的,也非常危险。对该类的简单重构会破坏这一点,直到运行时您才会知道;您甚至可能不会遇到运行时错误,您最终只会得到损坏的数据。永远不要依赖类中字段声明的顺序。
    • 我同意这很危险,但如果你有 Python 背景,那么你可能会觉得它可以接受。无论如何,它是一个可以用于善恶的工具。
    • 我不想使用它,因为不确定反射是否会以与类中声明的顺序相同的顺序为我提供字段。总是这样吗?
    • @Icarus:不,文档明确表示无法保证订单。但是,您可以按可预测的顺序对它们进行排序。查看其他问题以获取更多信息:stackoverflow.com/questions/1097807/…
    猜你喜欢
    • 1970-01-01
    • 2020-01-02
    • 1970-01-01
    • 2015-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多