Custom Object Model

October 9th, 2006 by Bernard Lebel - Viewed 17191 times -




ABSTRACT

This article discusses some aspects of object model programming and also serves as a tutorial to design your own code to look and behave like the XSI Object Model. I will talk about Python, classic classes, operator overloading, and code design. I will re-implement at a part of the XSI Object Model in Python.

INTRODUCTION

For some time I wondered how exactly is the XSI Object Model working under the hood. More precisely, I was not interested in the details of the C++ implementation, but rather how I could design my own code and data structures in Python so I could “navigate” these data structures the same way I navigate the XSI OM. For example, I wanted the ability to do the same thing as Object.Properties("Property Name").Parameters("Parameter Name").Value, but with my own data structures instead of XSI’s.

The only problem is that I didn’t really have a need for this, it was merely curiosity. I was thinking “maybe some day…”. But recently, while working on a rather large scripting project, I ran into a wall. I felt that it could be the right time to experiment with the idea. I have this library of scripts that use a significant amount of constants from the same file. Over time, that file had become a huge mess: highly fragmented, disorganized, very long. Thus, hard to maintain. Beyond a major overhaul, I wanted to hide all the data (many lists and dictionaries) and have a really simple, clear and clean way to access it.

After a bit experimentation, I have found some ways to accomplish this, which I shall present in this article. Keep in mind this is by no mean an exhaustive study, I don’t intend to present the guide to replicate all the features and behaviors of the XSI Object Model.

WHY PROGRAMMING OBJECT MODEL

The decision to implement the object model in my code is entirely personal. In my experimentation, my goals were simplicity, clarity and readability, nothing else. These goals were driven by a need to organize and sanitize a part of my script libraries. As of this writing I have not made any measurement of memory and resource usage as well as performance.

1. DISCUSSION: DATA VS INTERFACE

Very early in the experimentation process I realized that the XSI Object Model is in fact only a user-friendly programming interface. However, I started with the assumption that the XSI scene and the data it holds was some sort of giant data structure that was organized exactly as the Object Model hierarchy.

If you look at some old XSI SDK documentation (like in XSI 3.5), the object model hierarchy is presented with diagrams made of red blocks. From such diagrams, one could be led to believe that each object, each part of that hierarchy, is actually holding fractions of the data of the scene. Also, the fact that the Object Model closely resembles how the scene data is structured in the Explorer view can only strengthen this belief.

It turned out that the exact structure of the data, when navigated through the Object Model, is highly irrelevant for the user. It could be arrays, dictionaries/hash tables, strings, classes and instances, whatever. The only thing that matters is to have structured data, and preferably consistently structured. From that point, one can build an object model interface to read and write this data.

But this is the XSI Object Model. In the XSI SDK, the “raw” data structures are hidden from the user. From scripting, you either use commands or the Object Model to work with it. But now I’m creating my own Object Model, with the data structures behind it, and I decide how the two work together.

In fact, designing the data structures would be the first thing to do. I see two main designs for data structures:

Data as member of classes
This design is one of the principles of object-oriented programming. Data and operations are both members of the classes they relate to. Methods that read or write the class data don’t need to lookup other name spaces than their own local scope. Each class has its own predefined data. We can talk of an homogeneous design.

Separate data
This design goes against the previous one. Here, the data-structures are kept completely separate from the classes. The programmer defines “rules” for the class and its methods so that they read and write data at the right location. We can talk of an heterogeneous design.

In the project I have worked on and will discuss later, I chose the second design rather than the first one. The reason is that I wanted all the data to be consolidated in one place (the top of the file). Because it is not scattered across many classes and without any class decorations, I can look at it quickly. It also allows classes to be kept compact, which enables me to look at these quickly. In this particular case, I find this design clearer and easier to maintain.

So how are the data structures designed exactly? Are they lists, tuples, dictionaries, or else? In my case, I used a combination of lists and dictionaries. In fact, for every dictionary there is a list. Dictionaries hold the values, and lists are there to sort the dictionaries (in Python, unfortunately, a dictionary cannot be arbitrarily sorted).

Now that I have talked about the data structures, let’s talk about the interface.

Since the data is completely separate from the interface (the classes), and that this data is exposed through dictionaries and lists, why bother at all with making an interface? Isn’t using “traditional” means of reading dictionaries and lists sufficient?

Indeed, using typical dictionary lookups and list indexing is perfectly acceptable:

1
print dGRANULES['vertexcolor']['clusterchannels']

or, through iteration:

1
2
3
4
for sGranuleType in dGRANULES:
    print sGranuleType
    for sClusterPropChannel in dGRANULES[sGranuleType]['clusterchannels']:
        print sClusterPropChannel

There is nothing wrong this this. However it may become convoluted with deeply nested dictionaries and lists. For example, one of my classes has this somewhat deep data structure that causes the code using using it to look like this:

1
self.dClusterProperties[sPropertyType][oObject.fullname][0].add(oProperty)

At this point, readability and clarity are starting to take a hit. Such a line of code is subject to misinterpretation. In other words, scripting errors, even for me. The data structure must be strongly documented (a data graph is hardly avoidable), and the coder can spend considerable time reading that graph. Such a graph might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"""
dictionary
    { cluster type (string) :
        property-file map (dictionary) {
            parent3dobject.fullname (string) : [
                XSICollection( cluster props ),
                asset internal ID
            ]
            parent3dobject.fullname (string) : [
                XSICollection( cluster props ),
                asset internal ID
            ]
            ...
        }
    }
"""

(I don’t recommend using such documentation ethics, this is just the kind of doc string I put in my code to help me program fairly any deep/complex data structures)

Also, maintaining scripts that access that kind of deep data structure with traditional means can prove problematic if the data structure design is modified.

Ideally, getting back to the first example, something like this could be preferable:

1
print data.granules('vertexcolor').clusterchannels

or, through iteration:

1
2
3
for oGranule in data.granules:
    for sClusterPropChannel in oGranule.clusterchannels:
        print sClusterPropChannel

Not especially shorter, but a lot clearer. Such style may prove useful in the context of deep data structures, as in the second example:

1
self.clusterproperties(sPropertyType).objectnames(oObject.fullname).properties.add(oProperty)

Again, not significantly shorter, but a lot easier to read, understand, and maintain. The big advantage is that while the programmer makes modifications to the underlying data structures, the top-level interface can hide those changes. Of course it’s not always as easy as it sound, but it least it gives the programmer more chances.

Pages: 1 2 3

Comments are closed.