Re: Observer pattern limitations



On 13 Jul 2006 06:36:55 -0700, David Barrett-Lennard wrote:

Dmitry A. Kazakov wrote:

No, as with observer, GC does not solve this problem. The perspective must
be not "graph theoretic." I should be *just* theoretic. The problem is that
you don't have formally specified the requirements on what we call cache,
notification, dependency etc. Once you had, you would quickly discover that
the requirements are contradictory. Then the actual work begins: to remove
contradictions from there.

I like the following perspective : You have a graph of objects that
are persisted on disk (perhaps using an OODBMS). Conceptually these
objects are behaviourless before main() begins and after main() exits.
Therefore the dependency graph has no need to persist.

No it does with the objects, they know what they depend on. Note also that
there are good reasons why dependencies should be kept separately from the
objects. One of them is GC in the persistent storage. I used this approach
in my implementation of object persistence.

In fact,
dependents are generally transient and don't persist at all. All
behaviour in the objects is ultimately derived from an implicit
"liveliness" contract derived from main(). To help make the system
"operate", main() may directly or indirectly open a persistent store,
socket connections, create threads etc. A dependency graph results
from a liveliness contract in certain "observables" such as the
display. This requires that the display be redrawn at regular
intervals (say).

I am unsure what you've meant here. Normally it is a bad idea to traverse
dependencies backwards.

The dependency graph can be complex or simple; it
depends on the problem at hand. Normally it won't contain cycles, so
for a given snapshot of all the independents, all the dependent values
(including the screen) are *precisely* defined as pure functional
dependencies. Now the independents typically correspond to the
persistent sub-graph of objects (ie a sub-graph of all the objects that
reside in memory). This sub-graph has ACID properties and requires
transactional access.

That's problematic. Isolated graphs might become connected. I am using a
storage object. All objects persisting there depend on the storage object.
The object encapsulates a connection (and a transaction). When a persistent
object refers to some other object, the latter will be forced to persist.
In effect, dependencies across connections cause copying of objects.

This ensures atomicity of the persistent state.
It also allows for reasoning about the dependency graph : we are only
interested in its "atomic" recalculation *between* transactions.
Atomicity (and integrity) of the persistent objects leads to atomicity
and integrity in all the dependents, including the output display.
This conceptually eliminates the display of confusing, partly
calculated results, and aligns well with double buffered graphics that
atomically switch from one depiction to the next.

As I said before, persistence is a much lesser a problem, provided, you
don't want concurrent access to the persistent objects, while they are
inactive. I don't see how the above solves the problem of
publisher-subscriber *during* a transaction.

The two-phase
approach to processing the dependency graph aligns well with this
perspective. During a mutative, transactional update to the persistent
objects, dirtiness is propagated eagerly through all affected
dependents, but they are *not* recalculated immediately. This allows
(mutative) transactions to take the shortest possible time, maximising
concurrency on the persistent objects. By contrast, recalculation of
dependents, which is CPU intensive only requires shared read mode
locking on the persistent state, leading to the possibility of
excellent parallelism.

Shared read has problems as well. I designed several implementations of
mutexes which allow shared read / exclusive write. It is not a proper place
to discuss it, but in short, their performance is disappointing (as
compared with simple locks), then there are deadlock problems in
transitions between read and write. I'll definitely make one more attempt
based on Ada protected objects.

No, that won't solve the problem when you have pre-written components
in a third party library and you need to add the objects that represent
the "wiring". What deletes the wiring?

GC. You have a handle to it.

In my example only the subject does.

So the problem.

I'm talking about examples like "c := a.get() + b.get()". The
act of executing that code causes a and b to be calculated *before* c
is calculated. Lazy evaluation is great for solving most order of
calculation problems.

It allows enforcing order on operands, but that does not solve problem when
get() has side effects.

But it shouldn't. As I said, I'm taking a functional programming
perspective.

Then you wouldn't need laziness. You are talking about caching, that's
*not* functional. a and b are stateful and there is an independent source
which changes their states. There are some premises required to make your
proposition true. In general case, these premises don't hold.

This is why I suggested you to formalize the problem you have.

You will also have aliasing problem and one of
ordering operands of "+".

I don't know why you say there is an ordering problem. The only rule
is that a,b are calculated before c.

Provided that calculation of a does not influence the value of b. Typical
example: c := x^2. Funny as it may appear, but this cannot be always
implemented as c := x*x, because x^2 = x*x does not hold, when x is an
interval, x is fuzzy number, x is random, x is clock etc.

Note also that when there is no side effects, there is also no need to have
it lazy. Marshal values of a and b to c, that's it. Lazy can be great for
dealing with side-effects, but it does not eliminate the ordering problem.
It is a tool.

The problem space has to be analysed to find a solution of the *narrower*
problem.

Hmmm. I don't understand what you mean.

See posts of other people. From Lahman to Martin (usually I disagree with
both (:-)), they all make the point about applicability of the observer
pattern. Don't mix the problem and solution spaces.

[ As for me, I'm just trying to illustrate this point on war stories,
because publisher-subscriber stuff is my daily business. ]

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
.



Relevant Pages

  • Re: dip Notions 2 Major Errors
    ... > there is still a dependency on T in order to instantiate. ... I was attempting to focus on how one might see the "Inversion" ... > base AND does not breach the ingoing contract on the constructor. ... > seperate operation from instantiation. ...
    (comp.object)
  • Re: Observer pattern limitations
    ... You refer to a notification propagation. ... so a container of observers is an observer. ... some problems have complex dependency ... Therefore the dependency graph has no need to persist. ...
    (comp.object)
  • Re: Observer pattern limitations
    ... The "eagerly propagate dirtiness through the dependency graph" ... Therefore Y and Z are marked as dirty. ...
    (comp.object)
  • Re: Observer pattern limitations
    ... The nodes of the dependency graph are objects or members ... It is possible to implement a general solution to the dependency graph ... Observer pattern guaranties that under certain ...
    (comp.object)
  • Re: Observer pattern limitations
    ... The nodes of the dependency graph are objects or members ... any substance in what you are saying. ... which the observer pattern does. ...
    (comp.object)