【问题标题】:Object Oriented Programming in AS3AS3的面向对象编程
【发布时间】:2011-02-24 22:30:46
【问题描述】:

我正在用 as3 构建一个游戏,让球在墙上移动和弹跳。当用户单击时,会出现爆炸,并且任何击中该爆炸的球也会爆炸。任何击中爆炸的球都会爆炸等等。

我的问题是什么是球的最佳班级结构。我有一个水平系统来控制水平等,我已经想出了编码球的工作方法。这就是我所做的。

我的第一次尝试是为运动、弹跳、爆炸和最后的球创建一个类。这些都按照我刚刚命名的顺序相互扩展。我让它工作了,但是让 Bounce 扩展了 Movement 和 Explosion 扩展了 Bounce,它看起来不是很面向对象,因为如果我想添加一个不移动但爆炸的盒子类怎么办?我需要一个单独的课程来应对这种爆炸。

我的第二次尝试是在不扩展任何东西的情况下创建运动、反弹和爆炸。相反,我将对 Orb 类的引用传递给每个。然后该类存储该引用并根据 Orb 调度的事件(例如更新)执行所需的操作,该事件在每个进入帧时从 Orb 广播。这将推动运动和反弹,以及时机成熟时的爆炸。这种尝试也很有效,但它似乎并不正确。

我也考虑过使用接口,但因为它们更像是类的大纲,所以我觉得代码重用已经过时了,因为每个类都需要自己的代码来完成特定任务,即使该任务正是一样。

我感觉好像我正在为 as3 不支持的类寻找某种形式的多重继承。

有人可以向我解释一种更好的方法来做我正在尝试做的事情吗?我是否通过对运动、弹跳、爆炸和球体进行分类来实现“面向对象”?接口是要走的路吗?任何反馈表示赞赏!

