William
William

Reputation: 1895

How do I know when to use an enum or a sub-class?

Let's say I am making a game of chess. Would it be more effective to have a base class of Piece()? With sub-classes for each type of piece.

Or, an enum

enum Piece{
King,
Queen,
Knight,
etc;
}

It also brings me onto a common problem I have when refining data.

GameObjects
Piece extends GameObjects

Should I stop here and declare the objects with their individual properties?

Piece King = new Piece("King",5,10); //just made up values, no significance.

Or refine it further and have:

King extends Piece

and then handle King Pieces in a polymorphic way:

Piece king = new King("King,5,10);

thanks

Upvotes: 2

Views: 139

Answers (3)

Saintali
Saintali

Reputation: 4571

The main distinction is that the set of sub-classes is open-ended: you or other people who use your code can create new sub-classes without breaking old code. Enums are the opposite: other people cannot add new items to your enum and even you, the maintainer of the code, cannot add new enum constants without double-checking every single switch-case statement.

So, use enums if you do not expect new items to be added to the set. Use sub-classes otherwise. This does not mean that sub-classes are better: if you know the set of options to be closed-ended then you should NOT use sub-classes. If you do, you will find yourself writing if (... instanceof ...) chains!

If with enums you find yourself adding new cases to your switch statements (or you can anticipate that) then switch to sub-classes. And if with sub-classes, you find yourself writing instanceof chains, then switch to enums.

Upvotes: 0

Bohemian
Bohemian

Reputation: 425128

Use an enum when there are a limited and defined number if instances.

In this case, clearly there are a defined number of pieces, so use an enum.

Note that enums can have methods just like a regular class, so enums don't have to be just "values", they can do stuff.

I would not try to cram too much into your enum, and in fact I would name it PieceType to make it clear what it represents, and perhaps have a class Piece which is an instance of a piece that has a PieceType, a location (board square) and a color.

Upvotes: 0

superEb
superEb

Reputation: 5673

Polymorphism

It depends on how you want to structure the logic of your game, but it probably makes sense to define common behavior (methods) and attributes (fields) in an abstract Piece class and then have each subtype implement abstract methods (or override default methods) and set values of inherited fields based on the ways that they vary. Maybe something like:

public abstract class Piece {
    protected int value;
    public int getValue() {
        return value;
    }
    public abstract boolean isValidMove(String move);
    public void move(String move) {
        //common logic here
        doMove(move);
    }
    protected abstract void doMove(String move);
}

public class King extends Piece {
    public King() {
        this.value = 20;
    }
    public boolean isValidMove(String move) {
        //specific king logic here
    }
    protected void doMove(String move) {
        //more specific king logic
    }
}

This would allow you to use polymorphism to define various pieces with a common Piece API, while the important differences are handled by each concrete type.

Piece king = new King();
//...
if(king.isValidMove(move)) king.move(move);

Enums

Enums allow you to create a set of optimized singleton instances of a common type, which can also define behavior, but they don't support overriding/implementing type-specific behavior very well because you end up having to check which enum the current instance when implementing variations. You would also end up with a problem of only having a single KING or PAWN instance when you really need multiples of those for a game (one white king and one black king, 8 white pawns and 8 black pawns).

public enum Piece {
    KING(20),
    PAWN(1);
    private int value;
    private Piece(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
    public boolean isValidMove(String move) {
        switch(this) {
            case KING:
                //king specific logic
                break;
            case PAWN:
                //pawn specific logic
                break;
        }
    }
    public void move(String move) {
        if(this == KING) {
            //king specific logic
        } else if(this == PAWN) {
            //pawn specific logic
        }
    }
}

So an enum probably wouldn't work very well in this scenario.

Upvotes: 1

Related Questions