Re: Using static factories to create two objects with bidirectional linking
- From: "H. S. Lahman" <h.lahman@xxxxxxxxxxx>
- Date: Sun, 29 Apr 2007 15:38:01 GMT
Responding to KelvSYC...
I seem to have trouble building a static factory due to the need to
build a two-way association:
Right now I'm making some kind of manager class, which, for a static
factory, takes two maps: one <A, B> and the other <B, C>. The manager
manages two types of objects: one is a wrapper for A (call it A'), and
the other is a wrapper for B (call it B'). A and A' have a one-to-one
relationship, as does B and B'. The manager has a one-to-many
relationship with A' and B'. (The two input maps are intended to be
discarded after the manager is built - for example, A and B are
"primitive input" like strings)
My pushback is: what is "manager" managing? Generally objects like XxxManager and YyyController will cause OO reviewers to bring out stake, kindling, and matches. To quote Mellor and Balcer in Executable UML, "The first rule of partitioning control is that you do not make controller objects. The second rule of partitioning control is that YOU DO NOT MAKE CONTROLLER OBJECTS!". Such objects usually represent higher level nodes in a functional decomposition tree that hard-wire in their implementations the way other objects collaborate.
I suspect this question is related to your other question, so "manager" may just be a factory object. If so, that's fine but I would use a more conventional terminology. B-) BTW, if this the same problem, shouldn't <B, C> be <A, C>?
One issue is the need for a two-way relationship: A's and B's are
associated with their manager, so in order to create the A' for an A
the manager must first exist. But for the manager to exist it needs
all of the A's. This is a chicken-and-egg problem that I am unsure of
how to solve: If X and Y are two classes that are one-to-one with each
other, and an X and Y object are associated with each other, then
clearly both objects must be created at the same time. However, this
can only be done if one object was partially built, the other object
was built and linked to the partial object, and the partial object was
completed - which would imply that on one end there would be a public
interface to change the one-to-one association (making said
association no-longer one-to-one). How, then do I do this so that
this does not happen?
I've seen situations where the problem space demanded that referential integrity be managed explicitly at the OOA/D level, but it is so rare and was so long ago that I can't even remember a context for a plausible example. In particular, the X/Y example sounds really fishy. To instantiate an object all you need are values for its attributes (including referentials). Having those ready is a precondition on being able to instantiate it. To instantiate two objects all you need are two sets of values and having those ready is a precondition to instantiating them.
So my pushback is that I would bet something else is wrong and this difficulty is only a symptom. Without knowing anything about the specific problem it is difficult to speculate on how to resolve the difficulty. So the following are just some generic possibilities.
The chicken-and-egg problem may only exist within the scope of a factory method, which is fine. In reality, whenever any object with multiple unconditional relationships is instantiated there is going to be some period of time between instructions when referential integrity is not satisfied simply because instructions require finite time and are executed sequentially. But that is one reason we encapsulate instantiation at the method level in OOA/D. That makes it much easier to ensure that no one accesses the object during that time. (This comes for free in a synchronous, single-threaded application because only one procedure can execute at a time; in a concurrent application one has standard techniques for things like thread safety that work fine so long as the instantiation scope is a single procedure.)
[Note that it is fair when one has lots of objects and relationships to instantiate to, say, have a private method to instantiate the objects and another private method to instantiate their relationships once the objects are instantiated. So long as those private methods are invoked with the scope of a single public create() responsibility, one can still easily manage referential integrity at the OOP level around that public method scope.]
Note that one way to deal with the <very rare> situation where one needs multiple create() responsibilities separated in execution time to complete the instantiation is for all of those responsibilities to be in the same factory. Then that factory can keep track of "partially" created objects internally in its private implementation. Then no other objects in the solution can access them until they are completed (i.e., the last create() method instantiates relationships to them). Then the developer is effectively managing referential integrity in the OOA/D by ensuring that the multiple create() responsibilities always get invoked in the right order.
Another possibility is that the relationship may really be conditional. One tries to avoid such relationships because of the overhead and fragility, but it isn't always possible.
There is nothing in the multiplicity per se that requires /all/ the elements on the *-side of a 1:* relationship to be available when the 1-side object is created. When the 1-side object is created one just needs one object from the *-side. Of course the problem space may demand that all of them be there (e.g., Whole/Part), but that is pretty unusual. Even then, there will usually be a point in time in the problem space where all the necessary information is available. That's because most customer problem spaces are just as serial as the hardware computational models.
The other issue is in the construction of A' and B': beyond wrapping A
and B, I'd like them to have functionality that takes advantage of the
map - in A' I'd like to get the B's associated with it (determined
from whether the A is associated with the B), while in B' I'd like to
test whether an A is associated with this B' (which is done, of
course, through the manager link) and get the C associated with the
B' (determined from what C the original B is associated with). The
issue lies in how this is done, which I am stuck on.
Another basic question: why do you need to "wrap" A and B? Is this a 3GL dependency management issue, an OOA/D issue, or a problem space issue?
Also, I think things are a bit too abstract here with As and Bs. I am having a hard time getting my mind around what the real problem is. Could you be a bit more specific about the problem space, responsibilities, and collaborations?
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
.
- Follow-Ups:
- References:
- Prev by Date: compilation mechanisms (Was: Dependency Management)
- Next by Date: Re: compilation mechanisms (Was: Dependency Management)
- Previous by thread: Using static factories to create two objects with bidirectional linking
- Next by thread: Re: Using static factories to create two objects with bidirectional linking
- Index(es):
Relevant Pages
|