Re: Design question: polymorphism after object creation
Balog Pal wrote:
"Marcel M?ller" <news.5.maazl@spamgourmet.com>
I am seeking for a neat solution for the following problem:
There are objects of different types with a common base class, let's say
folders and files (although that does not completely hit the nail on the
head). These objects have a common primary key, the full qualified path.
But the polymorphic type of the objects emerges only *after* the object
has been referenced. See below.
[...]
I get this, but the following seem contradictory, especially the observer
part.
If the object is not there yet why on earth is it observed?
The object is existing, but not specialized. You may request different
kinds of information. This triggers a worker thread to obtain the
requested information. If the object is not yet specialized (e.g. File
or Folder) the first step is to determine the object type. This may
require an analysis of the file content or the http object. Than an
instance of the matching class is instantiated and this class fills the
requested kind of informations. Once completed, the observer is notified
that the information is now available. If some information is available
fast and other information take longer the event may also fire twice
with different flags.
In the current implementation there is a hard-coded function that guess
the object type from the PK without any I/O. So File and Folder in the
example can derive from Base.
But this guess sometimes fails. This results in an object with invalid
state.
From my limited understanding of the problem, I'd have something like:
map<string, shared_ptr<Base> > repository;
Yes, similar. But the PK is intrusive part of the object.
So it is more like set<int_ptr<Base> > repository;
As the only info up front is PK, in this phase you just put that in the map,
the ptr is null, signaling the not-accessed state.
Even an object, that is not fully complete, has some basic properties.
It may be selected, it may be referenced, it has a changeable display
name and so on. NULL is not sufficient.
When access comes, you have the extra info, create the object by factory and
put it in the ptr.
Access is done from the GUI thread. There must not be any I/O within
this context, to keep the application responsive. All I/O is done
asynchronously. In fact hundreds of objects may be in pending state at
the same time. A set of parallel workers do the jobs.
If your initial info is more elaborate, you can create a struct of it, and
use instead of string in the map. To avoid duplication, the object body
can have a pointer to the PK part, passed in ctor.
OK, you say that what I called BaseProxy could be the key part of a map
and the polymorphic part is the value. This is possible. But I see no
difference to my idea with BaseProxy that owns the polymorphic part. I
have really many calls from the specialized classes to the Base. Most of
them belong to the observable pattern and the synchronization. This
services have to be stable for the observable to work properly.
Each kind of information has a conditional variable. So I can lock
individual parts of the object without the need of thousands of mutexes.
All these calls have to go through the pointer to the PK part. This
makes the code look very ugly.
However, if I have no other choice, I will go that way.
If the object lives only
in this repository, and it is a map, the nodes are stable.
Yes. The objects are non-copyable anyway.
(certainly I
would internally firewall the access to PK by a function, so later it can be
simply converted in a local copy, or a shared_ptr instead of raw, etc...)
This is required anyway, because the repository docks to that interface
to lookup the PK for comparison.
A union with a few types and a stable common base class would be nice.
If the base is virtual, the memory layout could match the requirements
in theory - but only in theory.
Like this:
+------------------------+
| Base |
+-------+--------+-------+
| File | Folder | Other |
+-------+ | |
+ free | +-------+
+-------+--------+-------+
This could turn into 4 different classes (if Base is not abstract)
without the need to change the Pointer to Base.
Marcel