Saturday, August 2, 2008

Ownership, Aggregation, and Composition

Object Oriented Programming has a lot of associated jargon. You don't need to know the jargon to be a good architect, but understanding the concepts is important.

Short Form: composition is when class A owns an instance of class B; aggregation is when class A contains a pointer to class B (ie as a member variable), but doesn't own it. (Ownership implies that when the owner is destroyed, it will delete its ownees.)

Problem

There's lots of other sites that explain these things. Do they do a good job? I don't think so. I think most of them are targeted at either someone that knows nothing about OO, or someone that knows it fairly well but just wants a refresher. Like me. What's the difference between aggregation and composition again? Oh, composition is when you own the object, yeah, ok... If you aren't familiar with these concepts, then go read up on OO. I'm assuming that you've run across them before, and most likely distinguish between these two when you're writing code.

It's the sort of thing that's almost automatic, if you've learned to avoid pitfalls. Ownership is an important concept because of memory leaks. In C++, you've also got to be wary of dangling pointers and double-deletion. Back (in the olden days) when I was writing C++, I was always very careful to mark which class owned a given object.

In complex projects (especially games) you often have a ton of different objects which each have to reference the same thing. Bullets are fired by players, need to be drawn, create visual effects, collide with world geometry, etc. And in order to optimize several subsystems -- graphics and physics being two time-intensive subsystems -- you might have arcane container types. So how do you keep everything in order?

Solution

The answer is explicit ownership. Make it clear, to yourself and to whomever else is using the code. Put it in comments in the code, probably at the variable declaration and also in the header file for the contained class. If you scribble a diagram up on your whiteboard, make sure you're marking ownership clearly. Have only one owner.

Stay away from two owners. Good architectures would be having one clear owner, or having many owners -- like five, or twenty. In the latter case, you'd use reference counting or some other scheme to control deletion, such as using a globally-accessible (ie Singleton) container or manager or cache for those objects.

Sometimes the problem is something like this: bullets belong to players, but are aggregated within some class in the graphics system. But then a player logs out, the player object gets deleted, and now the graphics system has an empty pointer. Do you reference count this object? Do you keep the player object around (even after the player has logged) until all associated objects are deleted? This latter solution is why you often see players still in the game after their connection has died -- it's not so much an issue of the game not noticing the player has disconnected as the code doesn't want to delete the object yet.

Usually the cleanest solutions I've found to these my-two-dads sort of problems comes from thinking laterally. Do something completely different with bullets. Maybe a custom "reference count" used only by those two systems (although I think this is a hacky solution). Maybe bullets aren't owned by players at all, but contain a reference to a player ID allowing whatever system deals with the bullets to back-track to the player without having to worry about the player object still being around.

Jargon

The are two reasons to learn standard OO concepts. One is to help you become a better programmer. The other is so that you can communicate these concepts to other programmers. For any given concept that you learn, it's important to have a tag, a name, a label by which to refer to that concept. Having to say "that thing where you own this object that then has a pointer to a base class" is messy, when you could just say "bridge" -- which also captures a bit of intention, as well as architecture.

Say someone comes up to you and asks you what the difference between Aggregation and Containment is. Do you know? Can you explain it clearly? Probably only if you use those names a lot. I think ownership is an important concept and I use that word a lot in my code -- but I don't say // this object is aggregated or // contained. That would be lame.

I know the difference between the two. I don't think I'd be able to explain which one is which next week. Maybe, because I spent the time to write a post about it -- but that would be why, not because I use those words all the time. I think they're an awkward way to look at a more fundamental issue -- ownership.

No comments: