【问题标题】:Object Oriented design - dealing with items and inventory面向对象设计 - 处理物品和库存
【发布时间】:2015-11-10 10:03:52
【问题描述】:

我从事我的第一个 java 项目,这是一个基本的角色扮演游戏。我有一个关于处理物品和库存的问题。首先,我将概述一些课程。 Item 是描述所有项目的抽象类。物品的子类是武器(抽象类)和盔甲(非常合理,我将来还会有更多)。武器有两个子类 - MelleWeapon 和 RangedWeapon。这总结了如何按类别处理项目。此外,我还有一个 Inventory 类,用于描述每个角色的库存。

代码有效,但需要一些升级。我至少有两个主要问题:

首先,Item 的每个具体子类都有一个几乎相同的 getItem 方法。如何避免这种代码重复?

其次,Inventory 中的 addToInventory 方法会变得更长,因为我向 Item 添加了更多子类(很多 if/elseif),所以我认为这是一个糟糕的设计。如何避免以优雅的方式使用 if?

abstract public class Item {

private String name;
private long cost;
private double weight;

public Item(String name, double weight, long cost) {
    this.name = name;
    this.weight = weight;
    this.cost = cost;
}

public String getName() { return name; }
public void setName(String name) { this.name = name; }

public double getWeight() { return weight; }
public void setWeight(double weight) { this.weight = weight; }

public long getCost() { return cost; }
public void setCost(long cost) { this.cost = cost; }

}


abstract public class Weapon extends Item{

private boolean oneHanded;
private String reqTraining;
private int n;
private int dice;
private int attackBonus;
private int damageBonus;


Weapon(String name, double weight, long cost, boolean oneHanded, String reqTraining,  int n, int dice, int attackBonus, int damageBonus) {
    super(name, weight, cost);
    this.oneHanded = oneHanded;
    this.reqTraining = reqTraining;
    this.n = n;
    this.dice = dice;
    this.attackBonus = attackBonus;
    this.damageBonus = damageBonus;
}


String getReqTraining(){ return reqTraining; }
int getN() { return n; }
int getDice() { return dice; }
int getAttackBonus() {return attackBonus; }
int getDamageBonus(){ return damageBonus; }

public abstract void attack(Character attacker, Character defender);
}



public class Armor extends Item{

private String reqTraining;
private int acBonus;

Armor(String name, String reqTraining, int acBonus,double weight, long cost) {
    super(name, weight, cost);
    this.reqTraining = reqTraining;
    this.acBonus = acBonus;
}

static List<Armor> armorList = new ArrayList<Armor>();

static
{
    armorList.add(new Armor("Full Plate Armor","Heavy", 8, 25, 200));
    armorList.add(new Armor("Chain Mail Armor","Medium", 5, 18, 120));

}

String getReqTraining(){ return reqTraining; }
int getACBonus() { return acBonus; }


public static Armor getItem(String itemName) {
    try {
        for (Iterator<Armor> iter = armorList.iterator(); iter.hasNext(); ) {
            Armor item = iter.next();
            if (itemName.equals(item.getName())) {
                return item;
            }
        }

    } catch (Exception e) {
        System.out.println(itemName + " haven't been found in spells-list");
        return null;
    }
    return null;
}

}




public class  MeleeWeapon extends Weapon {

boolean throwable;

MeleeWeapon(String name,boolean oneHaned, String reqTraining, int n, int dice, int attackBonus, int damageBonus,double weight, long cost, boolean throwable) {
    super(name, weight, cost, oneHaned, reqTraining, n, dice, attackBonus, damageBonus);
    this.throwable = throwable;
}

static List<MeleeWeapon> meleeWeaponList = new ArrayList<MeleeWeapon>();

static
{
    meleeWeaponList.add(new MeleeWeapon("Long Sword",true, "Martial", 1, 8, 0, 0,8, 10, false));
    meleeWeaponList.add(new MeleeWeapon("Short Sword",true, "Martial", 1, 6, 0, 0,5, 5, false));
    meleeWeaponList.add(new MeleeWeapon("Dagger",true, "Basic", 1, 4, 0, 0,2, 3, true));
    meleeWeaponList.add(new MeleeWeapon("Quarter-staff",false, "Basic", 1, 4, 0, 0,3, 2, false));
    meleeWeaponList.add(new MeleeWeapon("Shield",false, "Martial", 1, 4, 0, 0,8, 8, false));

}


public void attack(Character attacker, Character defender){

    int attackRoll = DiceRoller.roll(20) + attacker.getBaseAttackBonus() + attacker.getModifier(attacker.getStrength()) + getAttackBonus() ;
    System.out.println(attacker.getName() + " attack Roll: " + attackRoll + "AC: " + defender.getArmorClass());

    if (attackRoll >= defender.getArmorClass()){
        System.out.println("Defender: " + defender.getName() + " had " + defender.getCurrentHp());
        int damage = DiceRoller.roll(getN(), getDice()) + attacker.getModifier(attacker.getStrength()) + getDamageBonus() ;
        System.out.println("Damage : " + damage);
        defender.setCurrentHp(attacker.getCurrentHp() - damage);
        System.out.println("Defender: " + defender.getName() + " has " + defender.getCurrentHp());
    } else {
        System.out.println("Missed Attack");
    }

}

public static  MeleeWeapon getItem(String itemName) {
    try {
        for (Iterator<MeleeWeapon> iter = meleeWeaponList.iterator(); iter.hasNext(); ) {
            MeleeWeapon item = iter.next();
            if (itemName.equals(item.getName())) {
                return item;
            }
        }

    } catch (Exception e){
        System.out.println(itemName + " haven't been found");
        return null;
    }
    return null;
}


}




