Re: Aggregation vs composition

From: Michael Rauscher (michlmann_at_gmx.de)
Date: 05/24/04


Date: Mon, 24 May 2004 10:34:40 +0200

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. We could also say, that "new" is responsible for creation and
"delete" for destruction.

>
> And since you brought creation into it:

Sorry :-( It was the UML that brought creation into it.

>
> 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. 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.

>
>>>>>both must ensure that the engine's lifetime is sufficient for the other,
>>>>
>>>>In your own words, you are simply holding this point self-evident, which I
>>>>find to be not a very convincing argument.
>>>
>>>
>>>Surely you must agree that if either client calls delete on the engine
>>>at an inappropriate time, that would be the responsibility of the client
>>>that did so?
>>
>>Right and therefore such client would violate the semantics of composition.
>
>
> So Car can "violate the semantics of composition" by deleting the object
> it is solely responsible for. How can it be solely responsible yet not
> be allowed to do the thing it is responsible for?

Oh, I misunderstood. With "client" I meant a client of Car that gets the
engine and calls delete on it.

>>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 ;-)

>
>>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.

>
>
>
>>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.

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.

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.

>
> So, in answer to your question, I would have no problem with
> Organization dropping a department even if Persons are associated with
> that department in this case, because Organization can intercept any
> message sends from that Person to the department. As you have already
> said, Organization actually *can* "delete" departments, even in GC
> languages. :-)
>
>
>
>>b) the composition prevents from deleting Deparments that Persons are
>>pointing to?
>>
>>Or would you now say, that it isn't a composition because Person *knows*
>>about the existence of departments?
>
>
> Strictly speaking, Person doesn't know about the existence of
> departments, all it knows is that there are a set of methods in
> Organization that require the Person to provide its 'depId'. For all
> Person knows, the Organization class doesn't even exist.
>
> I need to say though, that I dislike the design proposed. It allows a
> person to change his department by simply adding to or subtracting from
> an int that it holds, if Person has a link to its department object, it
> can give up on its department but a person can't change departments
> without consulting the Organization. In general, I would rather see the

Right.

> 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.

Bye
Michael



Relevant Pages

  • Re: Aggregation vs composition
    ... responsibility is shared because the object is shared. ... Is this not Composition, simply because Car is ... may destroy the part. ... >> other language that I'm aware of. ...
    (comp.object)
  • Re: Aggregation vs composition
    ... and as composition is transitive, the engine is part of Car, too. ... remove them) can be taken in responsibility for destroying them. ... >>This applies to every language where the programmer has full control on ... In a language, where one can destroy objects explicitly, it's not a big ...
    (comp.object)
  • Re: Aggregation vs composition
    ... aggregation and composition in UML are flawed concepts. ... > responsibility is shared because the object is shared. ... > may destroy the part. ...
    (comp.object)
  • Re: Aggregation vs composition
    ... Would you insist that it stay a Composition? ... Not only does X destroy the ... > int, it has no other choice but doing so as it cannot prevent the ... they would have no responsibility in your failure. ...
    (comp.object)
  • Re: Aggregation vs composition
    ... >> the lifetime of the B objects they share. ... >> then they also share responsibility for the disposition of that third ... reference to B's": ... >> explicitly destroy a B object at any moment, the other clients of that B ...
    (comp.object)