Reputation:
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
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