【问题讨论】:

    标签: flash oop class actionscript-3


    【解决方案1】:

    Gidday,

    这让我想起了我不久前读过的一个例子,它使用了几乎相同的类名来表示你的例子!如果它可以帮助您找到答案,我记得这本书是由 Ed 出版的 Friends of Ed 出版的,被称为“面向对象的 Actionscript”,虽然它适用于 AS2,但其概念在 AS3 中是相同的。

    除了那本书之外,在我阅读有关设计模式(un/flame at will, people)的文章中,听起来Factory pattern 可用于生成从标准对象生成的不同对象,因为您可以将细节覆盖为你需要。维基百科的第一段解释得很简单,图表也很有帮助。

    -d

    【讨论】:

    • 同意。查看第 2 章:来自 O'Reilly adobe.com/devnet/actionscript/articles/… 的工厂方法模式 [可免费下载的章节中包含射击游戏示例]
    • 其实我刚看完那本书。 :) 工厂模式似乎可行。我会试一试的!
    【解决方案2】:

    我认为您正在寻找类似于PushButton Engine 所做的事情:使用实体和组件。概念是,您有一个实体类 (Orb),它可以包含球体的属性(可能是位置和大小),也可能是您的渲染代码。然后你就有了附加到你的Entity类的组件类(MovementBounceExplosion)。 p>

    alt text http://img.skitch.com/20100528-kph48r4t1bb73tuk8rq4b2u5f5.jpg

    组件应该只包含它们应该做的功能(即反弹组件应该只处理何时/何地反弹,而不是在每一帧上移动球体)。他们还应该可以直接访问其所有者(实体)类。例如,他们可以修改属性(移动、弹跳)或对其调用explode。

    然后,您可以利用工厂模式来创建一个单独的工厂类,该类创建实体并将适当的组件附加到这些实体。

    【讨论】:

    • 感谢您的回复!我已经设置了一个工厂类来创建实体和另一个工厂类来创建组件。您谈论将组件附加到实体,但我不确定最好的方法是什么。我将 Orb 类作为组件工厂的客户端,它传递对实体 Orb 类的引用,Component Movement 使用该实体移动 Orb。有没有更好的附加方法?
    • 是的,听起来差不多。实体可以访问附加到它的组件(数组属性)。组件也可以访问实体(通过属性)。您还可以选择让实体管理 onFrame 事件并在该事件上调用 component.onFrame。您可以很好地控制哪些组件应该能够首先修改实体。
    • 所以你说每个实体都应该有一个附加的组件数组,以便它可以与它们交谈?现在我拥有它,因此实体不持有对其组件的引用,而是调度任何组件都可以决定侦听的停止事件。 Bounce 和 Movement 组件会监听它,但 Explosion 组件不会,因为它会在动画完成后移除其 enterFrame 事件监听器。解释为什么在实体中保留对组件的引用会更好。
    • 这可行。调度事件而不是直接调用方法是一个好主意。我认为我喜欢引用附加组件的主要目的是,当我调用entity.delete() 时,我可以处理其中的组件对象的所有删除。我基本上可以很好地控制属于实体的组件。
    【解决方案3】:

    任何引擎或框架的一个重要规则是,无论您实际使用何种继承或任何类型,引擎处理实体都应该是完全可选的。因此,虽然由引擎处理的具体实体可能使用复杂的继承,但对于引擎来说它是完全透明的,因为它们被抽象为接口。

    例如,您定义了一个接口IMovable。引擎可以处理这个问题并在这些物体周围移动。然后你可以提供一个默认实现,比如说MovableBase,它可以但不需要被其他实体通过继承或组合重用。

    还请注意,组合通常被认为比继承更好、更简洁。过度使用继承通常会导致违反Liskov substitution principle,这是SOLID 5 条原则之一。你可以找到很多关于inheritance vs. composition on Google的文章。

    常见用法中的继承实际上往往会破坏许多 OOP 原则并引入大量依赖项。它通常被视为 OOP 中的一个基本概念,但如果您将其视为实现代码重用的众多方法之一,则效果会更好。

    因此,与其为这个问题而烦恼,不如说您将如何构建特定组件,首先您需要抽象它们以便能够应用dependency inversion principle。这样,您将获得一个耦合度极低的架构。如果一个实体被证明是不必要的复杂,或者如果您发现您可以提取一些代码以供重用,或者您需要重新安排您的继承机制,则不会影响其他代码。

    您可能会发现,实际上尝试将您的实体拆分为多个继承层没有什么意义,因为事情变得不必要地冗长并且奇怪地交织在一起。然而,抽象出您的实体的不同“方面”或“角色”,例如IMovableIBounceableIExplodable 等,将会很有帮助。任何实体都可以随意选择实现任何接口,并因此由引擎进行相应处理。

    软件设计中最重要的一步是设计一种架构,其中不同的模块被彻底拆分并通过狭窄的接口进行交互。当一个模块变大时,对它应用相同的步骤,并将其重构为子模块(您可以这样做,而不必担心软件的其余部分,仅仅因为模块封装得很好)。当它看起来不自然时,不要试图将整体分成几部分。您将获得更多代码,但灵活性较差。

    问候
    back2dos

    【讨论】:

    • +1。你提出了一些非常好的观点。可悲的是,即使在一些广泛使用的框架中(Flex 我在看着你!),你提到的事情并不总是被观察到
    • @Juan Pablo Califano:是的,Flex 是... 一个不同的野兽 :) 对我来说,Flex 更像是一个带有内部框架的组件集,被组件大量使用,但不是那么多开发人员实际上可以直接使用(这就是为什么 Flex 需要使用继承来进行互操作)。我个人认为 Adob​​e 在几个地方出了问题。甚至还有一个IllegalOperationError,用于处理违反 Liskov 替换原则的情况,这对我来说是多态性的先决条件。 API 确实在各个地方都存在这个问题,但 Flex 确实充满了这个问题。
    猜你喜欢
    • 2010-09-18
    • 2011-07-09
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多