复合模式/实体系统和传统OOP

问题描述:

我正在开发一个用Java编写的小型游戏(但问题是与语言无关的).因为我想探索各种设计模式,所以我迷上了复合模式/实体系统(我最初在此处阅读过此处)作为典型的深层次继承的替代方法

I'm working on a small game written in Java (but the question is language-agnostic). Since I wanted to explore various design patterns, I got hung up on the Composite pattern/Entity system (which I originally read about here and here) as an alternative to typical deep hierarchical inheritance.

现在,在编写了数千行代码之后,我有点困惑.我想了解该模式,并且喜欢使用它.我认为它非常酷,有点像星巴克,但感觉它提供的好处是短暂的,并且(最让我讨厌的是)很大程度上取决于您的粒度.

Now, after writing several thousand lines of code, I'm a bit confused. I think understand the pattern and I enjoy using it. I think it's very cool and Starbucks-ish, but it feels that the benefit it provides is somewhat short-lived and (what irks me most) heavily dependent on your granularity.

这是上面第二篇文章的图片:

Here's a picture from the second article above:

我喜欢对象(游戏实体或任何您想称呼它们的对象)具有最少的组件集的方式,并且可以推断出您可以编写类似于以下内容的代码

I love the way objects (Game entities, or whatever you want to call them) have a minimal set of components and the inferred idea is that you could write code that looks something like:

BaseEntity Alien = new BaseEntity();
BaseEntity Player = new BaseEntity();

Alien.addComponent(new Position(), new Movement(), new Render(), new Script(), new Target());
Player.addComponent(new Position(), new Movement(), new Render(), new Script(), new Physics());

..真的很好...但是在现实中,代码最终看起来像

.. which would be really nice... but in REALITY, the code ends up looking something like

BaseEntity Alien = new BaseEntity();
BaseEntity Player = new BaseEntity();

Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());
Player.addComponent(new Position(), new KeyboardInputMovement(), new RenderPlayer(), new ScriptPlayer(), new PhysicsPlayer());

看来,我最终拥有一些由较少的组件组成的非常专业的组件.通常,我必须制作一些具有其他组件依赖性的组件.毕竟,如果没有职位怎么办?不仅如此,最终渲染玩家与外星人与手榴弹的方式也可能根本不同.除非您组成一个非常大的组件(否则,您为什么要使用复合模式?)

It seems that I end up having some very specialized components that are made up of lesser components. Often times, I have to make some components that have dependencies of other components. After all, how can you render if you have no position? Not only that, but the way you end up rendering a player vs. an alien vs. a grenade can be fundamentally different. You can't have ONE component that dictates all three, unless you make a very big component (in which case... why are you using the composite pattern anyway?)

再举一个真实的例子.我的游戏中有可以装备各种装备的角色.装备好齿轮后,一些统计信息以及可视化显示都会发生变化.这是我的代码现在的样子:

To give another real-life example. I have characters in my game that can equip various gear. When a piece of gear is equipped, some statistics are changed as well as what's displayed visually. Here's what my code looks like right now:

billy.addControllers(new Movement(), new Position(), new CharacterAnimationRender(), new KeyboardCharacterInput());

billy.get(CharacterAnimationRender.class).setBody(BODY.NORMAL_BODY);
billy.get(CharacterAnimationRender.class).setFace(FACE.BLUSH_FACE);
billy.get(CharacterAnimationRender.class).setHair(HAIR.RED_HAIR);
billy.get(CharacterAnimationRender.class).setDress(DRESS.DRAGON_PLATE_ARMOR);

上面的CharacterAnimationRender.class仅影响以视觉方式显示的内容.因此,我显然需要制作另一个处理齿轮状态的组件.但是,为什么我要做类似的事情:

The above CharacterAnimationRender.class only affects what's displayed VISUALLY. So I clearly need to make another Component that handles gear stats. However, why would I do something like:

billy.addControllers(new CharacterStatistics());

billy.get(CharacterAnimationRender.class).setBody(BODY.NORMAL_BODY);
billy.get(CharacterStatistics.class).setBodyStats(BODY_STATS.NORMAL_BODY);

什么时候我可以制作一个同时处理统计信息分布和视觉变化的CharacterGearStuff控制器/组件?

When I can just make a CharacterGearStuff controller/component that handles BOTH the distribution of stats as well as the visual change?

最重要的是,我不确定这将如何帮助提高生产率,因为除非您想手动处理所有事情,否则您仍然必须创建依赖于2个以上组件的元组件"(并进行修改/交叉修改)所有的子组件-让我们回到OOP).或者,也许我在想这是完全错误的.是吗?

Bottom line, I'm not sure how this is supposed to help productivity since unless you want to handle everything manually, you still have to create "meta-components" that depend on 2+ components (and modify/cross-modify all of their sub-components - bringing us back to OOP). Or maybe I'm thinking about it completely wrong. Am I?

听起来好像您对组件模式有误解.

It sounds like you've slightly misunderstood the component pattern.

组件仅是数据,没有代码.如果您的组件中有代码,那么它就不再是组件了,它会变得更加复杂.

Components are data ONLY, no code. If you have code in your component, it's not a component any more - it's something more complex.

例如,您应该可以轻松共享CharacterAnimationRender和CharacterStatistics,例如:

So, for instance, you should be able to trivially share your CharacterAnimationRender and CharacterStatistics, e.g.:

CharacterStats { int BODY }
CharacterGameStats { ...not sure what data you have that affects gameplay, but NOT the rendering... }
CharacterVisualDetails { int FACE, int HAIR }

...但是没有必要让他们知道彼此的存在.当您谈论组件之间的依赖关系"时,我怀疑您迷路了.一个整数结构如何才能依赖"另一个整数结构?他们不能.它们只是大块数据.

...but there is no need for these to be aware of the existence of each other. The moment you talk about "dependencies" between components, I suspet you've got lost. How can one struct of ints "depend upon" another struct of ints? They can't. They're just chunks of data.

...

从一开始就回到您的担忧,最后得到:

Going back to your concerns at the start, that you end up with:

Alien.addComponent(new Position(), new AlienAIMovement(), new RenderAlien(), new ScriptAlien(), new Target());
Player.addComponent(new Position(), new KeyboardInputMovement(), new RenderPlayer(), new ScriptPlayer(), new PhysicsPlayer());

...那太完美了.假设您已正确编写了这些组件,则已将数据拆分为易于阅读/调试/编辑/编码的小块.

...that's perfect. ASSUMING you've written those components correctly, you've split out the data into small chunks that are easy to read/debug/edit/code against.

但是,这是在猜测,因为您尚未指定这些组件内部的内容…… AlienAIMovement-这是什么?通常,我希望您有一个"AIMovement()",然后对其进行编辑以使其成为外星人的版本,例如更改该组件中的一些内部标志以使用您的AI系统中的外星人"功能来表示该组件.

However, this is making guesses because you haven't specified what's inside those components ... e.g. AlienAIMovement - what's in that? Normally, I'd expect you to have a "AIMovement()", and then edit it to make it into an Alien's version, e.g. change some internal flags in that component to indiate it's using the "Alien" functions in your AI system.