ID:194668
 
I've been working with a lot of hierarchical objects lately, and I just came to a conclusion that cost me a day of redoing a bunch of code:

For the most part, hierarchies should not be stored as hierarchies.

I've always assumed that if I had objects that were connected like so:

Father
child
child
stepchild
child

Then I should store them in the file that way, with the stepchild living several subdirectories down, and being restored when its parent wakes up.

However, now I'm thinking it is much more flexible and maintainable to store them flat, with references to each other, like so:

#1) Father (children = #2, #3, #5)
#2) child (no children)
#3) child (children = #4)
#4) stepchild (no children)
#5) child (no children)

This way, if you want to move the stepchild to belong to the father and make someone else the stepchild, it's simply a matter of switching a couple of references and you are done. No having to move directories around or anything like that.

Pretty obvious I suppose, but the first time I really had to work through the options myself. I did my usual approach: do it the wrong way, then understand the right way. Which is fun, believe it or not.
Uh-oh... Deadron... I never comprehend anything you write about coding the first time (well, or anything anyone else writes). Can you give us an example of how this could be used?

Z
In response to Zilal
On 6/1/00 1:06 pm Zilal wrote:
Uh-oh... Deadron... I never comprehend anything you write about coding the first time (well, or anything anyone else writes). Can you give us an example of how this could be used?

Z

Suppose that doesn't speak well for my former life as a Tech Writer! Well, come to think of it, I never understand what I write on these things either. But it makes sense while I'm writing it, I swear.

Here are the things I currently use object hierarchies (more formally known as object graphs) for:

A conferencing forum (like the one we're posting in now). The forum is constructed like so:

bbsystem (one one of these ever exists)
forum #1 ("Player forum")
forum #2 ("Guide forum")
conference #1 ("Bug reports")
conference #2 ("Building the world")
topic #1 ("Using images")
topic #2 ("Handling NPCs")
message #1 ("Zilal")
message #2 ("Deadron")

So the entire forum is kicked off and managed by the bbsystem object. When someone wants to enter a forum, the bbsystem looks at the forums it owns and tells the correct forum to display itself to the user.

The forum then puts together a browser page listing the conferences that are available. When the user chooses a conference, the forum tells the conference to display itself.

The conference puts together a page based on the topics it owns, etc...

The key here is that each level is handled by a unique class with its own behavior, and that each level only knows about its parent and its children. A bbsystem, for example, has no idea how to display a message. Only a topic knows about messages.

Of course, this whole thing has to be stored in a savefile, so the forums will persist between game sessions. This was my first hierarchical project, and I chose to store them in the savefile hierarchically (and probably would still do so if I started now, though I can't be sure).

So in the savefile, there is a bbsystem object stored, and it has a subdirectory containing the forums, and the forums have subdirectories containing conferences, etc.

My next hierarchical task was turfs. I basically have to bypass the Byond turf class (to my great chagrine, believe me) and use my own system in order to allow guides to dynamically create and place new kinds of turf while the game is running.

Well, any good turf system needs to allow for classes ("categories" is how they are presented to guides) and individual turf types ("definitions").

That way the guides can set up turf categories such as:

Turfs
Grass
Dark Grasses
Grass to Rock transitions
Walls
Brick walls
Log walls

Each turf category has some number of individual turf definitions, which are what guides actually choose to lay on the map. The turf definitions inherit some attributes from the category and have some attributes of their own. All "Brick walls", for example, display the same name to the user, which is probably "wall". On the other hand, each turf definition has its own setting (currently anyway) for density and opacity.

So given that this is a hierarchy, I started out using the same core classes I used for the forum discussed above, and therefore the Grass categories and definitions were stored in subdirectories of each other.

But this quickly fell apart.

Why? Well, in the forums, conferences and topics pretty much live where they live and are unlikely to move. But when you are building a hierarchy of turfs, you will regularly change your mind about what categories go where, which turfs belong to which categories, etc. So you need to move them around a lot.

I started thinking about the annoyance of moving a category to a new location: the category and all its children would have to delete itself in one location and write itself to the savefile in another location. Seems error prone and too heavyweight.

However, a different approach to storing the objects would make these operations simple.

So I created a turf_category_controller, which is responsible for storing and handing out turf categories. It stores all the categories in one directory by id, like so:

turf_categories/
ROOT/
name/"Turfs"
playerVisibleName/"ground"
subcategories/(#1, #2, #7)
turf_definitions/(#24, #87, #45)
#1
name/"Grass"
playerVisibleName/"grass"
subcategories/(#3, #4)
turf_definitions/(#88, #12, #17, #98)
#2
#3
#4

So the "subcategories" attribute maintains a list of things that belong to a category, and that's how the hierarchy is put together. If I wanted to move category #4 out of Grass and put it under the root, Turfs, it's a simple matter of telling the Grass category to remove #4 from its list, and telling the Turfs category to add #4 to its list, and bang the operation is done.

Whenever an object needs a particular category, it calls the turf_category_controller.CategoryByID("#4") method, and it doesn't matter where #4 lives in the hierarchy, it can be instantly found.

This way of storing and accessing the objects provides much more flexibility and is much simpler overall...

So that's the much longer explanation, although I'm suspicious I might not have really cleared anything up...

I think I'm developing an interesting set of classes as I work on this stuff, and if I can get them to a documentable point, I'm hoping to release them...
In response to Deadron
On 6/1/00 4:34 pm Deadron wrote:
On 6/1/00 1:06 pm Zilal wrote:
Uh-oh... Deadron... I never comprehend anything you
write about coding the first time (well, or anything
anyone else writes). Can you give us an example of how
this could be used?

Z

[snip]
Whenever an object needs a particular category, it calls
the turf_category_controller.CategoryByID("#4") method,
and it doesn't matter where #4 lives in the hierarchy, it
can be instantly found.
[snip]
This way of storing and accessing the objects provides
much more flexibility and is much simpler overall...
[snip]
So that's the much longer explanation, although I'm
suspicious I might not have really cleared anything up...


I understood most of what you were writing up until the end where you starting going into numbers and such. I'm assuming this is all based on effiecient ways of saving information into a save-file hierarchy.

=======================
Understandable:

A conferencing forum (like the one we're posting in now).
The forum is constructed like so:

bbsystem (one one of these ever exists)
forum #1 ("Player forum")
forum #2 ("Guide forum")
conference #1 ("Bug reports")
conference #2 ("Building the world")
topic #1 ("Using images")
topic #2 ("Handling NPCs")
message #1 ("Zilal")
message #2 ("Deadron")

Turfs
Grass
Dark Grasses
Grass to Rock transitions
Walls
Brick walls
Log walls


======================
Lost you:

turf_categories/
ROOT/
name/"Turfs"
playerVisibleName/"ground"
subcategories/(#1, #2, #7)
turf_definitions/(#24, #87, #45)
#1
name/"Grass"
playerVisibleName/"grass"
subcategories/(#3, #4)
turf_definitions/(#88, #12, #17, #98)
#2
#3
#4

I'm guessing that you have pre-defined something which exists as a main level instead of being the step-child of something. So...

obj/Ground
Grass
Green Grass
Brown Grass
Wall
Wood Wall
Stone Wall

exists instead as

obj/Green Grass
obj/Wood Wall

which contains properties which can be called by another object such as obj/Ground/Grass.

If so I think there are easier ways of doing it... which leads me to believe that I don't understand what you're writing :).