From: Sean Russell Date: 2002-01-25T01:16:17+09:00 Subject: RE: OOP UI Design James Britt (rubydev) wrote: > A question to ask is, why is some code asking an object for data? > Wht can't the code tell the object to proces its own data internally? I think the reason for asking an object for data is to reduce object bloat. I've seen it stated that Objects should contain only code that applies to their own state, as much as possible; if the code affects another object's state, move the code to that other object. The problem I see is that if you never ask objects for their states, you end up with one of two situations: in the first, you force objects to become more aware of their environment (and therefore increasing their dependancy on outside objects), by increasing the bindings between any two objects. In the second case, you over-abstract interfaces, which becomes a maintenance nightmare. Consider when A needs to be aware of the num_monkeys of B. External code can get num_monkeys of B and send it to A. Or you could make A a listener of B, in which case B starts to bloat with code designed to handle listeners, and you start to bloat your codebase with Listener interfaces. Or, you could just make everything follow the Observer pattern, and attach things to whatever they have to be aware of -- in which case you start to bloat your objects with case statements to interpret whatever messages their receiving. Personally, I've found the Observer interface to be a maintenance nightmare. One loses track quickly of what objects are supposed to be observing which objects, and what sort of relationships and roles they have WRT each other. That is aside from the aforementioned case statement code bloat. I like Listeners more; it becomes clearer what roles objects are performing, but you still have the problem of listener management, which includes threadsafe issues, and I've found that (in Java) your "real" code can quickly be lost in a cloud of Listener interfaces. Still, this is probably my prefered solution. Both of these are poor solutions the situation where the messages being passed are non-trivial, or require some processing before they get passed. For example, Class A from some third party package generates some linear data, and passes it on to Listeners. Class B, from some other package, expects log-scaled data. The solution? Write a Listener that is also a speaker that listens to A, massages the data, and passes it on to B. Or, it might be possible to extend A, if you're lucky. Unfortunately, I've found this sort of data massaging is so common, and the interactions between two objects so complex (in most cases, more than a simple listen event, but rather a conversation), that you end up with an unmanagable mess of classes. And that last point is the real crux of the matter. Listeners and Observers are insufficient for non-trivial communications. In many cases, objects don't just listen to a single message from each other; they need to listen, respond, and listen again. So you turn all of your getter/setter methods into a complex listening structure, and (I'd argue) that you're back where you started, with the added mess of supporting code to handle these more abstract listening mechanisms. And you still don't solve the problem of: if a class can change the return value of a get() method, it can just as easily change the argument it sends to event( arg ) of a listener. An API contract is a contract; it doesn't matter which pattern you're using -- if you break it, you're going to confuse somebody. -- --- SER