Tags:
Programming
Ruby on Rails
Number 8 was new information to me, and I had to think about it for a while. It says that almost everything in Ruby is a message. When you call a class method, that call is a message. The method and its arguments are the message, and the class (or instance of the class) is the destination of the message. The class and the message are not joined to each other in any special way. The message could have just as easily been directed at another class object. I now think of Ruby classes this way: Classes are written to receive messages. Is this only a distinction without a difference? I don't think so. I'll try to illustrate with an example. A while ago, I was working on a text-based MUD (multi-user dungeon, the ancestor to the modern MMORPG). I was philosophizing about where actions belonged in an object-oriented language. To unlock a door with a key in the game, how would the code be written? door.unlock(key)? key.unlock(door)? unlock(door,key)? use(door,key,unlock)? I argued against the first two examples, for reasons I can't remember. Looking at them side-by-side makes it clear that making one choice over the other may limit your design for any future features. But in practice, either one will probably work fine, so just make a choice. Now I'm thinking about it again in terms of Ruby's messages. What if a game action was implemented as a message that could be sent to any game object? Would that solve anything? Does it give a level of abstraction that opens up a game's design? Let's see. I made an example where we've got a "Creature" class, which represent any living thing, including the players, and a "Door" class, which is just a door. Then I implement an explosion in the room that is performed on everything in the room. Since explosions hurt, I will then apologize to everything in the room. I'm not going to create an "Explosion" class, or an "Apology" class. I'm not even going to make it more abstract by making "Attack" or "TextMessage" classes. I'll just make one class to encapsulate them all: ActionRecorder. The ActionRecorder is like a proxy. You record the "message" (ie, the explosion and apology) into the ActionRecorder, and then perform the action on any other class. This creates an instance of the action that can be reused, serialized, stored, logged, or whatever you want. A game action can have multiple effects, so the ActionRecorder will store each effect as a Ruby message. Enough blah blah blah, it's time to see some code. Here's the ActionRecorder, the most interesting part of the code (this is taken from the presentation): class ActionRecorderNote that method_missing is a built-in method that all Ruby classes have. It gets called automatically if you call a class for a method that it has not defined. More specifically, it catches the "NoMethodError" exception. Also, all Ruby classes have the send method, which receives all messages (aka, method invocations) and passes them to the methods. I assume that the send method is what throws the NoMethodError exception. So, the ActionRecorder is taking advantage of method_missing to record all messages that an ActionRecorder instance receives. I'm not sure how you would do this in Java without getting compiler errors. It's probably possible, but won't be as elegant as this Ruby code. Now let's create some game objects: class CreatureThis implementation is a bit sloppy. I would probably implement a "DamageEvents" module and apply it to those two classes, from which they would get the physical_damage and fire_damage methods, but whatever. Also, the method_missing method should not just ignore all illegal methods. There are probably cases where you will want to raise an exception if an object receives an illegal method message. But let's just go with it for the example. Now, how do you perform actions on the objects? Let's have some action! # ------- Test the classes ----------------------And here's the output: Start...I definitely like this way of coding this example! The code is written almost in english. Look at the definition of the explosion. It doesn't get much more readable than that! Also, "explosion.perform_on thing" is very easy to read. This is the kind of code that makes a programmer smile. I can see more ways that the ActionRecorder will facilitate new features. Here are some thoughts about how to extend this type of proxy class: - ActionRecorder can log all messages it receives, or send the messages to a Logger class that can choose which methods to log and which to ignore, in exactly the same way that we implemented the Creature and Door classes. - A similar recorder can be used to execute scripted effects. A ScriptRecorder class. So, if your game's content developers write scripts in Lua or some home-grown script language, you can read them in and record them as ScriptRecorder objects that can be applied to game objects. You can write a Lua script that defines the explosion, read it in to a ScriptRecorder, and you've got the implementation of that script done. (Actually, I would use Ruby as the scripting language, so nevermind...) - In-game chat messages that pass through a recorder (ChatRecorder?) could be parsed, logged, or blocked. Messages may need to go to some players, but not others. Whispers, guild chat, party chat, in-game languages, etc. - Convert in-game chat messages to telnet protocol strings before sending them over the network. What do you think? How would you take advantage of this? |
About Me
![]()
![]() ![]() |
Post a comment: