【问题标题】:How do I know when to use an enum or a sub-class?我如何知道何时使用枚举或子类?
【发布时间】:2023-03-14 18:20:02
【问题描述】:

假设我正在做一个国际象棋游戏。拥有 Piece() 的基类会更有效吗?每种类型的作品都有子类。

或者,一个枚举

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

这也让我想到了我在提炼数据时遇到的一个常见问题。

GameObjects
Piece extends GameObjects

我应该在这里停下来并声明具有各自属性的对象吗?

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

或者进一步完善它并拥有:

King extends Piece

然后以多态方式处理 King Pieces:

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

谢谢

【问题讨论】:

    标签: class enums polymorphism


    【解决方案1】:

    多态性

    这取决于您希望如何构建游戏逻辑,但在抽象 Piece 类中定义常见行为(方法)和属性(字段)然后让每个子类型实现抽象方法(或覆盖默认方法)并根据继承字段的变化方式设置继承字段的值。可能是这样的:

    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
        }
    }
    

    这将允许您使用多态性来定义具有公共Piece API 的各个部分,而重要的差异由每个具体类型处理。

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

    枚举

    枚举允许您创建一组通用类型的优化单例实例,它们也可以定义行为,但它们不能很好地支持覆盖/实现特定于类型的行为,因为您最终必须检查哪个枚举实施变体时的当前实例。当您确实需要多个实例用于游戏时(一个白色国王和一个黑色国王,8 个白色棋子和 8 个黑色棋子),您最终也会遇到只有一个 KINGPAWN 实例的问题。

    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
            }
        }
    }
    

    所以枚举在这种情况下可能不会很好地工作。

    【讨论】:

      【解决方案2】:

      如果实例数量有限且已定义,请使用枚举。

      在这种情况下,显然有定义的片段数,所以使用枚举。

      请注意,枚举可以像常规类一样具有方法,因此枚举不必只是“值”,它们可以做一些事情。

      我不会试图在你的枚举中塞进太多东西,事实上我会把它命名为 PieceType 以明确它代表什么,并且可能有一个类 Piece 这是一个片段的实例一个PieceType、一个位置(棋盘格)和一种颜色。

      【讨论】:

        【解决方案3】:

        主要区别在于子类集是开放式的:您或其他使用您的代码的人可以在不破坏旧代码的情况下创建新的子类。 枚举则相反:其他人无法向您的枚举添加新项目,即使您是代码的维护者,也无法在不仔细检查每个 switch-case 语句的情况下添加新的枚举常量。

        因此,如果您不希望将新项目添加到集合中,请使用枚举。否则使用子类。这并不意味着子类更好:如果您知道选项集是封闭的,那么您不应该使用子类。如果你这样做了,你会发现自己在写if (... instanceof ...)chains!

        如果使用枚举,您发现自己在 switch 语句中添加了新的 cases(或者您可以预料到),然后切换到子类。如果有子类,你会发现自己在写 instanceof 链,然后切换到枚举。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-03-09
          • 2011-11-15
          • 1970-01-01
          • 1970-01-01
          • 2020-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多