Stackoverflowers!
I’ve recently begun dabbling in Ruby, and I have a few questions. First some background: I’m used to C++ and Java inheritance, and have always had a dislike for how inheritance works there in many game-related scenarios (say, a roguelike, for example). Mainly due to it being hard to encapsulate behaviour in a good way. For examples, in Java: (not that I’d implement it this way, but it was the best example I could come up with on short notice.)
Actor extends GameObject
Creature extends Actor
FlyingCreature extends Creature
Dragon extends FlyingCreature
Humanoid extends Creature
…so far so good, right? But what if I decided to implement a class “Gargoyle” (winged humanoid)? Then it would belong to both FlyingCreature and Humanoid — so I’d have to encapsulate one of the traits and then refactor it or something.
The ideal solution — personally — would be if there were some effective way to extract the unique attributes/functionality into encapsulated objects, and then have every GameObject be a composition of different modularized components that can interact with each other. (With no inheritance besides eventualities such as Item:GameObject, Actor:GameObject, Tile:GameObject — if even then).
So that a specific GameObject (say, an orc) can be easily created by kinda creating a tree of components — something crudely like this but more flexible:
[Actor]
[Organic]/ | \[Persona] // Name, etc..
| \
[Humanoid] [Intelligence] // AI or PlayerController
|
[Inventory] // [ArrayList of items...]
|
[Equipment] // [Hash of :slot -> Item]
But more to the point: As I started learning about Ruby, I learned about it’s meta-programming abilities, it’s runtime freedom due to the language’s reflection, and the extend/include capabilities of modules. So I figured that if I divided specific traits into modules (and made sure to have careful naming policies and such), then I could allow my classes to include behaviour according to context and events. For example, let’s say I have a dwarf — named Bob O’Copp — and he trips on a tiny vampire piglet while spelunking in some dark sewer dungeon. And thus he falls into some magichemical puddle that mutates him — giving him cute fairy wings. But since dwarves aren’t normally avian, his class wouldn’t have the functionality of flight. But for this instance, it would be a valid trait. Thus, I figured that “bob.extend Flight” would be an excellent solution to the issue.
But what if one of the traits is temporal? Let’s say Bob O’Copp finds a scroll that summons magical wings on his back for the duration of the spell… But. After a lot of googling I’ve yet to find any good information as to how to retract/un-extend nor exclude mixins after it’s been applied — if at all practically possible. And since there’s a lot of really smart and experienced programmers here, so I’d like to formally implore you to bestow some of your magnificent insight, O mighty bearded oracles! 🙂
So… a few questions:
- Am I delusional to want to use mixins this way?
- Is there some big drawback I haven’t thought of?
- Is it at all possible to retract a mixin once applied?
- Do you have any other recommendations for a good way to deal with the issue? (giving me a direction in the form of some keywords would sufficient)
Eagerly awaiting any replies with anticipation
Great thanks in advance,
-Robocopulate
Am I delusional to want to use mixins this way?
I don’t think that you are delusional. Temporary mixins sounds like a neat way to describe your goals.
Is there some big drawback I haven’t thought of?
Not that is obvious to me after a short bit of reflection, except (see next question)…
Is it all possible to retract a mixin once applied?
Not in unmodified Ruby. There are various proposals for adding
unextendor similar, but none have yet been added to the official language.However, there is the Remix library, which gives you both
unextendandtemp_includeand more. If you’re going to run somewhere that Remix is available, you can use that.Do you have any other recommendations for a good way to deal with the issue?
You could always roll your own inheritance system. 😐 For example, create module functions that accept an instance to operate upon (possibly using
instance_evalto violate its encapsulation and store custom data) and then keep track of whether or not a particular module is available.