Monday, March 7, 2005

How Much Should Objects Know About Themselves?

This question came up in our CFUG meeting last month when I gave a CFC-101 presentation, but I was reminded of it again today while reading this article about Service-Oriented Architecture. Here's the pertinent quote that caught my attention:

"The idea of SOA departs significantly from that of object oriented programming, which strongly suggests that you should bind data and its processing together. So, in object oriented programming style, every CD would come with its own player and they are not supposed to be separated. This sounds odd, but it's the way we have built many software systems."

In the context of the discussions we had at our CFUG meeting, this specifically came up when I was discussing the bean objects in the sample application I built. Several people thought they seemed too simple, and that I was leaving a bunch of methods out of the beans for the purposes of illustration. The beans actually were relatively "real-world" examples albeit limited in the number of attributes they contained. Some people were wondering where the view() or display() methods were, and how the data in the beans would actually get displayed, which is a rather fundamental error in logic (IMO anyway) with respect to how much objects should really know about themselves.


This debate actually ties in directly with the quote from the SOA article. While on the surface the quote may seem correct, there is something a bit off about it to me. In OOP we of course say that an object's methods and its data are both contained within the object; this is a hallmark of OOP and one of the features that most distinguishes OOP from procedural programming. What isn't so cut-and-dry, however, is exactly *which* methods should be contained within the object. Where does it make sense to start taking methods out of the object and create another object to act as a service layer?

Using the example from the article, should a CD object know how to play itself? Some would say yes, citing the self-contained mantra of OOP. I, on the other hand, would likely say no, a CD shouldn't know how to play itself, any more than I would say a Person bean should know how to display itself.

Why would I say that? Because to me if you take the notion of self-contained objects too far, you're breaking two of the other tenets of OOP, namely tight cohesion and loose coupling. If a CD knows how to play itself, that may be taking things one step too far because while all CD objects get played the same way, what if you want to feed your CD object to a different type of player in the future? Now obviously in other cases you *do* want to have polymorphic methods to handle things like this, so in the classic Shape object example, you might have a draw() method that gets implemented differently for a circle than it does for a square.

Let's think about the CD again for a minute. Should a CD, LP, and cassette object each inherit from a single parent and have polymorphic play() methods? Or would it be better to have the media objects be just a bit more ignorant of themselves and have a service-type object, such as a Player, handle the play() method based on the media given to it?

As with so much in OOP, the answer is "it depends." In the case of something like a Person bean, if you have a display() method within the bean itself you're severely limiting its usefulness and the overall flexibility of the bean. Better to have a slightly more dumbed-down bean that can be used by multiple front-ends through a service architecture of sorts.

At any rate, this thought came back to me while reading this article, and it's something we have to be mindful of as we build object-oriented systems. There are no hard-and-fast rules but certain ways of doing things make more sense than others, and we should always be mindful of making our objects as reusable as possible. If you want to display a Person in more than one way, or you want to play a CD on more than one type of player (e.g. standard player vs. computer drive), then consider dumbing down your objects a bit and creating a service object to handle things. This can make for a far more flexible system than can more easily be expanded in the future.


I don't think a CD should know how to play itself. I want to be able to transfer it from my home stereo to my car. I want be able to rip it into MP3s. But the purpose of a metaphor, as Martin Fowler says, is not to give you the answers, but to help you figure out what questions to ask. A CD can be heard through headphones or a car stereo. Can your bean be viewed different ways? Can its data be extracted and reencoded in a new format?

Thanks Patrick. The way I think of beans is no different than what you're saying about CDs. The beans hold the information, but what's done with it is not prescribed by the bean itself. So long as the bean provides methods to get at the data it contains, it doesn't know or care what's using it and how it's getting displayed, which in my mind is the best way to keep it flexible. If the bean is generic I can drop it into an HTML front-end, a Flash front-end, or even drop it into a Flex application without any changes to the bean itself.

That SOA quote is absurd and terribly misleading! Even in the purist OO world, we should recognize that a CD player exists and has many variants. Our "real-world" model should lead us to somehow inserting a CD object into a CD player object with the knowledge that a CD does *not* know how to play itself - that's the job of the CD *player*. It's exactly this sort of nonsense that can get OO a bad name because it's used as (bad) justification of why OO doesn't "work"...

Thanks Sean, that's why I posted the quote--seemed to me to be at best a misunderstanding or at worst a misapplication of OO principles as a means of attempting to illustrate an absurdity of OO that doesn't exist. If there actually is a system designed as the SOA quote outlines I'd argue it's not a *well*-designed system and certainly shouldn't be taken as an example of OOP.

No comments: