13

Problem Description: I want to be able to pass around a list of methods to other classes where the methods have been defined in only one class. If the methods, some of which have input parameters and non-void return types, are defined in one class, I want to be able to pass a list of some of them, with possible duplicates, as a parameter to some other class's constructor.

Code Description: The code below is a crude example and can be ignored if it detracts from the main goal. Another example, in addition to the one below, would be a case where the methods are int Add(int n1, int n2), int Subtract(int n1, int n2), Multiply, etc.. and the interface has a method called int MathOperation(int n1, int n2).

Attempt to solve the problem: The adapter pattern seems to have the functionality I'm looking for but I have only seen examples where the methods in the interface have no input or output parameters. An example implementation I wrote just for this question is posted below.

Problem Analogy: You have a random picture generator web service. There are 30 mutations that can be applied to an image. The client connects and clicks a "generate" button and a random list of some of those functions are passed to some other class within the web service which then proceeds to run those functions with it's own data while also collecting and possibly re-using the return values to generate some mutated cat image. It can't just explicitly call the methods in the other class because that process needs to be done randomly at run-time. That is why I lean towards the idea of generating a random list of methods which are executed in-order when the 'generate' button is clicked.

I hope I have been clear.

public class SomeClass {
    ...
    public double UseWrench(double torque, boolean clockwise) { ... }
    public double UsePliers(double torque, boolean clockwise) { ... }
    public double UseScrewDriver(double torque, boolean clockwise) { ... }
    public boolean UseWireCutters(double torque) { ... }

    interface IToolActions {
        double TurnFastener(double torque, boolean clockwise);
        boolean CutWire(double torque);
    }

    private IToolActions[] toolActions = new IToolActions[] {
        new IToolActions() { public double TurnFastener(double torque, boolean clockwise) { double UseWrench(double torque, boolean clockwise); } },
        new IToolActions() { public double TurnFastener(double torque, boolean clockwise) { double UsePliers(double torque, boolean clockwise); } },
        new IToolActions() { public double TurnFastener(double torque, boolean clockwise) { double UseScrewDriver(double torque, boolean clockwise); } },
        new IToolActions() { public boolean CutWire(double torque) { boolean UseWireCutters(double torque); } },
    };
}

public class Worker<T> {

    public List<? extends IToolActions> toolActions;

    public Worker(List<? extends IToolActions> initialToolSet){
        toolActions = initialToolActions;
    }
}
Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
John
  • 775
  • 3
  • 11
  • 25
  • I think I see what you are after, but I'm going to need a better problem description or a better example. The hold-up seems to be the method parameters and the return value. You will have to define some way to deal with those, and your particular requirements will dictate how that needs to work. There's no generic "X" always solves this sort of problem. Maybe explain what problem you are having selecting the parameters themselves, that might be a start. – markspace May 04 '15 at 05:53
  • How about using `List`? –  May 04 '15 at 06:08
  • If you want it fast and dynamic, you can use Reflection and just list all Methods in the class, get their parameters and fill them in a loop. But this won't be as clean as the nice interface answers below – Falco May 04 '15 at 09:02
  • 1
    This problem cries "Command pattern" more than Adapter, at least when I read it. The random list of methods is just a collection of randomly chosen command objects (think of a macro of randomly generated photoshop operations). – Fuhrmanator May 05 '15 at 11:27

3 Answers3

8

While @alainlompo has the general idea, Java 8 simplifies this greatly by using something such as BiConsumer (for doubles) or even just a Consumer for the class object. In fact, you can go really crazy, and have a method accept varargs lambdas:

public class SomeClass

    public double useWrench(double torque, boolean clockwise) { ... }
    public double usePliers(double torque, boolean clockwise) { ... }
    public double useScrewDriver(double torque, boolean clockwise) { ... }
    public boolean useWireCutters(double torque) { ... }

}

public class Worker {

    @SafeVarargs
    public Worker(SomeClass example, Consumer<? extends SomeClass>... operations) {
        for (Consumer bc : operations) {
            bc.accept(example);
        }
    }
}

Then, this is easily simplified:

