Re: Aggregation vs composition
From: Daniel T. (postmaster_at_eathlink.net)
Date: 05/26/04
- Next message: Vaidas Gasiunas: "Re: Multiple inheritance revisited"
- Previous message: Daniel T.: "Re: Major steps of OOD"
- In reply to: Michael Rauscher: "Re: Aggregation vs composition"
- Next in thread: Michael Feathers: "Re: Aggregation vs composition"
- Reply: Michael Feathers: "Re: Aggregation vs composition"
- Reply: Michael Rauscher: "Re: Aggregation vs composition"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 26 May 2004 11:55:01 GMT
In article <c8sc1m$h1s$07$1@news.t-online.com>,
Michael Rauscher <michlmann@gmx.de> wrote:
>Daniel T. schrieb:
>[...]
>
>>
>> Say you saw code like this:
>>
>> Engine* makeEngine() {
>> return new Engine;
>> }
>>
>> void destroyEngine(Engine* e) {
>> delete e;
>> }
>>
>> void func() {
>> Engine* e = makeEngine();
>> // do something with e
>> destroyEngine( e );
>> }
>>
>> Which of the above is responsible for an engine's memory allocation /
>> deallocation? Or would you say that both func and makeEngine are
>> responsible for allocation, both func and destroyEngine are
>> responsible
>> for deallocation?
>
>In fact, it doesn't matter since we talk about objects and not about
>functions.
The functions above could easily have been wrapped up in classes.
> We could also say, that "new" is responsible for creation
>and
>"delete" for destruction.
Exactly! And the code that calls new and delete are also responsible for
creation and destruction, as well as the code that calls that code and
so on up the line. Well, not all the way up the line, there is a break
point: if the calling code has no way of knowing that a particular
object exists, then it cannot be responsible for deleting that object
even if it calls code to delete said object.
In the example above 'func' is every bit as responsible for creating and
destroying the engine as makeEngine and destroyEngine. The
responsibility is shared because the object is shared.
Maybe I should just come out and make my point here. If a particular
method in a particular class unconditionally destroys a part (and must
do so according to the documentation,) then any client object that calls
that method is every bit as responsible for the parts destruction as the
whole.
In much the same way you consider that any object that calls some other
objects "operator delete" function is responsible for that object's
destruction. I extend that up through the levels.
>> And since you brought creation into it:
>
>Sorry :-( It was the UML that brought creation into it.
You have to admit though that we have been avoiding the subject.
>> class Car {
>> Engine* e;
>> public:
>> Car():e(0){}
>> ~Car() { delete e; }
>> void acceptEngine(Engine*& e_) { delete e; e = e_; e_ = 0; }
>> };
>>
>> Here car doesn't create its engine, but does hide it (as best it
can,)
>> and does delete it. Is this not Composition, simply because Car is
not
>> "responsible for creation and destruction of the part"?
>>
>> For my part, I would say that this was composition, but I would love
to
>> hear what others think.
>
>As I wrote in an other posting, Composition doesn't *imply* creation,
>since a composite object may
>a) remove a part and give it to another composite object
>b) remove a part and the part may assume responsibility on itself
>
>At least a) implies, that a composite object may accept parts and take
>responsibility for them, like in your example.
But only composite objects may create the part according to the
description in the UML standard. For example, whatever does create the
engine that the above accepts, *must* also be a "Whole" to Engines part.
As far as point (b) is concerned, I would love to see a code example.
How is this responsibility for destruction shifted from the whole to the
part?
>I think, the point, why
>UML has brought creation into composition is due to the allowed
>multiplicities (0..1) and due to the fact, that objects don't fall from
>heaven. But that leads into another question that has nothing to do
with
>this discussion.
According to your interpretation, UML says that only the Composite whole
may destroy the part. It seems to me that the same interpretation would
require one to accept that only the Composite whole may create a part as
well (although it may be a different whole from the one that destroys
the part.) I think this has a much to do with the discussion. The
sentences in the UML spec that you are stressing put creation and
destruction hand in hand... Where does that leave a typical factory
class?
(I want to make sure it is understood; I don't agree with this
interpratation, I'm simply pointing out that your interpratation
requires the above to be accepted as well, but the above isn't generally
accepted, so your interpratation must be wrong.)
>>>Ad "Second": This has nothing to do with composition. I'd say that in
>>>one should never (at least with respect to software development)
destroy
>>>objects that are "in use" - regardless of the association type. But:
>>>neither association type prevents one from deleting used objects.
>>
>>
>> You added the period prematurely, that should read "... neither
>> association type prevents one from deleting used objects in C++." You
>> see the association *does* prevent the deletion of used objects in
every
>> other language that I'm aware of. IE a comment such as the one above
>> implies that Composition is solely for C++ and has nothing to do with
OO
>> in general.
>
>This applies to every language where the programmer has full control on
>object creation/destruction or memory allocation/dealloction
>respectively. The examples are shown in C++ because we have full
control
>on object creation/destruction and therefore can strictly follow UML's
>"implementation terms" to disprove your assumptions ;-)
So you are agreeing that your interpretation of Composition only applies
to languages that have some sort of explicit object destruction... I'm
left to wonder how a relationship should be protrayed if one doesn't
know yet what language will be in use? How should Composition be
implemented in languages that don't need such a mechinism?
>>>Of course, you may hide parts (so part objects are used only by the
>>>whole) and therefore prevent the programmer technically from deleting
>>>used objects - because there aren't used but by the whole. But this
is a
>>>design issue and has nothing to do with composition:
>>
>>
>> Isn't UML for design use? Shouldn't design issues be expressible in a
>> design language like UML?
>
>(Sorry, I've expressed myself wrong.) Of course, you can express this
>easily with UML but you don't express this simply by using a
>composition. It's a decision of the designer whether he wants the class
>to hide it's parts or not. And this has nothing to do with composition.
>
>Just because there's a method that returns a reference doesn't imply
>that this reference is used to destroy the referenced object. Moreover
>the composition tells us, that we don't have to use the reference to
>destroy the referenced object.
No one can destroy an object that is being used by others. As such your
interpratation of Compostion tells us nothing, unless it tells us that
no other objects can use the part, thus giving the whole the ability to
destroy it at any time.
>>>Compare (Organization doesn't hide departments)
>>>
>>>[Organization]<#>-----[Deparment]<-------[Person]
>>>
>>>with (Organization completely hides departments)
>>>
>>>[Organization]<#>-----[Deparment]
>>> /|\
>>> |
>>> [Person]
>>>
>>>Problem: The latter diagram doesn't show that a person works in a
>>>department.
>>
>>
>> Why not simply:
>>
>> [Organization]<>-----[Deparment]<-------[Person]
>>
>> Is this diagram so incredibly distasteful?
>
>Yes and No.
>
>No, because this diagram also shows us a whole/part relationship
between
>Organization and Department.
>
>Yes, because this diagram doesn't show the full truth. This diagram
>doesn't tell us, that we (clients of Organization) must not destroy
>Departments. It doesn't show, that it is Organization that "handles"
the
>existence of Departments.
Clients of Organization don't need a special diamond to know that they
must not destroy Departments. No object is permitted to destroy an
object that is being used by some other object.
>In order to prevent us from destroying Departments you would have to
>document, that it is Organization and only Organization that is
>responsible for destroying Departments. You have to denote strong
>ownership. This leads into the semantics of composition. Therefore: Why
>not simply use composition?
>
>>
>>
>>>Now, you could add an identifier to both, Deparment and Person:
>>>
>>>+-----------+
>>>| Person |
>>>+-----------+
>>>|-depId:int |
>>>+-----------+
>>>
>>>Now, Organization can hide Department. Organization can destroy
>>>Department at any time - even in GC languages :-)
>>>
>>>But: Person holds a "reference" to Deparment, although there is no
>>>association between them.
>>>
>>>Would you now say, that
>>>a) Organization should delete Deparments even if there are Persons
>>>that
>>>hold references on the latter?
>>
>>
>> Since person has no direct link to a Department object, the only way
>> a
>> message could go from Person to Department is through Organization
>> which
>> would obviously have measures in place to appropriately handle the
>> message if the department no longer exists. Organization would not
>> have
>> this option if Person had a direct link to an organization.
>
>This is just a technical issue. Logically we have a reference to a non
>existing object.
>
>How would you document the depId attribute of Person? E.g. "the ID of
>the perhaps existing department that this Person probably works in"?
>This would lead into millions ;-) of checks.
Actually, I would document the depId attribute as something that
Employee must feed to Orginazation for a particular set of methods. The
whole point is that Employee isn't dependent on departments, so why
imply a dependancy in the documentation that doesn't exist?
>What I want to say is: it's in first place not a technical but a
>logical
>issue, that prevents us from destroying used objects.
In GC languages, the symantics of the language prevents us from
destroying used objects. In C++, the desire to write programs that don't
crash prevents us from destroying used objects.
>> In general, I would rather see the
>> composition relationship dropped, converted to aggregation than see
>> the
>> composition relationship inappropriately maintained like this.
>
>With changing the relationship to an aggregation you get some important
>information lost: at least the fact, that only the whole may destroy
>departments.
If we assume that no object is allowed to destroy an object used by
others (a perfectly resaonsble assumption IMV because we want our
programs to not crash,) then no information has been lost at all. Only a
part that is no longer useful may be destroyed... If it is no longer
useful, then it really doesn't matter who destroys it, does it.
- Next message: Vaidas Gasiunas: "Re: Multiple inheritance revisited"
- Previous message: Daniel T.: "Re: Major steps of OOD"
- In reply to: Michael Rauscher: "Re: Aggregation vs composition"
- Next in thread: Michael Feathers: "Re: Aggregation vs composition"
- Reply: Michael Feathers: "Re: Aggregation vs composition"
- Reply: Michael Rauscher: "Re: Aggregation vs composition"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|