【问题标题】:Naming convention of mutable/immutable methods可变/不可变方法的命名约定
【发布时间】:2018-06-12 13:40:47
【问题描述】:

当您有两个通用名称方法执行相同的操作,而一个是不可变而另一个是可变时,命名约定是什么>?

示例

考虑接受 2 个数字数组的单元格操作:不可变版本创建一个新数组来放入结果,而可变版本将结果存储在第一个数组参数中。

由于方法名称是通用名称,例如apply,因为操作策略(add,sub,mul,div)由第三个参数指定,因此您不能使用add之类的常用词来实现可变性(a = a + 1 ) 和 plus 表示不变性 (c = a + b)。

代码(将被跳过)

以下细节可能是题外话,但为了说明我在说什么方法:

@FunctionalInterface 
public interface BinaryOperation { double apply(double a, double b); }

public final class BinaryOperations {
    private BinaryOperations() {}

    public static final BinaryOperation ADD = (a, b) -> a + b;
    public static final BinaryOperation SUB = (a, b) -> a - b;
    public static final BinaryOperation MUL = (a, b) -> a * b;
    public static final BinaryOperation DIV = (a, b) -> a / b;
}

public final class DoubleArraysMath {
    private DoubleArrayMath() {}

    // ---> METHOD NAME OF INTEREST
    public static void applyAsMutable(double[] a, double[] b, BinaryOperation o) {
        apply(a, a, b, o);
    }
    // ---> METHOD NAME OF INTEREST
    public static double[] applyAsImmutable(double[] a, double[] b, BinaryOperation o) {
        double[] c = new double[a.length];
        return apply(c, a, b, o);
        return c;
    }
    private static void apply(double[] result, double[] a, double[] b, BinaryOperation o) {
        for (int i = 0; i < a.length; i++) { result[i] = o.apply(a[i], b[i]); }
    }
}

// just an example of usage
double[] r = DoubleArraysMath.applyAsImmutable(a, b, BinaryOperations.MUL);
DoubleArraysMath.applyAsMutable(a, b, BinaryOperations.ADD);
DoubleArraysMath.applyAsMutable(a, b, (ai, bi) -> ai*ai + bi); // some custom operation

单独的类

DoubleArraysMutableMathDoubleArraysImmutableMath 类中分隔可变和不可变方法可避免在每个方法名称的开头/结尾写入“可变/不可变”前缀/后缀。按照这种模式,您最终会得到任何名为“可变/不可变”的实用程序类(它是否好用,我将作为一个悬而未决的问题留下)。

单类问题

如果我们想在单个类中拥有这些方法(更好的维护),正确的命名“模式”是什么?我在代码示例中使用的模式“asMutable/asImmutable”或通常的“可变/不可变”可能与较长的方法名称不兼容。还有其他选择吗?

【问题讨论】:

    标签: language-agnostic naming-conventions immutability


    【解决方案1】:

    根据 cmets 进行编辑

    您绝对应该分别实现可变类和不可变类。方法名可以相似也可以不同,没关系,反正接口不同。

    单课

    可变性策略可以作为方法的附加参数提及,例如:

    apply(a,b,Operation.ADD, ResultValue.NEW)
    apply(a,b,Operation.ADD, ResultValue.FIRST_ARG)
    apply(a,b,Operation.ADD, ResultValue.SECOND_ARG)
    

    但是,在单个方法中使用多种策略会使该方法使客户感到困惑并且容易出错。

    如果方法的签名是

    double [] apply(double[] arg1, double[] arg2, BinaryOperation)
    

    那么可变性或不变性可以是 BinaryOperation 本身的一部分:

    public class FirstArgMutablePlusOperation {
        double[] apply(double[] arg1, double[] arg2) {
             //sample mutation
             arg1[0] = arg1[0] + arg2[0];
             // still return arg1 as a result
             return arg1;
        }
    }
    
    public class SecondArgMutablePlusOperation {
        double[] apply(double[] arg1, double[] arg2) {
             //sample mutation
             arg2[0] = arg1[0] + arg2[0];
             // still return arg2 as a result
             return arg2;
        }
    }
    
    public class ImmutablePlusOperation {
        double[] apply(double[] arg1, double[] arg2) {
             //sample mutation
             double[] result = new double[arg1.length];
             result[0] = arg1[0] + arg2[0];
             return result;
        }
    }
    

    然后用户可以使用正确的策略调用 apply 方法:

    apply(arg1, arg2, new FirstArgMutablePlusOperation());
    apply(arg1, arg2, new SecondArgMutablePlusOperation());
    double[] result = apply(arg1, arg2, new ImmutablePlusOperation());
    

    不可变/可变策略可以是 BinaryOperation 的一部分。 但是,我宁愿避免这种解决方案,因为它会引入 if 语句和庞大的实现:

    public enum ResultStrategy 
    { RESULT, FIRST_ARG, SECOND_ARG };
    
    public class PlusOperation extends BinaryOperation {
            private final ResultStrategy strategy;
            public PlusOperation(final ResultStrategy strategy) {
                  this.strategy = strategy
            }
            double[] apply(double[] arg1, double[] arg2) {
                 if(strategy == ResultStrategy.FIRST_ARG) {
                      //sample mutation
                      arg1[0] = arg1[0] + arg2[0];
                      // still return arg1 as a result
                      return arg1;
                 } else if(strategy == ResultStrategy.SECOND_ARG) {
                      //sample mutation
                      arg2[0] = arg1[0] + arg2[0];
                      // still return arg2 as a result
                      return arg2;
                 } else if(strategy == ResultStrategy.RESULT) {
                      double[] result = new double[arg1.length];
                      result[0] = arg1[0] + arg2[0];
                      return result;
                 }
            }
        }
    

    用法:

    apply(arg1, arg2, new PlusOperation(ResultStrategy.FIRST_ARG));
    apply(arg1, arg2, new PlusOperation(ResultStrategy.SECOND_ARG));
    double[] result = apply(arg1, arg2, new PlusOperation(ResultStrategy.RESULT));
    

    更新

    根据问题提供的示例代码

    public enum ResultStrategy { FIRST_ARG, NEW_RESULT;} // SECOND_ARG = apply(b, a)
    
    public class DoubleArraysMath {
        ...
        public static double[] apply(ResultStrategy rs, double[] a, double[] b, BinaryOperation o) {
            if (rs == ResultStrategy.FIRST_ARG) { return apply(a, a, b, o); }
            return apply(new double[a.length], a, b, o);
        }
    }
    

    【讨论】:

    • 我已经编辑了我的问题以提供更多详细信息,为什么我想避免使用“单独的班级”解决方案。
    • 您可以添加第四个参数来定义可变性策略,例如:plus(a,b,Operation.ADD, ResultValue.NEW) 和 plus(a,b,Operation.ADD, ResultValue. FIRST_ARG) 和 plus(a,b,Operation.ADD, ResultValue.SECOND_ARG)。但是,我仍然会创建具有可变和不可变实现的单独类。
    • 对同一方法采用不同的策略会使用户感到困惑。通常,直接的方法(内部没有 if 语句)是可取的。
    • 运算策略实际上是接口,对两个数进行二进制运算,所以方法是这样的:apply(double[], double[], BinaryOperation)。我认为您将可变性作为论点的建议值得在您的答案中提及(如果您更新答案,我会给您+1)。但如果可能的话,我仍然在寻找一些命名约定。
    • 对不起,我在处理答案时错过了您对方法名称的更正。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多