Search code examples
javadata-structuresenumsenumeration

What is a good data structure for representing a field that is more complex than an enum, but not quite a class?


I am writing a program that is involved with rowing, and I am creating an immutable class called "BoatType" to represent the different "types" of boats that there are. This is becoming complex because there are many types that require unique description.

A BoatType should have the following:

  • Either SCULL or SWEEP type
  • A number that is the number of people that fit in the boat
  • Either PORT or STARBOARD type, but only if the first type is SWEEP
  • A boolean representing COXED or UNCOXED, generally only for SWEEP

This is even further complicated by the fact that in the rowing world, there only exist certain combinations of those fields. I'd like my program to have easy enumerated access to these standard types without having to create new BoatType objects. These are, in full:

SCULL 8
SCULL 4
SCULL 2
SCULL 1
SWEEP 8 PORT COXED
SWEEP 8 STARBOARD COXED
SWEEP 4 PORT COXED
SWEEP 4 PORT UNCOXED
SWEEP 4 STARBOARD COXED
SWEEP 4 STARBOARD UNCOXED
SWEEP 2 PORT COXED
SWEEP 2 PORT UNCOXED
SWEEP 2 STARBOARD COXED
SWEEP 2 STARBOARD UNCOXED

Currently this is the class I have written in Java:

public class BoatType
{
public enum RiggerType{SCULL, SWEEP_PORT, SWEEP_STARBOARD}

private int numSeats;

private RiggerType riggerType;

public boolean coxswain = true;

public BoatType(RiggerType type, int seats, boolean coxed)
{
    numSeats = seats;
    riggerType = type;
    coxswain = coxed;
}
}

with a enumeration of standard types elsewhere:

    public static final BoatType 
    SCULL_OCTUPLE = new BoatType(RiggerType.SCULL, 8, false),
    SCULL_QUAD = new BoatType(RiggerType.SCULL, 4, false),
    SCULL_DOUBLE = new BoatType(RiggerType.SCULL, 2, false),
    SCULL_SINGLE = new BoatType(RiggerType.SCULL, 1, false),
    SWEEP_PORT_EIGHT_COXED  = new BoatType(RiggerType.SWEEP_PORT, 8, true),
    SWEEP_STARBOARD_EIGHT_COXED = new BoatType(RiggerType.SWEEP_STARBOARD, 8, true),
    SWEEP_PORT_FOUR_COXED = new BoatType(RiggerType.SWEEP_PORT, 4, true),
    SWEEP_PORT_FOUR_UNCOXED = new BoatType(RiggerType.SWEEP_PORT, 4, false),
    SWEEP_STARBOARD_FOUR_COXED = new BoatType(RiggerType.SWEEP_STARBOARD, 4, true),
    SWEEP_STARBOARD_FOUR_UNCOXED = new BoatType(RiggerType.SWEEP_STARBOARD, 4, false),
    SWEEP_PORT_PAIR_COXED = new BoatType(RiggerType.SWEEP_PORT, 2, true),
    SWEEP_PORT_PAIR_UNCOXED = new BoatType(RiggerType.SWEEP_PORT, 2, false),
    SWEEP_STARBOARD_PAIR_COXED = new BoatType(RiggerType.SWEEP_STARBOARD, 2, true),
    SWEEP_STARBOARD_PAIR_UNCOXED = new BoatType(RiggerType.SWEEP_STARBOARD, 2, false);

This currently seems rather cumbersome, so I was wondering whether anyone had a better idea how to represent this. An enum is out of the question, because while those are the standard types in existence, I don't want to restrict it to those combinations.


Solution

  • You could make BoatType abstract and create two subclasses of BoatType, namely ScullBoatType and SweepBoatType. Then you can define two boolean fields "isPort" and "isCoxed" on SweepBoatType (but not on ScullBoatType). Like so:

    abstract class BoatType {
        protected int numSeats;
    
        public BoatType(int numSeats) {
                this.numSeats = numSeats;
        }
    }
    
    final class ScullBoatType extends BoatType {
        public ScullBoatType(int numSeats) {
            super(numSeats);
        }
    }
    
    final class SweepBoatType extends BoatType {
        private boolean isPort;
        private boolean isCoxed;
    
        public SweepBoatType(int numSeats, boolean isPort, boolean isCoxed) {
            super(numSeats);
            this.isPort = isPort;
            this.isCoxed = isCoxed;
        }
    }
    

    New instances can then be created like this:

    BoatType SCULL_OCTUPLE = new ScullBoatType(8);
    BoatType SWEEP_PORT_EIGHT_COXED = new SweepBoatType(8, true, true);
    // and so on...