Words of Wisdom
Ralf Bokelberg has a brief post on his blog about how Reusability is not the main benefit of Object-Oriented Programming (OOP) and I couldn't agree with him more.
As my students will attest, in every one of my classes I stress (ad nauseam) how the greatest benefit of OOP (and your #1 goal when developing applications) is Encapsulation (what Ralf calls "locality" in his post.)
Encapsulation refers to architecting your objects so that they are self contained and do not have intimate knowledge about their outside world.
Where an object is contained in (is a property of) another object, the parent object can communicate directly with its child by calling its public methods.
Of course, your objects will need to communicate with the outside world and this is where the event model of programming comes into play. Whenever one of your objects needs to communicate with the outside world, it should broadcast (dispatch) an event. (Basically, imagine your object with a speech bubble over it saying "something happened.")
Other objects that are interested in that event would have previously registered themselves as interested parties ("listeners") and will hear the event (so they can handle it, with an "event handler") when it happens.
A parent object should not have any intimate knowledge of the internals of its child beyond knowing its public methods and the events that it broadcasts. The combination of public methods and events for a given object is known as its Public API (or Application Programming Interface, commonly referred to as its "interface".)
(If you've used the Macromedia Version 2 components or the Flex framework, you may erroneously believe that Macromedia's engineers have violated this rule by allowing you access to properties within classes. In fact, what you may see as property access is actually achieved using implicit getter and setters that allow property-like calling of accessor methods.)
If dependencies are required between objects that do not exist naturally in a hierarchical parent-child relationship, it's a good idea to inject the dependency into one or both of the objects at runtime, thereby keeping a loose coupling between the objects while allowing them still to communicate using each other's Public APIs. For example, if two objects that are properties of the same parent object need to communicate, either the parent object can act as a broker between the two or it can inject the dependencies (references to each other) into the child objects.
The thing not to do is to hard code dependencies between objects. This is called tightly coupling objects and the way I visualize it is to see wires running between your various objects. If you have too many wires running everywhere, just like wires in the real world, they will tend to get tangled. Think of tightly-coupled procedural applications as a mad scientist's workshop with all manner of bare circuit boards spewed about, connected together via hundreds of wires, criss-crossing each other with the occasional spark or short-circuit. In other words, a room that's impossible to move in and where touching anything might make the whole place go up in flames. Contrast that with a well-encapsulated, object-oriented, loosely coupled application which is more like a network of sleek PCs, housed in a high-end penthouse, connected together via WIFI.
Of course, somewhere in your application you will need to tightly couple somethings together or else there won't be anything holding your application together. The trick here is to tightly couple as little as possible and to do it in a well-known/well-documented place.
For an example of a lightweight framework that handles most of this structural work for you, see Arp :)So, if you take nothing else away from this blog post, remember these three simple things:
- Encapsulation is the primary benefit of OOP
- Event model good
- Loosely-coupled good, tightly-coupled bad