【问题标题】:Java - abstract class and interfaceJava - 抽象类和接口
【发布时间】:2015-11-05 21:15:15
【问题描述】:

我正在开发我的 java 项目,这是一个基本的角色扮演游戏,我有一个 OOD 问题(我想这很简单,但这是我的第一个 java 项目)。我开始研究武器,我创建了一个抽象的 Weapon 类,其中包含一个名为 Attack 的抽象方法。武器有两个子类——近战武器和远程武器,都有攻击方法,没问题。

然后我想到了另一件事——如果像长矛这样的近战武器物体可以用于近战,也可以投掷,会发生什么?在这种情况下,什么是好的解决方案?

【问题讨论】:

  • 使用 Throwable 接口。
  • 一种可能的解决方案:忘记抽象武器,创建一个MeleeWeapon 接口(使用meleeAttack 方法)和一个RangedWeapon 接口(使用rangedAttack 方法)。现在一个类可以实现这两个接口。
  • @Turing85 ,我认为界面可以在这里提供帮助,但是我如何区分可以用作近战武器的剑和可以用作近战和远程武器的长矛?
  • 另一种选择是使用组合而不是继承。每件武器都可以拥有一组攻击。例如,Weapon.getAttacks() 返回一组受支持的攻击。 Spear.getAttacks() 返回 Thrust、Throw 和 BashBashBash。
  • 它实现了两者然后抛出一个 Throwable....

标签: java oop interface abstract-class


【解决方案1】:

一种选择是使用组合而不是继承。

每种武器都可以提供一组攻击。例如,Weapon.getAttacks() 返回一组受支持的攻击。在子类中,Spear.getAttacks() 返回ThrustThrowBashBashBash 的实例。

这使您可以对多种武器使用相同的攻击。狼牙棒和长矛都可以提供重击攻击——只是具有不同的 HP 伤害。你可以用长矛或 10 英尺长的杆子进行推力。你可以扔一本法术书。 (这些事情的伤害比你想象的要大。)一般来说,你可以用任何对象定义任何动作的结果。

【讨论】:

  • 我以为您在第一条消息中的意思是只使用一个 Weapon 类,所以每个武器都会有一个可用攻击列表,例如 throw 和推力(这将是 Weapon 类中的方法)。
  • 现在,我了解到您建议创建 Thrust 和 Throw 类,对吗?你能给出一个关于如何处理 Thrust 和 Throw 类的简短实现示例吗?
  • 对,武器是除了其他属性外还具有推力和投掷等攻击的物体。像 Spear 这样的 Weapon 子类可以在构造函数中初始化武器的攻击,传递适当的伤害范围。您引用的attack() 方法可能是 attack 类中的方法,而不是武器类。对于多态性,您需要一个超类 AbstractAttack 来提供抽象的attack() 方法。
  • 一个选项是 Weapon 是一个抽象类,其子类为特定武器类型配置攻击。另一种是有一个 WeaponType 类,每种武器都有一个对象。攻击的抽象基类允许您通过标准接口定义特定攻击并与之交互。方法执行动作,但您可以将动作表示为提供执行动作的方法的对象;有时这些被称为函数对象
  • 这里的关键是识别Attack。 (一种 DDD 方法)。然后你很快就会发现攻击的不是武器,而是使用它的某人/某物。然后你会看到像AttackSource 这样生成AttacksAttackTarget 接受攻击。例如。陷阱可以成为攻击源。例如。门、敌人、墙是有效的攻击目标。一个英雄可以使用很多AttackSources等。这将打开很多新的可能性。
【解决方案2】:

我喜欢界面和组合的想法,两者都提供了非常有据可查的解决方案。但我有第三个想法:只需将 throw() 添加到 MeleeWeapon。听我说,如果你使用界面,它会使随机生成武器变得更加困难。如果您使用合成,则必须重新开始并在武器内创建投掷和近战对象。有时最简单的答案就是要走的路,尤其是因为您现在可以通过其父类引用您的近战或远程对象并调用他们的 throw 方法而无需担心。

【讨论】:

  • 是的,这听起来也是个好主意,而且可能是最简单的:)。但它不会产生重复的代码吗?我的意思是RangedWeapon 会有攻击,并且在MeleeWeapon 中的投掷将与RangedWeapon 类中的攻击方法相同。 RangedWeapon 攻击方式的任何改变都需要我改变 MeleeWeapon 的投掷。
  • 我认为近战投掷与远程攻击不同(失去武器进行回合或伤害计算)。如果它们是完全相同的方法,则在父类中写入远程攻击方法。近战武器会用它的近战攻击覆盖它,并且可以有一个 throw 方法,它只调用 super.attack() 来获得远程版本。
  • 我还没有完全考虑过。也许投掷匕首与用弓射击相同,但与投掷长矛不同。
  • 你的解决方案很实用也很简单。
【解决方案3】:

接口接缝是最好的方法:

概念

也许让你的Weapon 类更抽象一点:只需要基本的方法(攻击、丢弃等)。甚至可以使它成为非抽象的,以便开箱即用。

现在为特殊功能制作一些接口,允许投掷、射击、爆炸等。这样为武器添加新功能会更容易,也更省时。

现在,当调用攻击函数时,它可以根据武器类实现的接口进行完全不同类型的攻击。

设计

基类:

//the general weapon class
public class Weapon{

   //you don't want your base class instantiatable 
   protected Weapon(){}

    public void attack (){
        //take enemy health ect.
    }

    public void drop(){
        //drop weapon
     }
}

你可以对接口做的事情是,如果敌人可以被击中,则让方法返回 true,或者如果敌人可以被击中,则从方法内部调用攻击函数。

近战界面:

public interface Melee {
    boolean onMelee();//called if the player tries to perform a melee attack
}

远程接口:

public interface Ranged {
    boolean onRanged();//called if the player tries to perform a ranged attack
}

弓类武器:

public class WeaponBow extends Weapon implements Ranged{

    @Override
    public boolean onRanged(){
        //play animation, check if target is vulnerable, ect.
        //return true if the enemy can be hit false otherwise
    }
}

现在当玩家尝试使用武器进行攻击时,应用程序可以检查武器类是否实现了这些接口,然后调用相应的方法。

如果一个武器类扩展了多个接口,那么确定要执行哪个操作的一个好方法可能是选择可能的操作,如果玩家选择了远程,则尝试执行远程攻击。例如,如果玩家选择了近战,但它是远程武器,则应调用默认攻击或仅其他可用攻击。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-14
    • 2011-12-05
    • 2011-07-02
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    • 2017-02-19
    • 2020-10-20
    相关资源
    最近更新 更多