这是构建 class 层次结构的正确方法吗?

Is this the correct way to build class hierarchy?

好的,所以我有整个模块负责在我的游戏中生成玩家 class,经过数小时的艰苦劳动,我想出了这个层次结构(代码片段没有让它过于图形化但仍然提供足够的继续)

我有实体(玩家或怪物)的基本接口:

public interface Entity {
public int getHP();
...
// all getters that denote all stats the Entities have in my game, nothing
else is in the interface
}

然后是扩展 Entity 的第二个接口,称为 Classes,它包含与 classes 相关的所有设置器:

public interface Classes extends  Entity {
void setHP(int a);
... //and so on}

终于得到一些真正的class,classPlayerClasses负责构建classes:

public class PlayerClasses implements Classes {    
private int HP, ATK, DEF, DAMAGE, DROP;

@Override
public int getHP() {
    return HP;
}

@Override
public void setHP(int a) {
    HP = a;
}

// this is how I build the player class
public void Rogue(){
   setHP(150);
   setATK(30);
   setDEF(20);
   setDAMAGE();
}

最后一个 class 构造函数用于创建播放器实例,然后传递给其他模块(没有继承或直接访问任何其他 class 字段或需要构造函数中的任何状态, 所以赢了吗?)

public class Player {
private int HP,ATK,DEF,DAMAGE,DROP;
private PlayerClasses c;

public Player() {
    c = new PlayerClasses();
    c.Rogue();
    HP = c.getHP();
    ATK = c.getATK();
    DEF = c.getDEF();
    DAMAGE = c.getDAMAGE();
    DROP = c.getDrop();
}

有点长的问题,但我尽量保持文明。非常感谢任何反馈。

编辑:好吧,澄清一下为什么我选择这样设计,我希望玩家实例是不可变对象,只能用正确的值实例化,同时保持其他模块中的初始化尽可能干净,没有任何依赖性.因此,例如,来自两个不同玩家 classes 的值不能混淆,以防模块中的实例显示玩家统计数据和怪物统计数据。我觉得在继承中传递私有 HP 和 ATK 变量,然后用相同的变量提取命名空间不是一种方法。

我想我不明白包含 PlayerClass 的不可变 Player class 的原因。但无论如何,IMO 你的 Player class 应该继承 Entity 特性。不是用作某种模板(?)的 PlayerClasses 对象。因为有一个 Player 有什么意义,我假设一个类似构造的 Monster class 如果它们不是 Entity?

您还以一种奇怪的方式混合了职责/抽象。 PlayerClassesPlayer 中封装的是什么? PlayerClasses 看起来应该像 "Rogue" 一样代表 class 类型,而不是实际的玩家。为此,它不应该有 setter 方法, class 类型也不是实体。 初始化 PlayerClasses 对象的类似工厂的方法是 "bad" 风格。您应该始终尝试仅基于 class 类型来保证事情是正确的,而不是需要为对象调用正确的魔术方法(即除了构造函数之外没有初始化方法)。

以一个将 PlayerClasses 对象作为参数的方法为例,您希望其他人使用该代码。他们看到他们需要对 PlayerClass 的引用和 class 的无参数构造函数,但他们不知道所有的初始化步骤是什么。构造函数或各种工厂/构建器模式可以准确地保证这一点。

下面是我如何完成的草稿:

interface PlayerClass {
    int getBaseHp();
    int getBaseAtk();
    ... // more class attributes
}

// as utility so I don't have to write 20 full classes
abstract class PlayerClassBase implements PlayerClass {
    private final int hp, atk, ..;
    protected PlayerClassBase(int hp, int atk, ...) {
        this.hp = hp;
        this.atk = atk;
    }
    @Override
    public int getBaseHp() {
        return hp;
    }
    ....
}

// short class but guaranteed that every instance of Rogue has the correct values
class Rogue {
     public Rogue() {
         super(40, 23, 123, ...);
     }
}

// something to represent entities
interface Entity {
    int getCurrentHp();
    int takeDamageFrom(Entity other);
    ...
}

// maybe an abstract base class here as well

// represents a player that has an immutable class and it can't exist without
class Player implements Entity {
    privte final PlayerClass playerClass;
    private int currentHp;
    ...

    public Player(PlayerClass playerClass) {
        this.playerClass = playerClass;
        currentHp = playerClass.getHp();
        ...
    }
    public int takeDamageFrom(Entity other) {
        currentHp -= other.getCurrentAtk();
        return currentHp;
    }

}

PlayerClass 部分也可以是一个简单的 enum 而不是一个大的 class 层次结构。

enum PlayerClass {
    ROGUE(23, 23, 4, ...),
    ...
    ;
    private final int hp;
    PlayerClass(int hp, int atk, ...) {
        this.hp = hp;
        ...
    }
    public int getHp() { return hp; }
    ...
}

这样您就可以静态引用 PlayerClass.ROGUE 并创建一个像这样的播放器:new Player(PlayerClass.ROGUE)。而不是当前 new Player(new PlayerClass().Rogue())。或者使用大层次结构:new Player(new Rogue())