public class RangedWeapon extends Weapon {

private String shoots;

RangedWeapon(String name, boolean oneHaned, String reqTraining, String shoots, int n, int dice, int attackBonus, int damageBonus, double weight, long cost) {
    super(name, weight, cost, oneHaned, reqTraining, n, dice, attackBonus, damageBonus);
    this.shoots = shoots;
}


static List<RangedWeapon> rangedWeaponList = new ArrayList<RangedWeapon>();

static {
    rangedWeaponList.add(new RangedWeapon("Long Bow", false, "Archery", "Arrow", 1, 8, 0, 0, 5, 10));
    rangedWeaponList.add(new RangedWeapon("Short Bow", false, "Archery", "Arrow", 1, 6, 0, 0, 3, 5));
}


public void attack(Character attacker, Character defender) {

    int attackRoll = DiceRoller.roll(20) + attacker.getBaseAttackBonus() + attacker.getModifier(attacker.getDexterity()) + getAttackBonus();
    System.out.println(attacker.getName() + " attack Roll: " + attackRoll + "AC: " + defender.getArmorClass());

    if (attackRoll >= defender.getArmorClass()) {
        System.out.println("Defender: " + defender.getName() + " had " + defender.getCurrentHp());
        int damage = DiceRoller.roll(getN(), getDice()) + attacker.getModifier(attacker.getStrength());
        System.out.println("Damage : " + damage);
        defender.setCurrentHp(attacker.getCurrentHp() - damage);
        System.out.println("Defender: " + defender.getName() + " has " + defender.getCurrentHp());
    } else {
        System.out.println("Missed Attack");
    }

}

public static RangedWeapon getItem(String itemName) {
    try {
        for (Iterator<RangedWeapon> iter = rangedWeaponList.iterator(); iter.hasNext(); ) {
            RangedWeapon item = iter.next();
            if (itemName.equals(item.getName())) {
                return item;
            }
        }

    } catch (Exception e) {
        System.out.println(itemName + " haven't been found");
        return null;
    }
    return null;
}

}



public class Inventory {

private Map<String,Item> inventory;

Inventory() {

    inventory = new HashMap<String, Item>();
}


public void showInventory() {

    System.out.println("Show Inventory: ");

    for (Map.Entry<String,Item> entry: inventory.entrySet()) {
        System.out.println(entry.getKey());
        }

    System.out.println(" ");
}


public void addToInventory(String itemName) {

    Item newItem = null;

    try {

        if (MeleeWeapon.getItem(itemName) != null) {
            newItem = MeleeWeapon.getItem(itemName);
        } else if (RangedWeapon.getItem(itemName) != null) {
            newItem = RangedWeapon.getItem(itemName);
        } else if (Armor.getItem(itemName) != null) {
            newItem = Armor.getItem(itemName);
        }else
            System.out.println("Add futural other options (like potions) ");

            System.out.println(newItem.getName() + " has been added to inventory");
            inventory.put(newItem.getName(), newItem);

    } catch (Exception e){
        System.out.println("Adding " + itemName +"to inventory has failed");
    }

}

}

【问题讨论】:

  • 虽然这可能仍在 stackoverflow 的范围内,但如果您将来有类似的问题,我想告诉您codereview.stackexchange.com - 可能会更合适。
  • 很高兴知道如果我的问题范围更广,还有另一种选择。

标签: java oop


【解决方案1】:

您的问题是您使用字符串来识别程序中的对象,这是一种常见的反模式,也是错误和性能问题的一大来源。

如果您使用纯字符串识别事物,您会遇到以下问题:(a) 使用名称时可能会出现拼写错误,并且 (b) 您必须一直查找这些内容。

您应该使用 Java 标识符来识别事物。通常的方法是使用public static final。例如:

public static final Armor FULL_PLATE_ARMOR = new Armor("Full Plate Armor","Heavy", 8, 25, 200)

然后你可以在你的代码中使用这个标识符,如果你想在你的 GUI 中选择一个列表,你可以将它添加到数组中。

然后您可以删除您的 getItem 方法。

【讨论】:

  • 目前还没有GUI计划,我该如何处理呢? .另外,我不能使用建议的公共静态 final 来使用 Map。
  • 你的意思是它是基于文本的?我不确定你的问题是什么意思。我认为您需要编写更多代码(用户交互位),然后它会更清晰。你会发现你不需要这些字符串。您可能会有某种菜单。
  • 是的,只是一个基本的 IO 菜单,没什么特别的
  • 如果您显示选项列表并且他们输入一个数字并使用它从数组中获取项目,那么对于玩家来说会更容易。如果您确实希望用户输入项目名称(也许搜索项目),我会将所有项目添加到一个大列表中,然后使用 instanceof 检查类型。
  • 我的建议:创建公共静态决赛。然后继续你的代码,看看你真正需要什么样的列表。
【解决方案2】:

不完全是解决方案,但有两个提示:

  • 您的 getItem() 效率不高。考虑使用 Set 结构来存储数据,那么访问是 O(1)。在任何情况下,如果您使用“新”类型的 for 循环,则不需要迭代器。

  • 考虑使用泛型,这可能会减少您的“双重实现”。

【讨论】:

  • Map 可能比Set 更合适
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多