views:

226

answers:

7

Quickly going through the GoF and the Head First Design Patterns book, it seems like there is no mentioning of infinite loop detection and handling for the Observer pattern?

I think if it is between 2 classes, we can be more careful about the infinite loop problem, but what if there are 5 classes or 12 classes, and the observers go multi-directions. In this case, won't an infinite loop be possible and some detection should be added to this pattern?

+2  A: 

No. Any situation causing an infinite loop is obviously a sign of a bad design. I have never seen this happen by accident in the observer, and I don't see a need to write special cases for it in the observables.

This is a case of Don't Do That Then.

Matthew Flaschen
+4  A: 

Infinite loop handling could be part of the actual implementation of the pattern, but the description of the pattern itself should be general and not be concerned with these details.

eljenso
+1  A: 

Is in general the infinite loop check equivalent to the Turing's halting problem? If so then could not be possible.

Luixv
I don't think they are equivalent. Assume that you can generate an id for the originating event. Mark in a global table each time a particular observer sees an event. If an observer has already seen an event, it simply does not propagate it. Eventually all observers registered for an event will have seen it. None of these will propagate it again so there could be no infinite loop. I don't think I've just solved the Halting Problem, but I do think it would work for this problem.
tvanfosson
It's more subtle than that. Observers don't rebroadcast changes in their Observables; rather, an observed change may trigger a change in the Observer, which would then need to be broadcast. If a change in A triggers a change in B, then B's change is a distinct change from A's change, and you can't just combine them under a single ID and ignore one.
Tom Anderson
+1  A: 

As @Matthew points out this would be a case of poor design and you ought to catch that during design and solve it by improving the design, not adding more complexity to the code. Specifically, I think it would be a sign that classes lack cohesion, that responsibilities are spread out through the various classes rather than properly assigned to a single class or small set of cooperating classes. Properly applied to a good architectural design the Observer pattern ought not need any special handling to prevent the infinite generation of callbacks.

tvanfosson
A: 

How would you implement such a check?

Jim Arnold
+4  A: 

Infinite loops can only happen if (a) Observers are also Observable, (b) changes they observe can lead to changes in themselves, (c) the graph of observation is cyclic and (d) there is a kind of change which can end up triggering a change of the same kind. The ideal solution would be to design out the risk of infinite loops by ensuring one of those requirements is absent. If your current design makes all four true, see if you can change it to make one of them false.

The traditional uses for Observer-Observable are in layered architectures - for instance, where view-controllers observe model objects, or where event handlers observe GUI components - and here, the graph will not be cyclic, so there's no risk of infinite loop.

I should probably explain about point (d), about different kinds of changes. What i mean is that if you have a situation where, say, a UserInputEvent can trigger a ModelStateChangedEvent, and a ModelStateChangedEvent can trigger a WidgetUpdateEvent, which can't itself trigger anything, then even if the Observers form a cyclic graph, you can never get infinite loops, because there's only a finite number of stages in the sequence of events. Effectively, the events form an acyclic graph, even if the Observers don't. If, however, a ModelStateChangedEvent can trigger another ModelStateChangedEvent, then you have the risk of cycles.

If you really can't avoid the risk of cycles, then you can steal an idea from Jon Postel, and make every event notification carry an integer time-to-live counter. When an Observable broadcasts an 'original' event, meaning something that comes in from outside the network of Observers and kicks off a cascade of events inside it, it sets the counter to some suitable initial TTL value. When an Observable responds to an event by broadcasting another event, it would use a TTL one less than that of the triggering event. When an Observer gets a notification with a TTL of zero, it ignores it. This would prevent infinite loops, but would also prevent an Observer responding 'correctly' to some events, so it's an idea to be used with caution. I would strongly suggest that an event cascade hitting the TTL limit should be considered the result of a programming error, and should be logged and reported in the same way as you'd handle something like a NullPointerException or an assertion failure.

Tom Anderson
A: 

It is kinda weird that people with high rating give such a useless answers. sigh

Event loops happen and happen VERY often in GUI programming. It seems to me that more or less standard solution is to break the loops by either filtering duplicate or redundant events or introduce some method that do not trigger events.