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

时间:2015-10-18 20:08:35

标签: java hierarchy class-hierarchy

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

我有实体的基础界面(可以是玩家或怪物):

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,其中包含与类相关的所有setter:

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

最后进入一些真正的类,类PlayerClasses负责构建类:

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();
}

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

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();
}

有点长问题,但我试图保持文明。任何反馈都非常感谢。

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

1 个答案:

答案 0 :(得分:0)

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

您还会以奇怪的方式混合责任/抽象。什么是封装在PlayerClassesPlayer中的内容? PlayerClasses看起来应该像“Rogue”一样代表类类型,而不是实际的玩家。为此,它不应该有setter方法,也不是类类型实体。 初始化PlayerClasses对象的类似工厂的方法是“坏”样式。你应该总是尽量保证只根据类型确定事情是正确的,而不是需要调用对象才能正确的魔术方法(即除了构造函数之外没有init方法)。

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

以下是我将如何做到的草稿:

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而不是大类层次结构。

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())