SomeClass c = new SomeClass();
new Worker(c, SomeClass::useWrench, SomeClass:usePliers, SomeClass::useScrewDriver, SomeClass::useWireCutters);

While it seems a little awkward applying it like that (due to it being an Adapter pattern), you can easily see how this could apply to a class body:

public class SomeClass

    public double useWrench(double torque, boolean clockwise) { ... }
    public double usePliers(double torque, boolean clockwise) { ... }
    public double useScrewDriver(double torque, boolean clockwise) { ... }
    public boolean useWireCutters(double torque) { ... }

    @SafeVarargs
    public void operate(Consumer<? extends SomeClass>... operations) {
        for (Consumer<? extends SomeClass> bc : operations) {
            bc.accept(example);
        }
    }

}

//Elsewheres
SomeClass c = new SomeClass();
c.operate(SomeClass::useWrench, SomeClass:usePliers, SomeClass::useScrewDriver, SomeClass::useWireCutters);

Of course, you don't need varargs, it will work just as well simply passing a Collection

But wait there's more!!!

If you wanted a result, you can even use a self-returning method via a Function, e.g.:

public class SomeClass {

    public double chanceOfSuccess(Function<? super SomeClass, ? extends Double> modifier) {
        double back = /* some pre-determined result */;
        return modifier.apply(back); //apply our external modifier
    }

}

//With our old 'c'
double odds = c.chanceOfSuccess(d -> d * 2); //twice as likely!

There's so much more flexibility provided from the Function API in java 8, making complex problems like this incredibly simplified to write.

