面向对象的设计 - 当一个 class 中有很多数据字段时,封装有多重要?

Object-Oriented design - how important is encapsulation when there're lots of data-fields in one class?

我有一个关于封装的问题:

当 class 有很多数据字段时,是否建议使用封装?

以下面的class为例:

abstract public class Character {
    private String name;
    private String characterClass;
    private int level;
    private int hitDice;

    private int strength;
    private int constitution;
    private int dexterity;
    private int intelligence;
    private int wisdom;
    private int charisma;

    private int hp;
    private int currentHp;
    private int armorClass;
    private int BaseAttackBonus;

    private long xp;
    private double gp;

    private Inventory inventory;
    private double carriedWeight;

    private Equipment equipment;

    protected Character(String name) {

        setName(name);
        setCharacterClass("Class");
        setLevel(1);
        setHitDice(0);

        setStrength(10);
        setConstitution(10);
        setDexterity(10);
        setIntelligence(10);
        setWisdom(10);
        setCharisma(10);

        setHp((int) getLevel() * (getHitDice() + getModifier(getConstitution())));
        setCurrentHp(getHp());
        setArmorClass(10 + getModifier(getDexterity()));
        setBaseAttackBonus(0);

        inventory = new Inventory();
        setCarriedWeight(0);

        equipment = new Equipment();

        setXp(0);
        setGp(20);

    }

    protected Character(String name, int lvl) {

        setName(name);
        setCharacterClass("Class");
        setLevel(lvl);
        setHitDice(0);

        setStrength(10);
        setConstitution(10);
        setDexterity(10);
        setIntelligence(10);
        setWisdom(10);
        setCharisma(10);

        setHp((int) getLevel() * (getHitDice() + getModifier(getConstitution())));
        setCurrentHp(getHp());
        setArmorClass(10 + getModifier(getDexterity()));
        setBaseAttackBonus(0);

        inventory = new Inventory();
        setCarriedWeight(0);

        equipment = new Equipment();

        setXp(1000 * (getLevel() - 1));
        setGp(getLevel() * 20);

    }

    void displayCharacter() throws IOException {
        System.out.print("\n\n");
        System.out.println("Name: " + getName());
        System.out.println("Class: " + getCharacterClass());
        System.out.println("Level: " + getLevel());
        System.out.println("HP: " + getHp());
        System.out.println("Current HP: " + getCurrentHp());
        System.out.println("Armor Class: " + getArmorClass());
        System.out.println("Base Attack Bonus : +" + getBaseAttackBonus());

        System.out.println("***************");
        System.out.println("Attributes: ");
        System.out.println("Strength: " + getStrength());
        System.out.println("Constitution: " + getConstitution());
        System.out.println("Dexterity: " + getDexterity());
        System.out.println("Intelligence: " + getIntelligence());
        System.out.println("Wisdom: " + getWisdom());
        System.out.println("Charisma: " + getCharisma());
        System.out.println("***************");
        equipment.showEquipment();
        inventory.showInventory();
        System.out.println("Carried weight: " + getCarriedWeight());

        System.out.println("");
        System.out.println("XP: " + getXp());
        System.out.println("Gold: " + getGp());
        System.out.println("");

    }

    public int getModifier(int number) {
        int mod = (int) ((number - 10) / 2);
        return mod;
    }

    public String getName() {
        return name;
    }

    public String getCharacterClass() {
        return characterClass;
    }

    public int getLevel() {
        return level;
    }

    public int getHitDice() {
        return hitDice;
    }

    public int getStrength() {
        return strength;
    }

    public int getConstitution() {
        return constitution;
    }

    public int getDexterity() {
        return dexterity;
    }

    public int getIntelligence() {
        return intelligence;
    }

    public int getWisdom() {
        return wisdom;
    }

    public int getCharisma() {
        return charisma;
    }

    public int getHp() {
        return hp;
    }

    public int getCurrentHp() {
        return currentHp;
    }

    public int getArmorClass() {
        return armorClass;
    }

    public int getBaseAttackBonus() {
        return BaseAttackBonus;
    }

    public Equipment getEquipment() {
        return equipment;
    }

    public Inventory getInventory() {
        return inventory;
    }

    public double getCarriedWeight() {
        return carriedWeight;
    }

    public long getXp() {
        return xp;
    }

    public double getGp() {
        return gp;
    }

    protected void setName(String Name) {
        name = Name;
    }

    protected void setCharacterClass(String characterClass) {
        this.characterClass = characterClass;
    }

    protected void setLevel(int lvl) {
        level = lvl;
    }

    protected void setHitDice(int hd) {
        hitDice = hd;
    }

    protected void setStrength(int str) {
        strength = str;
    }

    protected void setConstitution(int con) {
        constitution = con;
    }

    protected void setDexterity(int dex) {
        dexterity = dex;
    }

    protected void setIntelligence(int intel) {
        intelligence = intel;
    }

    protected void setWisdom(int wis) {
        wisdom = wis;
    }

    protected void setCharisma(int cha) {
        charisma = cha;
    }

    protected void setHp(int hitPoints) {
        hp = hitPoints;
    }

    protected void setCurrentHp(int curHp) {
        currentHp = curHp;
    }

    protected void setArmorClass(int ac) {
        armorClass = ac;
    }

    protected void setBaseAttackBonus(int bab) {
        BaseAttackBonus = bab;
    }

    protected void setXp(int XP) {
        xp = XP;
    }

    protected void setGp(double GP) {
        gp = GP;
    }

    protected void setCarriedWeight(double weight) {
        carriedWeight = weight;
    }

    public void attack(Character target) {

        try {
            ((Weapon) getEquipment().getPrimaryHand()).attack(this, target);
        } catch (NullPointerException e) {
            getEquipment().equipPrimaryHand(
                    MeleeWeapon.meleeWeaponList.get(0)); /* equip fist weapon */
            ((Weapon) getEquipment().getPrimaryHand()).attack(this, target);

            if (target.getCurrentHp() <= 0) {
                System.out.println(target.getName() + " is down !");
            }
        }
    }

    public void equip() {
        getInventory().equip(this);
    }

    public void addToInventory(Item newItem) {
        getInventory().addToInventory(this, newItem);
    }

}

考虑将大部分数据字段存储在不同的 class 中,例如 strengthconstitution 中的 Stats class作为更好的设计?

对象有行为。对象内的状态支持该行为。分离出一堆状态,简单地作为状态的容器,而不是一个具有自己特定行为的独特对象,在事物的大局中没有多大意义。

我认为您指的是 decomposition - 将大型系统分解为更小、更易于理解的部分的行为。

要正确分解你的代码,你必须关注cohesion:你的class代表了多少东西?他们 "stick" 在一起有多好?

你的class目前代表了很多东西:

  1. 角色身份信息,例如name
  2. 技能跟踪器,例如constition
  3. 体验追踪器
  4. 库存
  5. 个人体重秤

您的 1 class 代表多个实体;它具有低内聚性。


所以为了回答您的问题,是的,将与统计相关的字段移动到统计 class 是一件好事。但这不仅仅是您应该移动的领域,而是整个责任。这意味着如果你有一个 resetStats() 方法,它也会转到 Stat class.

如果被移动的成员是private,分解有助于进一步封装,因为它进一步隐藏了信息。但分解是它自己的主题。