Here’s a question that’s come up at work over the last week or two. A couple of us are trying to rehabilitate some unmaintainable code that’s currently in our product: trying to bring it “From Mud to Structure”, as POSA1 puts it. The obvious pattern to start with is is Layers: we’d like to separate the pieces of software into a core that provides the core functionality that we need and that uses the same classes and patterns as the rest of our system, with an outer layer on top of it that satisfies customer-specific needs (e.g. using CORBA instead of our own RPC system and threading model) and that could be easily changed for different customers.
So: how do adjacent layers communicate? You could have adjacent layers invoke each other via function calls, but that would couple them too tightly. One standard trick to use here is to use the Observer pattern: in one direction, you’re allowed to do function calls, but in the other direction you’re only allowed to publish state change events. (And, of course, information can also be passed in the other direction via return values of function calls.)
But who gets to do function calls, and who is restricted to being observed? You could do it either way (replace a function call
foo() by publishing the event that your
desiredAction attribute has been set to
foo), but presumably one way is more natural than the other. In the situation, it’s pretty obvious: the outer, customer-specific layer frequently wants to invoke customer-neutral functionality in the inner layer, get the results back in a customer-neutral format, and translate them to a customer-specific format. There are also some situations where the inner layer wants to trigger actions in the outer layer: for example, the outer layer may request that the inner layer start a long-running action, and want to know when the action is finished. Which is a classic situation where Observer works well. So, here, it looks like the best solution is for the outer layer to make function calls on the inner layer, and for it also to be an observer of the inner layer.
Is this a general rule, then? If you have a layered architecture, do you want your function calls to always go inward? At work, there’s somebody who sometimes gets annoyed if it looks like we’re doing things that he disagrees with, so I looked to see if he’d written anything on the subject. He gives an example at the other end of a layer architecture – a machine-independent part (of a kernel?) on the outside, and a hardware abstraction layer on the inside. And there, he wants the hal to call and be an observer of the mi layer.
So, some questions:
- Does this make sense, or should it be reversed? Honestly, here I’m hampered by my lack of experience – it sounds fishy to me, but I’ve never written such software, and the devil is in the details. (It is the case that the person in question thinks about observers fairly differently from me.) One thought experiment is: what if we go down still further, so the outer layer is the hal and the inner layer is the hardware itself? If my understanding of hardware is correct (which it probably isn’t!), we have two communication mechanisms: the hal can read/write memory (and some memory locations may trigger special actions, e.g. disk writes), and the hardware can raise interrupts. If that’s the case, then I’d say that reading and writing memory is more like performing a function call (consider, e.g., replacing a public member variable by a pair of accessors), while an interrupt is more like publishing an event to on observer (“something happened!”).
- If we do agree with his point of view, is it really inconsistent with the example I’ve given above? In my example, the customer-specific layer was a caller and observer of the customer-independent layer; in his example, the hardware-specific layer was a caller and observer of the hardware-independent layer. Maybe if I thought about it more, I’d decide that we were both right, and come up with a generalization along the lines of X-independent vs. X-specific.
- Is there really only one correct answer here? POSA1 says no: it’s more common for outer layers to call and observe inner layers, but that the other direction is also possible. (As well as other solutions, e.g. observers everywhere to reduce coupling still further.)
There are no revisions for this post.