Rogue
  • 11,105
  • 5
  • 45
  • 71
  • 1
    That would presumably be a [`ToDoubleFunction`](https://docs.oracle.com/javase/8/docs/api/java/util/function/ToDoubleFunction.html). – Boris the Spider May 04 '15 at 12:55
  • This is a(nother) good example of why more people will likely just start to use languages like Scala (Clojure, et al). Java as a language can only evolve so much (...and I've been using it since jdk 1.0; evolved much, it has.) – michael May 05 '15 at 07:30
4

@John here is how I have approached a solution to your problem.

I used the case of MathOperations to make it simpler. I think first that I would be better to have the interface outside of SomeClass like:

public interface MathOperable {

    public int mathOperation(int n1, int n2);

}

I created two examples of classes implementing this interface and one anonymous implementation inside SomeClass (I did an Add, Multiply and an anonymous "Substract")

public class Add implements MathOperable {

    public int mathOperation(int n1, int n2) {

        return n1 + n2;
    }

    public String toString() {
        return "<addition>";
    }

}

The overriding of toString() is simply for the purpose of giving more readability to the examples that I will show at the end of my post.

public class Multiply implements MathOperable {

    public int mathOperation(int n1, int n2) {
        // TODO Auto-generated method stub
        return n1 * n2;
    }

    public String toString() {
        return "<multiplication>";
    }

}

Here is my SomeClass class, it contans a getRandomListOfOperations, where I simulate what happens when the click on the button is done

public class SomeClass {

    private static MathOperable addition = new Add();
    private static MathOperable multiplication = new Multiply();

    // Anonymous substraction  
    private static MathOperable substraction = new MathOperable() {

        public int mathOperation(int n1, int n2) {
            // TODO Auto-generated method stub
            return n1-n2;
        }

        public String toString() {
            return "<substraction>";
        }

    };


    public List<MathOperable> getRandomListOfOperations() {

        // We put the methods in an array so that we can pick them up later     randomly
        MathOperable[] methods = new MathOperable[] {addition,     multiplication, substraction};
        Random r = new Random();

        // Since duplication is possible whe randomly generate the number of     methods to send
        // among three so if numberOfMethods > 3 we are sure there will be     duplicates
        int numberOfMethods = r.nextInt(10);
        List<MathOperable> methodsList = new ArrayList<MathOperable>();

        // We pick randomly the methods with duplicates
        for (int i = 0; i < numberOfMethods; i++) {
            methodsList.add(methods[r.nextInt(3)]);

        }

        return methodsList;     
    }

    public void contactSomeOtherClass() {
        new SomeOtherClass(getRandomListOfOperations());
    }
}

Now here is my SomeOtherClass (which may correspond to your Worker class)

public class SomeOtherClass<T extends MathOperable> {

    Random r = new Random();

    List<T> operations;

    public SomeOtherClass(List<T> operations) {
        this.operations = operations;

        runIt();
    }

    public void runIt() {

        if (null == operations) {
            return;
        }

        // Let's imagine for example that the new result is taken as     operand1 for the next operation
        int result = 0;

        // Here are examples of the web service own datas
        int n10 = r.nextInt(100);
        int n20 = r.nextInt(100);

        for (int i = 0; i < operations.size(); i++) {

            if (i == 0) {
                result = operations.get(i).mathOperation(n10, n20);
                System.out.println("Result for operation N "  + i + " = " +     result);
            } else {

                // Now let's imagine another data from the web service     operated with the previous result
                int n2 = r.nextInt(100);
                result = operations.get(i).mathOperation(result, n2);
                System.out.println("Current result for operation N " + i + "     which is " + operations.get(i) +" = " + result);

            }
        }
    }

}

I have a simple test class that contains a main to connect the two classes

public class SomeTestClass {

    public static void main(String[] args) {
        SomeClass classe = new SomeClass();
        classe.contactSomeOtherClass();
    }

}

Now a few examples of executions:

example1

And another illustration!

example 2

I hope this could be helpful!

alainlompo
  • 4,414
  • 4
  • 32
  • 41
  • Just a constructive critique: see for comparison my answer, re: command pattern; that is actually similar to what you have with `public interface MathOperable`. Specifically, note that a two-arg `mathOperation` taking `int`'s could be a problem down the road. As a general design change, you could pass args in via a ctor (like Commands), and make `mathOperation` no-arg. The "`int`" result will still be problematic; but, it could return either another MathOp or some type of "result" object (but composition will get likely messy... OO math is a hard problem.) – michael May 04 '15 at 10:33
  • @michael_n, thank you, that's a great remark! At first I started digging in the pattern, but then I wanted to have a working approach then match/adapt it to one of the patterns. – alainlompo May 04 '15 at 11:12
2

Okay, I'm going to be "that guy"... the one who understands the question but asks anyway to restate the problem because I think you are on the wrong path. So, bear with me: if you like what you see, great; if not, I understand.

Basically, you have a different intent/motivation/purpose than what "adapter" is suited for. The command pattern is a better fit.

But first, more generally, one of the goals of designing "elements of reusable software" (from the title of the original GOF design patterns book) is that you don't want to modify code when you add functionality; rather, you want to add code without touching existing functionality. So, when you have:

public class Toolbox {
    public void hammer() { ... }
}

and you want to add a screwdriver to your toolbox, this is bad:

public class Toolbox {
    public void hammer() { ... }
    public void screwdriver() { ... }
}

Rather, ideally, all existing code would remain unchanged and you would just add a new Screwdriver compilation unit (i.e., add a new file), and a unit test, and then test the existing code for regression (which should be unlikely, since none of the existing code changed). For example:

public class Toolbox {
    public void useTool(Tool t) { t.execute(); ...etc... }
}

public interface Tool { // this is the Command interface
     public void execute() // no args (see ctors)
}

public Hammer implements Tool {
     public Hammer(Nail nail, Thing t) // args!
     public void execute() { nail.into(t); ... }
}

public Screwdriver implements Tool {
     public Screwdriver(Screw s, Thing t)
     public void execute() { screw.into(t); ... }
}

Hopefully it should become clear how to extend this to your example. The Worker becomes straight-foward list of Tools (or, for clarity, instead of "Tool" , just call it a "Command").

public class Worker {
    public List<Command> actionList;
     ....
    public void work() {
      for(...) {
         action.execute();
      }
    }
}

This pattern also allows for easy "undo" functionality and "retry", as well as memoization (caching results so they don't have to be re-run).

Patrick
  • 1,717
  • 7
  • 21
  • 28
michael
  • 9,161
  • 2
  • 52
  • 49