【问题标题】:Use of Java [Interfaces / Abstract classes] [duplicate]Java的使用[接口/抽象类] [重复]
【发布时间】:2011-02-21 14:10:40
【问题描述】:

最近我决定看一下 Java,所以我对它和 OO 编程方法仍然很陌生,所以我想在学习更多之前先搞清楚一些东西,(我想它永远不会很快开始有良好的做法)。

我现在正在编写一个小 2D 游戏,但我认为我的问题适用于任何不平凡的项目。为简单起见,我将提供我游戏中的示例。

我有不同种类的僵尸,但它们都有相同的属性(x、y、健康、攻击等),所以我编写了一个界面僵尸,我通过WalkingZombie、RunningZombie TeleportingZombie 等实现。这是最好的做法吗?我是使用抽象类更好吗?还是使用超类? (我不打算部分实现功能 - 因此我选择 interface 而不是 abstract class

我有一个描述主角(幸存者)的类,由于它很大,我想编写一个具有不同功能的界面,以便我可以轻松查看和分享它的结构。这是好习惯吗?还是只是浪费空间和时间?

我希望这个问题不会被认为是主观的,因为我认为有经验的程序员不会对这类话题持不同意见,因为接口/超类/抽象类的使用遵循逻辑规则,因此不仅仅是个人选择.

【问题讨论】:

    标签: java interface abstract-class superclass


    【解决方案1】:

    我有不同种类的僵尸,但它们都有相同的属性(x、y、健康、 攻击等)所以我写了一个接口僵尸,我由WalkingZombie实现, RunningZombie TeleportingZombie 等。这是最好的做法吗?我是不是更好 抽象类?还是有一个超类?

    抽象类将成为僵尸的超类。接口在某种意义上也是僵尸的超类(超接口?)。

    通用属性建议至少有一个通用属性的抽象基类。

    (我不打算部分实现功能 - 因此我选择接口 而不是抽象类)

    不知道你的意思。

    如果您有不同种类的怪物(地精、兽人等),您可能会发现这些怪物的共同行为想要属于不同的基类。这将建议一个接口。

    我会从一个抽象基类开始,看看代码告诉你什么。

    我有一个描述主角(幸存者)的类,因为它很大,我 想写一个具有不同功能的界面,以便我可以轻松查看和 分享一下它的结构。这是好习惯吗?还是只是浪费空间和 时间?

    您的幸存者是所谓的玩家角色(与非玩家角色相反 - 游戏中通常不会攻击您的幸存者的人)。

    大多数游戏都将所有这些角色类型视为某种怪物,因为它们都有许多共同的属性(健康、魔法、宝藏、武器等)

    所以也许这更像是一个接口的参数。

    见:

    Using inheritance and polymorphism to solve a common game problem Class diagram examples for RPG (Role Playing Game) designing class hierarchy for typical characters in role playing game

    【讨论】:

      【解决方案2】:

      是的,我认为您在使用接口而不是抽象类方面走在正确的轨道上。

      您可能想要制作的任何具体僵尸都可以拥有您想要实现的步行、跑步或传送功能的任意组合。

      我认为现代编程理论尽可能不鼓励继承,因为从长远来看它会抑制可重用性和灵活性。相反,使用接口和组合来实现灵活性,而无需“紧密耦合”。

      一种无需继承即可重用代码的方法,您可以应用“优先组合优于继承”范式。

      我喜欢认为 Josh Bloch 的“Effective Java”(第 2 版)可以看作是“当前的想法”......

      http://books.google.com/books?id=ZZOiqZQIbRMC&pg=RA1-PA71&lpg=RA1-PA71&dq=%22Bloch%22+%22Effective+java:+programming+language+guide%22+&hl=de&sig=RxlDlRBWUvNAzsAFzqOcftrYI5E#v=onepage&q&f=false

      因此,您可以将所有行为实现为独立的类,然后通过实现和组合为每个僵尸实现赋予其自己的行为组合..

      希望这有意义并有所帮助......

      【讨论】:

      • 是的,但是如果所有僵尸在移动时都应该做一些共同的事情呢?他们都必须有重复的代码,不是吗?
      • @Lerxst:不,这是构图的重点。您只需“包装”一个代表通用功能的类的实例,并将通用调用委托给该类。唯一重复的代码是委托调用,但每个方法只有一行(在您的每个 XXXZombie 类中)。好的 IDE 可以自动将调用委托给封装的主题。
      • 嗨 Lerxt&Wizard 我认为 Wizard 说了我想说的话.. 常见行为将在成员类中定义,并且每个僵尸实现都会委托给这个“移动行为”成员。当然,您最终会得到重复的委托代码 [one-liners],更不用说更多的类了,但它提供了更多的灵活性。例如,“人类”现在可以使用这种常见行为,而无需携带任何与僵尸相关的行李。
      • @Wizard&Amir 是的,我指的是单行代码,我知道您可以从这些东西中将代码抽象为一行,但您仍然必须维护这些单行代码。但是,我认为我们开始进入个人喜好
      • 您可能是对的,在您真正需要这种灵活性之前,有时不值得为合成而烦恼。每当我认为我只会在一个类层次结构中使用某些功能时,我仍然倾向于使用继承。不过,我认为当你发现继承阻碍了你的进步时,组合是一个很好的工具。万事如意
      【解决方案3】:

      我的意见是你最好使用名为 Creature 的抽象类作为所有类型的生物的超类,并将其扩展到所有类型的僵尸的 Zombie .
      而且你还需要一个接口..来定义一个生物可以做什么..
      像也许,走路,抓抓,或尖叫......
      你需要一个抽象类的原因是禁用Creature的实例化,你不希望有一个不知道它是什么生物的生物,对吧?

      【讨论】:

      • 技术点:如果你想禁用实例化,abstract 是不够的,你还必须让构造函数受保护,否则匿名实例化像这样: new Creature(){};是可能的。
      【解决方案4】:

      如有疑问,我会选择遵循 GOF 范式。

      封装变化: - 在自己的类中定义独特的行为。参考上面的示例,在其单独的类中实现步行、跑步和传送的行为。这样就实现了多态行为。

      相反,**聚合常见的内容** - 使用抽象类来定义多态关联中的常见行为。我在设计对象之间的关系时使用这些原则。

      【讨论】:

        【解决方案5】:

        没有人同意在超级/抽象类上使用接口;)

        使用接口和超/抽象类的主要原因是启用多态性。例如,在您的情况下,您有东西在屏幕上移动(玩家和僵尸等)。为什么不使用相同的方法让它们都在屏幕上移动呢?也许从一个名为“Movable”的对象或类似的东西继承所有将在屏幕上移动的东西。

        如果你真的很喜欢这些东西,你可能还想看看 mixins。这不是 Java 直接支持的东西,但有一些为它构建的库。

        【讨论】:

        • Joshua Bloch,Spring 框架背后的人,基本上任何不想陷入单一(实现)继承的人都喜欢组合而不是(实现)继承。那是相当多的人:)
        • 当然,这是一个很好的观点,但我不打算开始讨论这个主题;)世界上没有足够的时间。
        • 对于常见行为,请改用策略模式。让接口zombie有一个getMoveStrategy()方法,并使用zombie.getMoveStrategy().move()调用它。可能有一个 HopStrategy、一个 SlouchStrategy 和一个 RunStrategy 等,它们可能有参数(例如,受伤的 Zombie 速度较慢,愤怒的 Zombie 速度更快等,在没有策略模式的情况下涉及很多 if-then-else)
        【解决方案6】:

        您可以将接口视为“合同”。您正在定义一组实现此接口的类必须实现的方法。

        另一方面,当您有一些代码对您想要实现的所有子类而言可能是通用的时,就会使用抽象类。因此,您可能有一个名为 Shape 的抽象类,它有一些通用代码,而在您的派生类(Circle、Square 等)中,您可能有特定于这些形状的代码(getArea 就是一个例子)。但是颜色之类的东西可能对所有形状都是通用的,因此您可以在 Shape 抽象类中放置一个 getColor 方法。

        您可以将这两个想法结合起来。您可以拥有实现接口的抽象类,这可以让您两全其美。

        这些概念在 OO 中反复使用,因此理解它们很重要。你似乎一切顺利:)。

        因此,如果您的僵尸类具有适用于所有类型的僵尸的一些共同行为,那么它听起来像是一个很好的抽象类候选者。如果您的游戏中有其他角色(可能是 UndeadMice 或其他 :)),您也可以考虑创建一个界面(可能是 GameCharacter 界面)。那么你的Zombie抽象类和UndeadMouse抽象类将实现GameCharacter接口。

        【讨论】:

        • 谢谢 :) 虽然很难决定给谁“回答”。我特别喜欢你关于实现接口的抽象类的想法,它提供了一个非常清晰的类型结构,大致相同属性,同时允许非常不同类型的运动处理,例如。仍然很难以正确的方式真正连接所有内容,查看所有关系等
        • @Samuel - 当您在查看如何连接事物时遇到问题时,有时将其绘制在纸上或使用 Microsoft Visio 之类的工具会有所帮助。它可以帮助您可视化事物如何组合在一起,以便您设计适当的抽象级别。
        【解决方案7】:

        我会把 Zombie 写成一个抽象类,以避免重新定义字段 x、y、健康等...

        对于 Survivor 类,我只需将要在外部使用的函数声明为 public。我在类的顶部声明了公共函数。当只有一个类实现接口时声明接口会无用地添加要维护的文件。避免它。

        【讨论】:

        • 我不会将添加一个文件称为维护问题。稍后,当您想要通过界面获得的额外内容但您没有界面并且要投入大量工作时,这是一个问题。
        • 一个文件不是问题,但如果你系统地这样做,你最终会得到很多文件。在需要时添加接口对于当今的 IDE 来说没什么大不了的。
        • 对我来说,接口文件也帮助我保持整个程序的结构清晰。 x, y, health 很好,我可能会使用上面提到的想法,即为拥有共同 x, y, Health 的僵尸实现带有抽象类的接口。
        【解决方案8】:

        就您的僵尸示例而言,界面会很好,除非您有希望所有僵尸都执行的通用代码。

        假设你有一个Move 方法,它可以让walkingzombies 走,runningzombies 跑等等。但是,如果你想让“Move”让任何种类的僵尸做一些共同的事情,那么界面就会迫使你复制代码,因为您不能将主体放在界面中。

        【讨论】:

          【解决方案9】:

          我认为在您的情况下,您的接口和类结构与现实不符。事实上,我相信(如果我错了,请纠正我)每个僵尸都可以行走、奔跑、传送等,具体取决于它所在的位置。

          因此,您应该有一个僵尸类或接口,并具有修改僵尸状态的操作。该动作可能是一个接口或一个抽象类,因此您可以在不知道确切动作的情况下将任何动作应用于僵尸(例如action.perform(zobie))。

          如果您有不同种类的僵尸,例如三足僵尸和单臂僵尸,您可能需要实现处理僵尸内容的不同类,例如显示自己或验证状态更改(例如特殊种类的僵尸可能不接受被传送)。

          【讨论】:

          • 这将是一个有趣的社会学问题,看看某人有多大可能将“行走”、“跑步”和“传送”解释为状态,而不是根据他们生命中损失了多少小时的类型来绘制便宜的僵尸射击游戏:)
          • 为什么我有不同的类型是因为它们有不同的方法 1. 移动和 2. 初始化,行走的僵尸走进屏幕传送僵尸出现在你附近并攻击你。也可能有醉酒僵尸高速乱跑乱扔瓶子(例子)
          • 我明白了,所以它们不同的僵尸类型,而不是任何僵尸都可以执行的动作。在这种情况下,拥有自己的实现会很好恕我直言。
          猜你喜欢
          • 2011-12-05
          • 2011-07-02
          • 1970-01-01
          • 2011-07-13
          • 2011-07-12
          • 1970-01-01
          • 2013-11-25
          • 2015-08-20
          • 1970-01-01
          相关资源
          最近更新 更多