这是构建 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
?
您还以一种奇怪的方式混合了职责/抽象。 PlayerClasses
和 Player
中封装的是什么? 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())
好的,所以我有整个模块负责在我的游戏中生成玩家 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
?
您还以一种奇怪的方式混合了职责/抽象。 PlayerClasses
和 Player
中封装的是什么? 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())