6
\$\begingroup\$

I'm trying to write a Minecraft server in Ruby as an educational project and I've hit the point now where I need to decide on how I am going to structure the in-game objects (entities). In my mind, an Entity is an in-game thing such as a block, or a player, or a skeleton.

The reason this is code review, and not game design, is because I am interested in what you think of the design from a general programming perspective, as this is an open source project and I want it to be as programmer friendly as possible.

When I started writing the current spike, I came up with a few methods I could use for entity structure.

Standard Object-Orientated Programming Inheritance

In this example, I would have used the standard OOP class hierarchy to design my entities. you would have a base class Entity which would be extended by Player, Sign, Skeleton, you get the idea. The upside of this is that it's simple and fast to come up with - until you start wanting to share functionality in two derivatives. Ruby does not have multiple inheritance, so it gets messy when I want an Enderdragon entity to share some functionality with a Sheep. It also limits functionality unless you do a lot of code duplication at runtime if you want to, say, make a Villager controllable by a Player.

Modules and Inheritance

This approach would use modules to share functionality and have it all defined at compile-time. We would have modules such as Positionable, Orirentable, Nameable and a base Entity class would define all of these at compile time in derivatives (for want of a better word - no compiling in Ruby). The advantage of this is that it's more 'clear' to other programmers. Player would have Positionable and RemoteControllable whereas a Sheep might have Positionable. These subclasses are all designed at compile time.

Modules and Meta-Programming

Finally, this one would utilize the Factory (or Builder) pattern to have a similar approach to Modules and Inheritance, except this time, it would define the mixins at runtime. Instances of Entity would be re-opened at runtime and have the appropriate modules mixed-in in their factory. This has the benefit of huge flexibility at the cost of maybe perhaps slightly violating POLA.

I'm currently siding on the side of meta-programming. What do you think?

module Entity
  # The Id of the Entity
  attr_accessor :id

  def to_s
    "Entity <ID:#{id}>"
  end
end

module Positionable
  attr_accessor :x, :y, :z
end

module Orientable
  attr_accessor :yaw, :pitch
end

module Nameable
  attr_accessor :name
end

entity = Entity.new
entity.extend(Positionable)
entity.extend(Nameable)
entity.extend(Orientable)

entity.x # => 0f
\$\endgroup\$
  • \$\begingroup\$ Forget my previous comment -- I didn't read the question carefuly and thought you wanted to build the map in OOP, that would be nonsense. \$\endgroup\$ – Nakilon Mar 10 '14 at 14:59
  • \$\begingroup\$ for the love all that is holy, do not take approach #1 \$\endgroup\$ – Jonah Mar 11 '14 at 19:17
4
\$\begingroup\$

I would prefer your second plan, with inheritence and mixin modules. I'll restate their different uses, which I am sure you know:

  • Use inheritance to model relationships between objects (B is a A).
  • Use mixins to model behavior (C logs, D remembers its position).

Unless you have a need to dynamically add mixins, apply YAGNI and don't do it yet. Wait until you have a compelling need for dynamic mixins, and use them only where needed. Metaprogramming is a power tool, but it does make the code harder to follow. Use it judiciously.

\$\endgroup\$
  • 1
    \$\begingroup\$ Well stated, Wayne. I'd add that metaprogramming can be especially hard to follow when there is an obvious simpler solution, for it evokes the IMBMS ('I must be missing something') reaction on the part of the reader. \$\endgroup\$ – Cary Swoveland Mar 12 '14 at 5:15
  • 1
    \$\begingroup\$ @CarySwoveland FYI, re IMBMS, BTDT. \$\endgroup\$ – Wayne Conrad Mar 12 '14 at 15:10
  • 1
    \$\begingroup\$ I've concluded that "BT" probably means "been there" or "bacon tomato", but I'm still working on "DT". Don't tell me--I want to work it out myself. \$\endgroup\$ – Cary Swoveland Mar 12 '14 at 18:58
3
\$\begingroup\$

One of my hobbies is woodworking. When I'm about to embark on a new project, I have a general idea of how I'm going to do it, just based on experience. I almost always begin by working up plans with Sketchup, which includes the materials I'll be using for each part. I then give considerable thought to which individual boards I'll use for which parts (e.g., to emphasize, downplay or match grain, and minimize waste), and the order in which I'll be performing various operations. For example, I know I'll be jointing, planing and sawing to square up stock, and want to do that efficiently, to save time. At that time I don't give much thought to the tools I'll be using, because I know the choice will generally be obvious, and when it is not, I can decide that later. One thing I never do, however, is ask myself whether I should be using power tools or hand tools for a project, for I know I'll be using both, whatever tool works best for each operation.

I expect most people take a similar approach to coding, which leads me wonder whether your question can or should be answered. I am not questioning its merit; if nothing more, it is good food for thought.

With regard to metaprogramming, although we all know what it is,.. Stop there. We don't, or at least I don't. We probably can agree that anything having to do with singleton classes probably falls under metaprogramming, but what about hooks like included and extended, method_missing, alias_method, even lowly send, and more? Moreover, while some uses of metaprogramming are truly mind-bending, others are dead-simple (e.g., using a class instance variable instead of a class variable). So what criteria could possibly be employed to decide whether a metaprogramming approach should be taken to a particular problem or a class of programs?

One knock against metaprogramming is that it makes it difficult for other coders to understand what's going on, and therefore should be used sparingly. I would tend to agree when the metaprogramming is both complex and unnecessary. Only 'tend' because life is complex. Forget not our responsibility to teach. Sometimes that means doing things that cause others to struggle. Let's say you've done some metaprogramming that has been given to me to maintain. Knowing little about metaprogramming, it takes me awhile to figure out what you've done, more time that it would have taken had you used a less sophisticated (ww?) approach. But then I have an 'aha' moment and think, "now that's cool, very cool indeed", and realize I could apply the same approach to another project I'm working on, allowing me to save a huge amount of time, and impress my co-workers in the bargain.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.