I am attempting to reduce code duplication in my java code. I am not a java expert, so I might be missing something basic. The goal is to operate on a list via several operators, foo1, foo2, ..., fooN
.
Each operation is iterated until the list stabilizes (no longer changes by the operation). Each operation consists of a forward pass, and then a backwards pass. The passes are the same, just the list is reversed. Each pass is also iterated until the list stabilizes. It's important that the operators are applied sequentially, resulting in frustrating code duplication.
Below is some pseudo-java showing how it is currently done. Note: the foo
operations here attempt to modify list arg1
; they return true if (and only if) a modification to arg1
occurred.
reduceByFoo1(arg1,arg2,arg3){
doUpdate = true;
while(doUpdate) {
doUpdate = false;
doPass = true;
while(doPass) {
doPass = foo1(arg1,arg2,arg3);
doUpdate |= doPass;
}
Collections.reverse(arg1);
doPass = true;
while(doPass) {
doPass = foo1(arg1,arg2,arg3);
doUpdate |= doPass;
}
Collections.reverse(arg1);
}
}
reduceByFoo2(arg1,arg2,arg3){
...same code as above, but with foo2..
}
reduceByFoo2(arg1,arg2,arg3){
...same code as above, but with foo3..
}
...and so on...
What might be a good way to reduce duplication or improve the design pattern?
EDIT: Using the accepted answer, this is roughly the code I ended up with. Much better now:
interface Foo {boolean bar(arg1, arg2, arg3);}
fooUntilStable(foo, arg1, arg2, arg3) = {
iterate = true;
updated = false;
while(iterate) {
iterate = foo.bar(arg1, arg2, arg3);
updated |= iterate;
}
return updated;
}
biFooUntilStable(foo, arg1, arg2, arg3){
Foo biFoo = (a1, a2, a3) -> {
updated = fooUntilStable(foo, a1, a2, a3);
Collections.reverse(a1);
updated |= fooUntilStable(foo, a1, a2, a3);
Collections.reverse(a1);
return updated;
}
fooUntilStable(biFoo,arg1,arg2,arg3);
}
Foo foo1 = (arg1,arg2,arg3) -> {...}
Foo foo2 = (arg1,arg2,arg3) -> {...}
...
Foo fooN = (arg1,arg2,arg3) -> {...}
biFooUntilStable(foo1,arg1,arg2,arg3);
biFooUntilStable(foo2,arg1,arg2,arg3);
...
biFooUntilStable(fooN,arg1,arg2,arg3);
The way I handle functions that do the same thing, but slightly different, is like this:
reduceByFoo1(arg1,arg2,arg3){
// do common operation
foo1(arg1, arg2, arg3);
// do stuff
}
reduceByFoo2(arg1,arg2,arg3){
// do common operation
foo2(arg1, arg2, arg3);
// do stuff
}
In other programming languages, we can pass functions as arguments, but Java is slightly different. Check out this post for passing methods as parameters.
But what I would do is this:
public void reduce(arg1, arg2, arg3, someFunc) {
// do common operation
someFunc(arg1, arg2, arg3);
// do common operation
}
based on the info from the mentioned SO post.
The, you can just call reduce with the specified function like:
reduceByFoo1(arg1,arg2,arg3){
// do common operation
reduce(arg1, arg2, arg3, foo1);
// do stuff
}
reduceByFoo2(arg1,arg2,arg3){
// do common operation
reduce(arg1, arg2, arg3, foo2);
// do stuff
}
Edited to add: This other post for other ideas that may fit a little better into your request.