james@rubyxml.com schrieb:
How would an object know the methods of an arbitray parent? Doesn’t this
completly couple the two objects? The parent (reasonably) expects the
object it creates to implement certain methods; the newly-created object
expects the parent to implement certain methods.
Yes, indeed. But let me think aloud a bit:
1.) Sometimes you have this tight coupling between two
classes/objects. Customer and Address perhaps is a bad
example as you can Address imagine to belong to other
containers too.
OTOH a relationship between an Order and an Orderline or an
Invoice and Invoiceline is much closer.
2.)
Designing that relationship as bidirectional navigable
(as expressible with the UML) is a possibility.
You could hand around orderlines and they keep the relation
to their container/master/whatever-term-you-want like with
closures at the technical level.
OTOH: Wouldn’t it be a better design to always hand over the
reference to the order or to the invoice instead of single
lines, and access the parts of it via the order’s/invoice’s
interface? (delegator). Otherwise the client of Order would
have a deep inside, how an order is implemented inside.
But: Wouldn’t that mean, that an Order has to implement
something like a Orderline interface?
(As I could not easily return a reference to one of its
orderlines …)
This tight coupling is an intimate knowledge between
Order and Orderline, and is directly understood by people
knowing the domain.
But looking at this: Some people here stated never need
to pass a self reference to a contained object. This would
mean to have a strictly uni-directional navigation troughout
the whole system.
And that’s IMHO not true, unless they make some design pull-ups to
strictly avoid callback and delegation.
Perhaps I misunderstand what you need to accomplish or how self_parent
would be implemented, but it sounds like it would encourage fragile designs
without any real gains.
Hum, not really. I’ve got the feeling that there should always be
two kind of interfaces.
First: The ‘inner’ one between two tightly coupled
classes/objects. Why shouldn’t be there a relation between
a class/object and its close “environment”?
(In comparision: Think of the effort of writing a shellscript
which gots everything explicitly as CL-options instead of
using the environmentvariables.)
They would communicate with each other in a form of call and
callback.
Second: The ‘outer’ one between a component and it’s
environment. To be useful in many “environments”, perhaps
contexts, the component should never rely on specific behaviour
or structure of its outer side. Here works the Law Of Demeter:
“Don’t talk to strangers”.
In this description a ‘component’ can be a single class or
a combination of tightly coupled, co-relating classes.
Looking into the real world, there are technical items
which can be assembled into many environments by having
a standardised “interface” (plug, switches …) but having
parts inside them which possibly never (hum I don’t like this
word) could be used outside in other combinations.
Coming to the original subject:
- Yes, indeed it is a design decision to couple classes
So to be open, the language has to support all (hum, most of)
the decisions I made. Not necessarily language inherent (when
passing self) but it could simplify things, if it would be
possible (when having a self_composite reference, to give
another name for that ;o) ).
This is the fact with Ruby. Your design decisions are supported.
In this case by using self.
- As we’ve seen in the discussion in this thread, this is a
design decision and not a general feature of classes in OOP.
So this reference pointer can not be considered an every-class
feature requirement.
It is the special case the UML calls:
Composition, meaning a one-to-many aggregation.
And it is the special case this asssociation is bidirectional.
Perhaps it would be worth thinking about a keyword in the
language declaring a class as ‘bidirectional’, which, when
using it, would introduce this self_composite reference?
- But it would not be enough to have this keyword. You should
declare more of this relationship. So you would declare the
class to which this composition refers, to give the system
ability to proof the structure at runtime just before
calling a self_composite’s method.
This would lead indirectly to a design language, where you
would declare associations in another way than common today.
The question then is: What about all the other kinds of
associations, what about multiplicity?
I resume: This feature would lead to a totally other PL concept,
and leads outside of Ruby.
In your example, Address requires an object that implements pretty_name.
Indeed.
It works with any parent if that parent passes in a pretty_name-compliant
object.
Right.
Having Address use self_parent means it only works when the
parent responds (and in the right way) to pretty_name.
Indeed, that’s the problem with communication .
The called object has to implement a method for the message.
Changing the
Customer class can break the Address class, and vice versa.
What you stated is a problem you always have when changing the
interface of a class which is used by other parts of the system
(like usual, as classes/objects exist to be used by other parts,
or they will be killed by the GC).
So changing the Customer class will have much more effect
to objects not mentionted here in the current discussion:
Its clients!
Using the self_whatever reference would not break the
rule of communication via interfaces.
It would only simplify the definition of a very special
kind of association.
And as I told, Customer->Address is a bad example.
Regarding to my examples:
Changing a class without knowing the relating (domain) model
(Order → Orderline) is a foolish attempt.
When knowing the (domain) model, changing the Order class
without having a look at the Orderline class (or vice versa)
should be considered a implementor’s offence.
BTW: Regarding the last post I read.
This is indeed not a matter of OO “inheritance”. But looking
at the ways to construct systems there are two relation
dimensions: inheritance and association.
It is interesting, that OO languages directly support only
the former one, but have no implicit mechanisms for the latter.
This is why this relations must be explicitly ‘generated’ out
of an UML diagram into code mechanisms (e.g by passing self).
But these mechanisms just are flexible enough to support the
very different kind of design decisions.
JM2C
Det