user4413257
user4413257

Reputation:

Naming convention of mutable/immutable methods

What is the naming convention when you have two generic-name methods performing the same operation, while one is immutable and the other is mutable?

EXAMPLE

Consider cellwise operation accepting 2 arrays of numbers: immutable version creates a new array to put the result in, while the mutable version stores the result in first array parameter.

Since the method name is something generic like apply as operation strategy (add, sub, mul, div) is specified by third argument, you can not use common words like add for mutability (a = a + 1) and plus for immutability (c = a + b).

CODE (TO BE SKIPPED)

Following details might be offtopic, but for illustration about what method I'm talking about:

@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

SEPARATE CLASSES

Having mutable and immutable methods separated in DoubleArraysMutableMath and DoubleArraysImmutableMath classes avoids writting of "mutable/immutable" prefix/suffix at the begining/end of each method name. Following this pattern, you will end up with any utility class named as "mutable/immutable" (whether it is good or not I will leave as an open question).

SINGLE CLASS PROBLEM

In case we want to have these methods in single class (better maintenance), what is the proper naming "pattern"? Pattern I have used in my code sample "asMutable/asImmutable" or generally "mutable/immutable" might be incompatible with longer method names. Is there any other options?

Upvotes: 1

Views: 989

Answers (1)

Pavel Molchanov
Pavel Molchanov

Reputation: 2409

Edit based on the comments

You should definitely implement mutable and immutable classes separately. Method names can be similar or different, it doesn't matter as interfaces will be different anyway.

Single class

Mutability strategy can be mentioned as an additional argument of the method, for example:

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

However, using multiple strategies in a single method will make the method confusing for the clients and error-prone.

If the signature of the method is

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

then mutability or immutability can be part of the BinaryOperation itself:

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;
    }
}

Then a user can call apply method with correct strategy:

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

Immutable/mutable strategy can be a part of the BinaryOperation. However, I rather avoid this solution as it will introduce if statements and a bulky implementation:

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;
             }
        }
    }

Usage:

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));

UPDATE

According to sample code provided in question

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);
    }
}

Upvotes: 0

Related Questions