HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaModerate

An enum that has situationally invalid entries

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
situationallyenumhasthatinvalidentries

Problem

Is it considered bad style to have that BOTH value in the Direction enum? Problems can arise from BOTH being present, for example when trying to build a procedural UI for the Car class, it (the generator) would see the setTurnSignal(Direction) method and create a way for the user to give it a Direction, but it would suggest all values, of which one is illegal.

Conversely, not having BOTH causes issue with the getBlinkerStatus() as it is no longer able to indicate that both are on. There are a couple ways I could address that issue, such as creating more blinker getters, but that or switching form an enum to bit based values and building masks for them.

enum Direction{
    LEFT, RIGHT, BOTH, NONE;
}

class Car{
    Direction turnSignal;
    boolean hazards; 

    void setTurnSignal(Direction direction){
        if(Direction.BOTH.equals(direction)){
            throw new IllegalArgumentException("Turn signal cannot be set to the given direction: " + direction);
        }else{
            turnSignal = direction;
        }
    }

    Direction getBlinkerStatus(){
        if(hazards){
            return Direction.BOTH;
        }else{
            return turnSignal;
        }
    }

Solution

There are a couple ways I could address that issue, such as creating more blinker getters, but that or switching form an enum to bit based values and building masks for them.

java.util.EnumSet is the piece you are looking for here.

public class Car {
    static enum Direction {
        LEFT, RIGHT
    }

    EnumSet turnSignal;

    void setTurnSignal(Direction direction) {
        turnSignal = EnumSet.of(direction);
    }

    void setHazards() {
        turnSignal = EnumSet.of(Direction.LEFT,Direction.RIGHT);
    }

    EnumSet getBlinkerStatus() {
        return turnSignal;
    }
}


That said, this probably isn't the right approach. If you are trying to capture the state of the blinkers, then you should probably be implementing a state machine that tracks what is going on. A state machine is absolutely what you want if you are worried about the hazards being enabled and disabled when the turn signal is going (as you indicate in your comment).

The straight forward model would be that you have six states - [LEFT,NONE,RIGHT] x [HAZARDS ON, HAZARDS OFF] - with simple transitions between them.

You can hand roll a state machine easily enough, but beware -- it's common for state machines to snowball in complexity, as you learn more about the system you are modeling. So it might be worth investing in a library like stateless4J, which gives you a DSL for wiring together your state transition diagram.

public class FSM {
    enum State {
        OFF, LEFT, RIGHT, HAZARDS
        // Unimaginitive names for the "hidden" states.
        , HAZARDS_LEFT, HAZARDS_RIGHT
    }

    enum Toggle {
        LEFT, RIGHT, HAZARDS
    }

    static StateMachine create() {
        StateMachine fsm = new StateMachine(State.OFF);
        fsm.configure(State.OFF)
                .permit(Toggle.LEFT, State.LEFT)
                .permit(Toggle.RIGHT, State.RIGHT)
                .permit(Toggle.HAZARDS, State.HAZARDS);
        fsm.configure(State.HAZARDS)
                .permit(Toggle.LEFT, State.HAZARDS_LEFT)
                .permit(Toggle.RIGHT, State.HAZARDS_RIGHT)
                .permit(Toggle.HAZARDS, State.OFF);
        // etc.

        return fsm;
    }
}


Alternatively, you might consider a state machine design where some of these states are substates of others; stateless4j has substate support, but I haven't explored it enough to have an opinion on whether this should apply for your problem.

Code Snippets

public class Car {
    static enum Direction {
        LEFT, RIGHT
    }

    EnumSet<Direction> turnSignal;

    void setTurnSignal(Direction direction) {
        turnSignal = EnumSet.of(direction);
    }

    void setHazards() {
        turnSignal = EnumSet.of(Direction.LEFT,Direction.RIGHT);
    }

    EnumSet<Direction> getBlinkerStatus() {
        return turnSignal;
    }
}
public class FSM {
    enum State {
        OFF, LEFT, RIGHT, HAZARDS
        // Unimaginitive names for the "hidden" states.
        , HAZARDS_LEFT, HAZARDS_RIGHT
    }

    enum Toggle {
        LEFT, RIGHT, HAZARDS
    }

    static StateMachine<State, Toggle> create() {
        StateMachine<State, Toggle> fsm = new StateMachine<State, Toggle>(State.OFF);
        fsm.configure(State.OFF)
                .permit(Toggle.LEFT, State.LEFT)
                .permit(Toggle.RIGHT, State.RIGHT)
                .permit(Toggle.HAZARDS, State.HAZARDS);
        fsm.configure(State.HAZARDS)
                .permit(Toggle.LEFT, State.HAZARDS_LEFT)
                .permit(Toggle.RIGHT, State.HAZARDS_RIGHT)
                .permit(Toggle.HAZARDS, State.OFF);
        // etc.

        return fsm;
    }
}

Context

StackExchange Code Review Q#55147, answer score: 11

Revisions (0)

No revisions yet.