Refactoring Part 2: Basics / by Jeremy Ernst

I didn’t mean for two months to pass between these posts, but c’est la vie. The last post went over some high level concepts of refactoring. In this post, I’ll start to show how the concepts are being applied to ARTv2. Let’s start with the base component class. This is an abstract base class that all components inherit from.

Abstract classes may not be instantiated, and require subclasses to provide implementations for the abstract methods

The original (currently available) version of ARTv2, the base class was huge. It did way too much and was too cumbersome to sort through and debug issues. One of the goals for the refactor was to do a better job simplifying classes and their responsibilities. Below is the current state of the base class.

new_base_class.PNG

The base class contains the bare minimum amount of common functions and a few necessary properties. Properties are being used to handle lots of functionality when modifying aspects of a component. In the previous post, I mentioned how many ways I had implemented setting a parent of a module. This is now done via a property on the abstract base class.

For those that don’t know about properties, they’re essentially class attributes that contain functionality. There are plenty of good articles out there explaining them, like this one. Take for example, the property parent. If I want to know a component’s parent bone, I can call inst.parent, which will use the getter function of the property decorator to return the parent bone. This functionality of how that info is returned in defined in the property, like this:

This is just returning the attribute value on the metanode (more on that later). If I want to set or change the parent of this component, I can do inst.parent = “new_bone”. This will call on the setter of the property, which contains a little more functionality.

Compared to how I was doing this before, this is a significantly cleaner way to handle getting and setting the parent bone of a component. You may notice the setter calls on some extra functionality. This line in particular is of interest:

In order to separate out responsibilities, I’ve been using composition.

Composition means that an object knows another object, and explicitly delegates some tasks to it.

At the beginning of the base component class, the following code is executed:

The last two lines are an example of composition. An instance of a class is assigned to a class attribute, which then delegates functionality to that class. So rather than include all the joint mover functionality in the base class, it gets separated out into its own class that only handles joint mover functions. Then the base class can call upon that JointMover class to execute functions related to joint movers. (In this case, adding the joint mover for this component to the scene). An important thing to note here, is that the ART_Component knows about JointMover, but JointMover does not need to know anything about ART_Component. It is given all the information it needs on instantiation (which is the joint mover maya file and the metanode that contains all the metadata it needs).

To finish this post, I’ll talk about the metadata/metanodes. While the current version of the tools utilizes these, it does not nearly utilize them enough. Probably because I didn’t fully grasp how to properly utilize them. First, they (in my refactored implementation) are a huge part of the component’s class. Any information the class returns when asked is going to be pulled off the metanode. Anytime data is changed, it is on the metanode. The properties mentioned earlier, are essentially getting and setting metanode data as well as doing the extra needed functionality.

For example, when setting a parent for a component, one of the first things it does if the parent is valid, is set that data on the metanode.

When returning the parent, it returns the data from the metanode. Why does this matter? Well, the biggest reason is that it makes it incredibly easy to make an instance of a component to get access to its functionality when you have a way of supplying all of the information an instance of the class would need.

In the ARTv2 beta, I actually do not have a great way of getting instances of classes to access functionality. If I want to call on a component’s buildRig method, I do all this extra work to build up an instance of that class in order to do so. Now, a component can be instantiated with a metanode, which it will then use to populate its properties.

Furthermore, everything can be done via a command line now. Embarrassingly, this was not the case in ARTv2 beta. So much of the functionality was only accessible through the user interface. Here is an example of some of these concepts in action:

Creating a root and leg, and setting some properties on the leg.

Creating a root and leg, and setting some properties on the leg.

Accessing an instance of a component by passing in its metanode.

Accessing an instance of a component by passing in its metanode.

One thing you might notice is that proxy geometry is gone. More on that next time!