0

I have an outline for an algorithm - some logical steps that has to be performed in a specific order. The result of the algorithm has to be some number. Naturally this led me to the idea of using the template method pattern. This works fine for void methods but here comes my problem: each of the steps in the algorithm is not a void method but is allowed to return a number (so they are int methods) - if a step returns a non-zero number, this number is the result of the algorithm's execution, if it is a zero - the execution continues with the next step.

This might sound really trivial but I just still find it somehow ugly to have something like:

public int algorithm() {
    int resultStep1 = step1();
    if (resultStep1!=0) {
        return resultStep1;
    }
    int resultStep2 = step2();
    if (resultStep2!=0) {
        return resultStep2;
    }
    ...
}

Of course step1(), step2() and so on are abstract methods and have their own specific implementations in the corresponding classes that extend mine.

The other idea that came to my mind was using exceptions but as we are talking about control flow here this would be an anti-pattern.

Am I missing something here or is this just the way I have to write it?

Anton Sarov
  • 3,712
  • 3
  • 31
  • 48
  • I stopped understanding at the half of the question. Do you want to return the first non-zero step, and what the real problem is – Royal Bg Nov 24 '14 at 11:02
  • How about an Iterable? You could write a next() to your steps() which does not give back the next step, but your result, if it is non-zero. All tight up into a neat loop. – Andreas Gnyp Nov 24 '14 at 11:04
  • @RoyalBg when some of the steps returns a non-zero number, then this is the algorithm's return value, otherwise the execution continues for N steps and returns something else as a last resort – Anton Sarov Nov 24 '14 at 11:05
  • You can have a look at [`ComparisonChain` from Google Guava](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ComparisonChain.html), which is doing something like what you want. – Florent Bayle Nov 24 '14 at 11:06

2 Answers2

3

Java 7

You can define an interface for your steps:

interface Step {
    int step();
}

Then use a list of steps:

ArrayList<Step> steps = new ArrayList<Step>();

Iterate over it like so:

public int algorithm() {
    for (Step step : steps) {
        int result = step.step();
        if (result != 0)
            return result;
    }
    return 0;
}

To initialise that list, you can do this using anonymous implementation classes:

steps.add(new Step() {
    @Override
    public int step() {
        return step1(); //or the code directly
    }
});

steps.add(new Step() {
    @Override
    public int step() {
        return step2();
    }
});

Or create well named implementation classes for each step:

public class Step1 implements Step {
    @Override
    public int step() {
        // TODO Auto-generated method stub
        return 0;
    }       
}

And add to list like this:

steps.add(new Step1());
steps.add(new Step2());

Using lambda in Java8

No interface required.

List:

ArrayList<Supplier<Integer>> steps = new ArrayList<Supplier<Integer>>();

Setup:

steps.add(()-> step1());
steps.add(()-> step2());

Algorithm:

public int algorithm() {
    for (Supplier<Integer> step : steps) {
        int result = step.get();
        if (result != 0)
            return result;
    }
    return 0;
}
weston
  • 54,145
  • 21
  • 145
  • 203
  • This is would take a lot of work to do, since there are a lot of step()s –  Nov 24 '14 at 11:31
  • 2
    + use lambda instead of anonymous, if you use java8 – user2504380 Nov 24 '14 at 11:33
  • @user2504380 great point, I was just trying to install Java 8 to do that. I'm Android programmer, so I haven't had need to yet! – weston Nov 24 '14 at 11:38
  • 1
    @weston I think that `step()` may need to have a context passed as an argument, because the originals steps where done in the same class, where you can just keep the context as local variables, but if you start using external classes, you may want to pass a context to each step (updated by the previous steps). – Florent Bayle Nov 24 '14 at 12:05
  • @FlorentBayle quite probably, but I didn't want to over complicate. But yes if needed, the `step` method in the interface could be made to take arguments and the Java 8 lambda list could be made to be `Function`. Rather than the parameterless `Supplier` – weston Nov 24 '14 at 12:08
  • @FlorentBayle Also, the `Step1` `Step2` classes could be nested classes, then they would still have access to the same fields as the methods `step1` `step2` – weston Nov 24 '14 at 12:12
0

You can do the following:

if(setResultAndCheckIsNonZero(step1())) {
    return result;
} else if(setResultAndCheckIsNonZero(step2())) {
    return result;
} else if ...

Where:

private int result;

private boolean setResultAndCheckIsNonZero(int x) {
    result = x;
    if(result != 0) 
        return true;
    return false;
}
  • 2
    This uses a "side effect". The method called `is...` is not just a test, it sets a field. That's a bit nasty. – weston Nov 24 '14 at 11:27
  • Fix the name would be one way. If you called the method `setResultAndCheckIsNonZero` That way the side effect is explicit. – weston Nov 24 '14 at 11:31
  • I think it's pretty brilliant to set a field, since we are talking about the same class here, which is already loaded in memory. –  Nov 24 '14 at 11:32
  • Well what I did here is just an example. the OP could rename it whatever he/she likes to –  Nov 24 '14 at 11:34
  • OK, yes the OP can do what they like. But if **you** want to get better, educate yourself on side effects: http://en.wikipedia.org/wiki/Side_effect_%28computer_science%29 – weston Nov 24 '14 at 11:37
  • I know all about side effects, don't worry about it –  Nov 24 '14 at 11:41
  • Maybe that's not the best link. You can't avoid side effects. But you can avoid *hidden* side effects and avoid taking your colleagues and even yourself in the future by surprise. If I were to call a method called `isNonZero` I'd be surprised that some field gets updated. – weston Nov 24 '14 at 11:44
  • @weston I changed the name, hope it reflects the presence of a side effect –  Nov 24 '14 at 14:05