Tuesday, February 5, 2008

A System of Interactions

According to Newton's Third Law of Motion, our world is a system of continuously interacting objects. Essentially, whenever an action is taken by an object, hosts of other objects react, each in turn evoking more reactions ad infinitum; and thus, our world goes round. OK, I know what you're thinking. "Is a code-monkey really going to give me a lesson in classical physics?" The answer is, of course, no. I'm using Newton's Third Law as an analogy to the execution of an object-oriented application.

You see, procedural applications tend to execute in a manner easily defined in a decision tree; take this action, check a condition, take the action associated with that condition, check another condition, take another action, so on and so forth. I associate the simplest form of this type of execution almost directly with the world's most beloved pricing game from The Price Is Right, Plinko. In Plinko, one drops some input in at the top of the board, the Plinko chip, and it makes its way down a grid of pegs until it falls into one of a number of buckets determined by its path through the pegs. As the chip falls between any two pegs, it collides with another peg, forcing it to go either to the left or the right before it can fall again to the next peg where it will make another decision to go left or right. If one had the ability to observe the conditions of the falling chip (gravitational forces, rotation, etc.) at each peg, one could easily deduce the route the chip will take throught the pegs. Thus, enters the decision tree notion I mentioned early; based on the conditions surrounding any given input, a procedural application's execution is easily seen to be deterministic.

Because of the deterministic nature of procedural applications, this type of app doesn't lend itself to pluggability or extendability without modification. Not to say that every app should strive for this quality; I've certainly written many apps that didn't need to be extended (sometimes you just need a script; sometimes the number of use cases for the app is finite; there could be many reasons). However, it's undeniable that considering an app with this form of execution, a developer is more likely to create rigid dependencies on the deterministic nature thereof. For example, a developer modifying or extending some action F may assume that actions A, C and E have already been taken, programming in such a way that F is very dependent on A, C and E having done their work, and in the process, hampering F's ability to be reused when A, C and E have not all executed. For this reason, procedural applications can be difficult to extend or modify. In order to add a decision (or a whole new sub-tree) into the decision tree, a developer has to explicity know the addition's position in the tree; in a worst case scenario, where all the actions in the tree are tightly coupled to the actions taken before them, the logic for significant portions of the tree would have to be updated to take into account the addition.

Conversely, well-designed object-oriented applications execute in a manner remarkably similar to the system of objects existing in the physical reality of our world. Where objects in our reality monitor each other through physical media provided by the laws of physics, objects in OO apps monitor each other through some kind of event mechanism or messaging framework allowing both to react to actions taken in their space of existence.

Objects in the most loosely-coupled applications don't even need to be aware of each other, they could let the application (or some mechanism therein) do that for them; basically the application broadcasts the fact that some action has been taken and all those that want to react should do so now. A developer adding or modifying functionality need know nothing about the application as a whole, just simply, how to listen for an action to be taken and what to do then. Because the application is unaware of whom is listening and what actions they might take, execution of an OO application is seen to be somewhat non-deterministic (at least in the abstract).

The non-deterministic nature of OO applications forces developers to decouple the action(s) on which they're working from the rest of the application. Developers can't be explicitly sure what actions have already been taken, neither can they be sure what actions will be taken after theirs executes, eliminating their ability to create a dependency on them. This significantly reduces each object's awareness of the rest of the application, making its development small and focused, enhancing readability and maintainability, not to mention the ability for new developers to contribute value right away without needing "ramp-up" time to learn how the whole app works.

So, no physics lesson. However, if you're doing OO development, you might give some thought to how you think about how pieces of your app interact. Ask yourself, "Am I constructing my objects to interact in a similar fashion as objects do in our physical reality?" "Am I thinking about creating and using objects in my logical construct the way nature creates and uses them in her physical construct?" If so, you're probably headed down the right track from a maintainablity and extendability point of view.