View Full Version : Inheritance
ShannonA
09-24-2001, 02:36 PM
Discuss Richard Bartle's discussion of inheritance.
Is there a confusion here between the concept of a "class" and an "object"? Bartle makes the comment that if something has 0 things inheriting, then it's an object.
What seems to be missing IMHO is the distinction between classes (the idea) and instances (the real thing). I'm not sure I see these interacting in Bartle's discussion.
And what is the value of "instance inheritance" vs "class inheritance". As an example, my understanding is that Skotos' Marrach engine does "instance inheritance" - you have an UrObject that the children copy. This object is not a template (class) that instances instantiate from, but is instead itself an instance that other instances (normal objects) defer to if they don't override it.
I wouldn't mind seeing some more discussion of the pros and cons of each approach.
Thanks.
Originally posted by Nom
As an example, my understanding is that Skotos' Marrach engine does "instance inheritance" - you have an UrObject that the children copy. This object is not a template (class) that instances instantiate from, but is instead itself an instance that other instances (normal objects) defer to if they don't override it.
From what I can gather, from a technical point of view, the difference is really moot unless you get into such goodies as multiple inheritence.
After all, whether you have a template that instances come from, or simply an object that the new instances behavior defaults to, is there really a difference? Especially in this environment where ordinary users don't get to run around with UrSwords anyway. In either case, the behaviours of the new object defaults to those defined in the previous class or object.
From what I can gather, from a technical point of view, the difference is really moot unless you get into such goodies as multiple inheritence.
Yes, it is largely moot, and largely semantic.
It is my experience that the object oriented paradigm works best when let to live in loose symbiosis with one's intuition, and that it is counter-productive to try to pin down the definitions too hard. For this reason and others I am going to avoid a step-by-step commentary on Dr. Bartle's articles.
That said, it makes good sense to comment on what our engine does do, if for no other reason than to provide a bridge between Dr. Bartle's examples and what you folks experience out in the game.
The first difficulty here is the multi-layered nature of our engine. Our code base is written in LPC, which is an interpreted, object-oriented dialect of C. There are classes in our system and there are instances, all of which are called objects (see, already we have confusion).
However, that object orientation is not exported to the level you see in the game: internally, all our physical objects are instances of precisely the same class. There is no 'code' in a sword that does not also exist in a flower. We decided very early on to make our virtual world completely data driven so that an object may polymorph at will with nothing but internal state changes. A flower should be a weapon, albeit a rather soggy and ineffectual one. A sword should have a smell.
Our command parser is deliberately centralized, as is our set of possible physical actions: if a verb works in one place of the game, it works in another. If there is such a thing as a torch in the game, then all objects should have the option of bursting into flame. I say should, because we don't actually have torches, and this is an ideal that is occasionally difficult to live up to!
So we don't really use sub-classing and inheritance and other such object oriented goodies -- not code-wise, not so you can see it. However, as Shannon has written previously, we do have dataspace inheritance, which is a very different matter but ends up being remarkably similar.
Thus Dr. Bartle's SMALLPLANT/HERB/PARSLEY example works quite well in regards to the Skotos engine: The SMALLPLANT UrObject would perhaps have some identifying piece of data that the goat sensed; the HERB one would inherit this data and add to it a tailored response to being picked, etc; the difference is merely in how the engine asks an object for a response to a physical action.
The meager point I am leading up to is this: for these simple examples, dataspace and programmatic inheritance turn out to work similarly, and the similarity does extend somewhat into discussions on multiple inheritance too -- but there are substantial differences, and I will try to keep up with Dr. Bartle's articles explaining how it works in our code.
I'll also try not to write the next one quite so late; this is barely coherent: I apologize.
Zell
Richard
10-09-2001, 02:03 AM
Originally posted by Nom
Is there a confusion here between the concept of a "class" and an "object"? Bartle makes the comment that if something has 0 things inheriting, then it's an object.
What would you call it, then?
What seems to be missing IMHO is the distinction between classes (the idea) and instances (the real thing).
There is such a distinction: classes have children and instances don't. There need be no formalise instances as being something of an entirely different nature to classes, because they're not!
It's how you use things that's important. If you only ever use the leaves of the inheritance hierarchy when you need objects, that's all you ever get. If you WANT people to be able to manipulate general concepts, though, you nevertheless can (as classes can be treated as objects if you insert them in the game where players can see them).
And what is the value of "instance inheritance" vs "class inheritance". As an example, my understanding is that Skotos' Marrach engine does "instance inheritance" - you have an UrObject that the children copy.
I don't know the ins and outs of the Marrach engine, but if there's a separation between inheritance from instances and from classes then it would suggest that instances are palpably different from classes (ie. implemented using different data structures). Why this is so, I haven't the faintest idea - I'd guess that it was to do with some kind of efficiency concern.
Basically, though, you can abstract out all the differences between classes and instances of classes ("objects") and you end up with the instances being the leaves of the class hierarchy.
Richard
Thanks Richard for taking the time to reply.
I agree that we are dealing with a grey area, here. In a laguage such as C++ or Java, a "class" is a set of rules that defines how an object will behave, while an "object" is a collection of data that behaves according to those rules.
{Discussion of background - sorry to those who know all this}
To use a simple example:
The Weapon class describes functionality and attributes common to all weapons.
A Sword is a weapon, and inherits the functionality of weapons. However, Sword is also a class. It is the concept of a sword, not the sword itself.
You can only start manipulating things when a sword "object" is created, which obeys the rules for the concept of a sword.
This is class driven inheritance.
Things get murkier when we can dynamically add functionality and new attributes to existing objects. Suddenly, the clear line between concept and thing is gone.
Weapon is no longer a class; it is a "typical" (as in "type") object. A Sword is no longer a Weapon (since one phyiscal object cannot actually be another); instead, it behaves like Weapon unless it's attributes say differently. And individual swords are not Swords, either, they are "like-Swords".
This is object driven inheritance (or dataspace inheritance, as Zell puts it).
Class inheritance:
Specification->Specification->Specification->Specification => Thing
X -> Y: Y is an X (but more specific)
X => Z: Z is an object according to specification X.
Dataspace inheritance:
Example->Example->Example->Example->Thing
X -> Y: Y mimics (is like) X
{end intro}
Dataspace inheritance has some useful semantic advantages. In class inheritance, is "blue" the value of the colour attribute or an attribute in it's own right? Should a Broadsword be a Sword named "Broadsword" or an instance of the Broadsword sub-class of sword? These are less significant in dataspace inheritance.
On the other hand, every instance has to be given all the rights of a first-class object. In class inheritance, if I want twenty Swords, I just instantiate twenty objects of type Sword. In dataspace inheritance, I create twenty new classes, each of which has Sword as a parent.
Industrial systems usually go with a class-based approach, while MUD-style games tend in practice to use dataspace inheritance. From a purely technical standpoint, I am quite interested in a discussion of what would be easier or harder under each approach.
Which comes to the answer the confusion between Richard and myself about classes and objects, and it has to do with the step of how we get to the final object.
Is there a confusion here between the concept of a "class" and an "object"? Bartle makes the comment that if something has 0 things inheriting, then it's an object.
What would you call it, then?
What seems to be missing IMHO is the distinction between classes (the idea) and instances (the real thing).
There is such a distinction: classes have children and instances don't. There need be no formalise instances as being something of an entirely different nature to classes, because they're not!
In class-based inheritance, there is a final instantion step to make an object that is fundamentally different to the specification step.
Broadsword is a class. I create broadswords (objects) by instantiating Broadsword.
Richard's Flaming Broadsword is a class (that extends Broadsword). I create "Richard's Flaming Broadsword" (the object) by instantiating Richard's Flaming Broadsword.
The final objects are not classes. They cannot be further extended or redefined.
In dataspace inheritance, there is no difference between instantiation and inheritance.
Broadsword is an object (UrBroadsword, in Marrach-speak). If a player needs a generic broadsword, I create a new object that mimics Broadsword without redefining anything.
Richard's Flaming Broadsword is an object that mimics Broadsword, and then redefines some of its functionality. The concept and the object are one and the same.
Sorry for the long post, but I hope that clears things up somewhat, and explains why I see the distinction between class-based and dataspace inheritance as interesting.
"Nom"
Atama
10-09-2001, 10:19 PM
You can also have a Flaming Broadsword which inherits from urSword, and not Broadsword. I would inherit from broadsword for consistency though. The Marrach engine is really flexible. The bad thing about flexibility is that if you aren't careful you can end up with a pretzel. :)
Richard
10-10-2001, 01:28 AM
Originally posted by Nom
In a laguage such as C++ or Java, a "class" is a set of rules that defines how an object will behave, while an "object" is a collection of data that behaves according to those rules.
This makes a distinction between the objects and the rules that govern them. Now while there obviously IS a difference, it doesn't necessarily have anything to do with inheritance, nor does it necessarily have anything to do classes.
I'm using a more generic definition for classes and objects. A class is a concept which is subdivided into other classes that have an "is a kind of" relationship with them. A class's subclasses are more specific and its superclasses are more general. Objects are those classes that are so specific they only refer to one physical thing, and therefore they have no subclasses of their own.
Weapon is no longer a class; it is a "typical" (as in "type") object. A Sword is no longer a Weapon (since one phyiscal object cannot actually be another); instead, it behaves like Weapon unless it's attributes say differently. And individual swords are not Swords, either, they are "like-Swords".
What does this kind of inheritance buy you that class inheritance doesn't? You say it gives better semantics, but that's only because the separation of classes and instances means that class inheritance can't deliver when really it ought to be able to. The separation of class and instance is the core of the problem; implementing two forms of inheritance is an attempt to get round it that wouldn't be necessary if classes and their instances weren't treated as completely separate entities.
The system I am describing is only one system, albeit a very clean and flexible one. There are other ways to do it, each with their pros and cons. The reason I'm adopting this particular one will become more apparent in later articles, but essentially I like it because it makes a distinction between rules and what the rules are about.
All entities in the inheritance hierarchy are automatically instantiated when the program starts. This includes elements higher up the hierarchy, as well as the ones at the bottom. I call these "atoms". These are NOT sets of rules in the sense that C++ classes are; they're just nodes in a hierarchy.
Separate to these are functions (or properties). Functions take atoms as parameters, and they work as the "rules" part of what in C++ would be called a "class".
Functions can be applied to any entity in the hierarchy. Thus, if you give the atom for SWORD a DAMAGE property of 15, then anything immediately below SWORD in the atom hierarchy which doesn't have a definition for DAMAGE itself will inherit SWORD's. Its descendents will, too, until any point where one of them defines DAMAGE for itself.
In this sense, everything in the atom hierarchy is an object (because they are all instantiated). However, only the ones at the tips are actually present in the game world as things that the world can physically do things to. The game CAN do things to the others - it can change the meaning of a class dynamically, because a class is just an atom like an object. It doesn't want to have them present in the game world, though, unless making some kind of metaphysical point.
Dataspace inheritance has some useful semantic advantages. In class inheritance, is "blue" the value of the colour attribute or an attribute in it's own right?
Is there a reason it can't be both?
Should a Broadsword be a Sword named "Broadsword" or an instance of the Broadsword sub-class of sword?
If you have no formal distinction between "instance" and "class" then the problem doesn't arise. BROADSWORD is an atom below SWORD in the atom hierarchy. If there's only one of it, you can use BROADSWORD directly; if there are several "instances" of it then you create atoms for BROADSWORD0, BROADSWORD1 etc. and use those (by "use" I mean you can place them in the game world).
In class-based inheritance, there is a final instantion step to make an object that is fundamentally different to the specification step.
That's an implementation issue. It doesn't HAVE to be fundamentally different to the specification step.
When classes are specified, data structures are formed to represent those classes. In C++, these are only needed at compile time because the language is so constructed that the by run-time any inheritance involved is known in advance. Anything involving dynamic inheritance, however (eg. adding new rules to classes at run-time - something we want to do in MUDs), requires that the run-time system be aware of the class. This means it needs a data structure.
Now if you're going to need a data structure for classes and a data structure for class instances, the question arises as to whether you use different data structures or identical ones. That's an implementation issue. If you choose separate structures it may run faster but you could lose some expressiveness, and vice versa for choosing identical structures.
I think the system I'm describing tends towards the "dataspace inheritance" approach. I make everything an instantiation, but only actually use the leaves of the hierarchy in the game itself. Rules sets are thus tied to concrete atoms, rather than to abstract classes. However, all but the tips of the hierarchy are treated by the game itself as representing abstract concepts.
In summary, then, as I'm using the terms in my articles: classes are concrete representations of abstract concepts; objects are concrete representations of concrete objects.
Richard
I think the system I'm describing tends towards the "dataspace inheritance" approach.
And in the process you have explained the advantages (semantic and implementation) to using such in a MUD-style environment.
Thanks. I await future articles with much interest.
joelouis
11-13-2003, 02:23 AM
hello,
i've discovered this set of articles and they're brilliant. The fact is that I've recently re-discovered old Infocom games and googling for interpreters (I always need to know how things work) and Brian Moriarty, I've landed on the Skotos site.
I'll soon post to this forum to clarify a few things :confused: and will be happy to discuss with you !
What I understand about inheritance as described in the articles, is that classes work as "singletons" or, expressed similarly, that the data & methods are "static".
Therefore a Sword is both a class and an object. The notion of terminal class disappears. You can/could use this Sword as an object (in the game) and you could also have SmallSword inherit from it.
This is consistent with the 1 game object = 1 class (Sword1, Sword2...)
Thank you for reading.
mikedsc
11-15-2003, 11:35 PM
*innocently* I think someone should go and drag Dr. Bartle back here to write more on this subject. This was the column that originally brought me to Skotos, and I'd love to see more out of it.
Richard
11-17-2003, 06:13 AM
Originally posted by mikedsc
*innocently* I think someone should go and drag Dr. Bartle back here to write more on this subject. This was the column that originally brought me to Skotos, and I'd love to see more out of it.
The column is officially on hiatus, and I do intend to return. Indeed, I asked Shannon if it was OK to return after I finished my book, and he said yes. However, I have yet to think of anything on an engineering topic that I could write about. Sorry!
Richard
Lindahl
07-19-2004, 04:47 PM
Posted this in another place in respect to this article and a style of implementing "dataspace inheritance". Figured I'd post the link here.
Original discussion link:
http://www.mudconnect.com/discuss/discuss.cgi?mode=MSG&area=adv_code&message=10781
The point of the dataspace inheritance is to argue for dynamic behavior inheritance. Classes don't exist (except for possibly initial instantiation of objects). Objects contains a set of reactions as well as a set of data (hidden - and used to calculate reactions). One would only instaniate objects from other objects, instead of classes, which not only copies data, but behavior as well. Overriding behaviors can then be done dynamically, which is well-suited for AI and MUDs (where builders are often hindered by lack of flexibility in templating). Copying behaviors from another object simply means asking for it's reaction object which is mapped to the event type in question. This lends itself well to NPC learning.
Example implementation of "dataspace inheritance":
http://www.codealchemy.org/phpBB2/viewtopic.php?t=53
Note that this approach is NOT necessarily better than static behavior inheritance, i.e. C++ classes. It often leads to very under-structured objects which one can hardly assert anything about (unpredictable) - hell to debug, but interesting from an interaction point of view.
A hybrid method would be using C++ class methods to fall back on, inheriting from strict C++ classes, but using a sinlge reaction map object (perhaps a collision-based hash array with linked list support when the hash table fills) to override specific reactions. One must taken into account the back-end storage and typical coding style to determine what, if any, dataspace inheritance should be incorporated.
In reference to the article (#5) the tragedy of objects, this article describes what is commonly known as multimethod programming, a term coined by CLOS (Common Lisp Object System). There's plent of material out there on this style of programming if you search for the keyword multimethod and programming.
vBulletin® v3.7.1, Copyright ©2000-2008, Jelsoft Enterprises Ltd.