2

I have the following code and I believe I see why I get the errors:

The errors are, for example:

  • Self-reference in initializer in the constructor of ROCK at RPSGesture.ROCK.
  • Illegal forward reference in the constructor of ROCK at RPSGesture.PAPER.

public interface Gesture {
    public List<? extends Gesture> winsFrom();

    public List<? extends Gesture> tiesTo();

    public List<? extends Gesture> losesTo();
}

public enum RPSGesture implements Gesture, Action {
    ROCK(
        Arrays.asList(RPSGesture.SCISSORS),
        Arrays.asList(RPSGesture.ROCK),
        Arrays.asList(RPSGesture.PAPER)
    ),

    PAPER(
        Arrays.asList(RPSGesture.ROCK),
        Arrays.asList(RPSGesture.PAPER),
        Arrays.asList(RPSGesture.SCISSORS)
    ),

    SCISSORS(
        Arrays.asList(RPSGesture.PAPER),
        Arrays.asList(RPSGesture.SCISSORS),
        Arrays.asList(RPSGesture.ROCK)
    );

    private final List<RPSGesture> winsFrom;
    private final List<RPSGesture> tiesTo;
    private final List<RPSGesture> losesTo;

    private RPSGesture(final List<RPSGesture> winsFrom, final List<RPSGesture> tiesTo, final List<RPSGesture> losesTo) {
        this.winsFrom = winsFrom;
        this.tiesTo = tiesTo;
        this.losesTo = losesTo;
    }

    @Override
    public List<RPSGesture> winsFrom() {
        return winsFrom;
    }

    @Override
    public List<RPSGesture> tiesTo() {
        return tiesTo;
    }

    @Override
    public List<RPSGesture> losesTo() {
        return losesTo;
    }
}

I have seen Peter Lawrey's Answer, however is a static initializer really the best way to do it? Are there any other reasonble alternatives?

Does the design of this enum look correct, or would you do it differently yourself? With hopefully less code in the class.

Community
  • 1
  • 1
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • 1
    Well, you always `tiesTo()` yourself, don't you? Just make `tiesTo()` return `Collections.singletonList(this)`... – fge Mar 19 '14 at 08:45
  • @fge I'm not so sure about that. In regular Rock-Paper-Scissors yes, however in custom variants you might be able to tie to multiple gestures. – skiwi Mar 19 '14 at 08:46
  • Either a `static` initialiser (this is the **same** as a constructor in the case of an `enum`) or just implement the methods _in_ the `enum` instances. – Boris the Spider Mar 19 '14 at 08:46

1 Answers1

1

I found a proper way to do it myself, using Java 8:

public interface Gesture {
    public List<? extends Gesture> winsFrom();

    public List<? extends Gesture> tiesTo();

    public List<? extends Gesture> losesTo();
}

public enum RPSGesture implements Gesture, Action, RuleGestureFPSFactory {
    ROCK,
    PAPER,
    SCISSORS;

    @Override
    public List<RPSGesture> winsFrom() {
        return winMapping().get(this);
    }

    @Override
    public List<RPSGesture> tiesTo() {
        return tieMapping().get(this);
    }

    @Override
    public List<RPSGesture> losesTo() {
        return loseMapping().get(this);
    }
}

public interface RuleGestureFactory<T extends Gesture> {
    public Map<T, List<T>> winMapping();

    public Map<T, List<T>> tieMapping();

    public Map<T, List<T>> loseMapping();
}

public interface RuleGestureFPSFactory extends RuleGestureFactory<RPSGesture> {
    @Override
    default public Map<RPSGesture, List<RPSGesture>> winMapping() {
        Map<RPSGesture, List<RPSGesture>> mapping = new HashMap<>();
        mapping.put(ROCK, Arrays.asList(SCISSORS));
        mapping.put(PAPER, Arrays.asList(ROCK));
        mapping.put(SCISSORS, Arrays.asList(PAPER));
        return mapping;
    }

    @Override
    default public Map<RPSGesture, List<RPSGesture>> tieMapping() {
        Map<RPSGesture, List<RPSGesture>> mapping = new HashMap<>();
        mapping.put(ROCK, Arrays.asList(ROCK));
        mapping.put(PAPER, Arrays.asList(PAPER));
        mapping.put(SCISSORS, Arrays.asList(SCISSORS));
        return mapping;
    }

    @Override
    default public Map<RPSGesture, List<RPSGesture>> loseMapping() {
        Map<RPSGesture, List<RPSGesture>> mapping = new HashMap<>();
        mapping.put(ROCK, Arrays.asList(PAPER));
        mapping.put(PAPER, Arrays.asList(SCISSORS));
        mapping.put(SCISSORS, Arrays.asList(ROCK));
        return mapping;
    }
}

This way you can even easily have different implementations of rules, it does however not support switching at runtime.

skiwi
  • 66,971
  • 31
  • 131
  • 216