本文介绍了游戏引擎设计选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我听说组合比继承更可取.因此,当我设计简单的游戏引擎时,我倾向于执行以下操作:

I heard composition is preferable over inheritance. So when I design my simple game engine I tend to do the following:

http://yuml.me/1252399d

代替:

http://yuml.me/51cacb4f

这是一个好方法,还是我应该重新考虑?

Is this a good approach, or should I reconsider?

澄清符号.箭头表示继承,带菱形的箭头表示组合,<<>> 表示接口.yUML 网页上提供了有关符号的更多信息.

To clarify the notation. The arrows means inheritance, the arrows with a diamond means composition and the <<>> means interface. More info on the notation is available at the yUML webpage.

推荐答案

好的,这里有几件事情要介绍.

OK, there are a few things to cover here.

第一:为什么要表达PointLightSpotlight的共性?是吗

First: why do you need to express commonality between PointLight and Spotlight? Is it

  1. 因为有些客户只是想将它们用作 Light,而不关心他们得到的是哪一种?
  2. 因为它们共享一些实现并且您不想重复?
  1. Because there are clients who simply want to use them as a Light, and don't care which variety they get?
  2. Because they share some implementation and you don't want to duplicate?

因为客户需要统一对待Lights

这是接口继承,也称为子类型.作曲在这里不适用 - 因为没有什么可作曲的.您可以通过两种方式实现这一点:您选择哪种方式在很大程度上取决于您的语言.

This is Interface inheritance, also known as subtyping. Composition doesn't apply here - because there's nothing to compose. You can realise this in two ways: which you choose depends largely on your language.

  • 如果您使用的是 Java/C#,这通常可以通过定义一个接口 (ILight) 来实现,该接口公开 Lights 的所有共同特征.客户端将只依赖于接口,而不是它的实现.PointLightSpotLight 将各自实现接口.
  • 如果您没有使用具有一流接口的语言,请将 Light 声明为一个抽象基类,PointLightSpotLight 继承自它.
  • If you're using Java/C# this would usually be achieved by defining an Interface (ILight) that exposes all the common characteristics of Lights. Clients would depend only on the interface, not the implementations of it. PointLight and SpotLight would each implement the interface.
  • If you're not using a language with first class Interfaces, declare Light as an abstract base class with PointLight and SpotLight inheriting from it.

因为你想分享实现

通常有两种方法可以实现这一点:

There are generally two approaches to achieving this:

  • 实现继承.定义类 Light 并在其中实现常见的行为.将 PointLightSpotLight 声明为继承自 Light,根据需要覆盖/添加行为.
  • 组成.定义 PointLightSpotLight 类,而不是从公共超类继承.实现第三个类(您可能仍将其称为 Light)来实现常见行为.PointLightSpotLight 都将包含一个 'Light` 实例并委托给它来实现共享行为.
  • Implementation inheritance. Define class Light and implement common behaviour in it. Declare PointLight and SpotLight as inheriting from Light, overriding/adding behaviour as required.
  • Composition. Define classes PointLight and SpotLight without inheriting from a common superclass. Implement a third class (which you'd still probably call Light) that implements the common behaviour. Both PointLight and SpotLight would include an instance of 'Light` and delegate to it for the shared behaviour.

对于您的具体示例,在实现继承和组合之间选择相对较少.当继承层次结构变得更深时,前者会出现问题.在支持多重实现继承的语言中,它也是出了名的容易出错(例如脆弱的基类问题).另一方面,实现继承意味着更少的输入:语言自动委托给共享行为,而使用组合,您需要编写委托方法.

For your specific example there's relatively little to choose between Implementation inheritance and Composition. Issues with the former arise when the inheritance hierarchy gets deeper. It's also notoriously error prone in languages that support multiple implementation inheritance (e.g. the fragile base class problem). On the other hand, Implementation inheritance means less typing: the language autoamtically delegates to the shared behaviour, whereas with Composition you need to write the delegation methods.

总结

请注意,上述内容并不相互排斥:例如,您可以:

Note also the above are not mutually exclusive: you could, for example:

  • 声明一个SpotLightPointLight 都实现的接口ILight
  • 使用实现继承或组合共享常见行为

从根本上说,您需要明确您要实现的目标.

Fundamentally you need to be clear what you're trying to achieve.

这篇关于游戏引擎设计选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 16:19