Jump to content

How do you handle Aggregation?


Recommended Posts

I've noticed a budding trend in my LV development that I want to stop and consider. When I have a class that aggregates other classes -- meaning that the owning object has a different lifetime than the owned object(s) -- I've started putting the owned object into a DVR and holding the DVR in the owning object. The reasoning for this is that I can check for a null refnum on the DVR when I want to see whether an owned object indeed exists as a member of the owning object. It leads to extra work every time I access an owned object, but I can make merge VIs and templates for most of that.

I've attached an example of this. Is this stupid, or does it make sense?

Alternatives I can think of are:

  • Store the owned object directly in the owning class. When I unbundle the owned object, compare against its default value to see whether it's actually there. There are problems with this, though. If I change defaults in the class definition, they aren't reflected in any constants I've placed on the block diagrams of calling methods. I may also need to use the defaults as a valid configuration for the owned object.
  • Add a Boolean data member to the owning class that indicates whether an owned object has been loaded. I hate this approach though, because it's a configuration parameter, not a data member. I don't like that I have to lug a bunch of meta-information about the object around with its real data.
  • Make every owner hold an array of owned objects, but know that the array size is limited to 1. Then I can check for an empty array when looking for the owned object. Of course, there's a memory allocation every time I add an owned object to an owner, and there are extra array operations every time I access or modify the owned object.

Aggregation Discussion.zip

Link to comment

To me it kinda makes sense. It is a symptom of having no way to destroy an object. The by-val way of creating a pseudo-destroy method is as you describe in No3 since you can clear the array which is a surrogate for destroying the objects. You then pray to the great god MemMan and hope that the garbage collection will get rid of allocations (or at the very least, reused). The by-ref is a way of achieving the same pseudo-destroy, since you can destroy a ref (and just ignore what it returns).

The difference in the destroying is that the owner is responsible for destroying the objects, rather than a function of whatever object you want to get rid of. In LV it's not a biggie because you have no control over de-allocations anyway, but in other languages you may have several different de-allocation procedures to satisfy that the owner may not be aware of.

Link to comment

The description makes sense; haven't opened the attachment. It's common in C to check for a null pointer, and this seems analagous.

Yep, it is analogous, but many things common in C make no sense in a by-value dataflow language, so I want to make sure the rest of the LV expert community agrees that this is a good way to do it.

Link to comment
  • Store the owned object directly in the owning class. When I unbundle the owned object, compare against its default value to see whether it's actually there. There are problems with this, though. If I change defaults in the class definition, they aren't reflected in any constants I've placed on the block diagrams of calling methods. I may also need to use the defaults as a valid configuration for the owned object.

Do this one. The default object will always update to reflect the class definition (I’m pretty sure, please correct me if I’m wrong) as default objects don’t save values, they just point to the class definition. To get around the second problem, always have a parent class that is “virtual”, meaning that only child objects are ever actually created and used. Then you can use the default parent object as “null”. Having a virtual parent class is very useful for lots of other reasons, so it is usually good to put it in regardless.

— James

PS, here is your example, modified to make “Config” a virtual parent, overridden by a ConcreteConfig class, and eliminating the DVR:

Aggregation Discussion with virtual parent.zip

Edited by drjdpowell
  • Like 2
Link to comment

To me it kinda makes sense. It is a symptom of having no way to destroy an object. The by-val way of creating a pseudo-destroy method is as you describe in No3 since you can clear the array which is a surrogate for destroying the objects. You then pray to the great god MemMan and hope that the garbage collection will get rid of allocations (or at the very least, reused). The by-ref is a way of achieving the same pseudo-destroy, since you can destroy a ref (and just ignore what it returns).

The difference in the destroying is that the owner is responsible for destroying the objects, rather than a function of whatever object you want to get rid of. In LV it's not a biggie because you have no control over de-allocations anyway, but in other languages you may have several different de-allocation procedures to satisfy that the owner may not be aware of.

As you all might guess, I see this a bit differently -- given that we're all functioning in LV which is byval in its exposed interface. The idea that the byref construct is "better" comes IMO from a history of having learned it first (generally in a CS program) and then assuming that the optimal way to program is to follow that paradigm and that would include the necessity of being the MemMan god, instead of just knowing how it operates for you in a certain well implemented byval environment like LV. So you have two broad choices:

1. try to force LV to be as byre-like as possible (which is the basis for a number of these suggestions, like the virtual parent)

2. accept and use the native tools and this would mean esp, release the need to believe that you can optimize memory management IFF you were allowed to be god.

If you adopt the second course then creating the refnum and checking for a null refnum is IMO the way to go. It's simple, direct, is inline with the paradigm and it works, esp because you can count on the native MemMan god of LV to do its job.

Just my two cents worth.

Link to comment
  • 1 month later...

(Hadn't seen this post before...)

Store the owned object directly in the owning class. When I unbundle the owned object, compare against its default value to see whether it's actually there.

This doesn't make sense to me. As far as LV is concerned, a default object is just as real as a non-default object. I'm guessing what you really are looking for is a way to know if the bundled object has actually been configured. With that in mind, here are other options I use at various times:

  • Labview doesn't have constructors, but one of the first things I do when I create a new class is add a "Create MyClass" method to it. I add required inputs for any data members the class needs to operate correctly. By convention all my objects are instantiated using the creator method instead of by dropping a class constant. In your case, your "Create OwningObject" vi would have a required input terminal for "Owned.lvclass," and users will not be able to instantiate an owning object without also giving it a configured owned object, assuming they are following the convention. (This avoids your problem 90% of the time.)
  • Ditch the comparisons and let your owning object perform its operations on the default object. Ideally I give the default object some reasonably simple and useful functionality. If that's not possible and the operation fails you can return a descriptive error message. Ultimately it's up to the developer to make sure they are using your api correctly. (This covers another 9% of the cases.)
  • Use LVObject as a placeholder for the owned class in the owning class. Create a private vi to retrieve and downcast the object to the owned class. If the downcast fails you'll know the user has not set the object correctly. This is very similar to what James suggested, but you don't have to create a separate parent class that doesn't do anything. (Another 0.9% covered.)
  • Include an "IsConfigured" parameter in the owned (not owning) object. I'm not particularly fond of this approach, but sometimes it is necessary.

Link to comment

Also somehow missed this discussion the first time around.

I've noticed a budding trend in my LV development that I want to stop and consider. When I have a class that aggregates other classes -- meaning that the owning object has a different lifetime than the owned object(s) -- I've started putting the owned object into a DVR and holding the DVR in the owning object.

I don't understand how an aggregation model can ever be implemented purely by value. The very fact that you're aggregating something with a lifetime independent of the aggregator means the items being aggregated are exposed and potentially likely used outside of the scope of the aggregator. If these objects are operated on in an external scope and you're aggregating by-value, how would the aggregated values ever possibly contain accurate state information?

The aggregator model to me requires some level of referencing mechanism, be it DVRs or otherwise. If anyone can generate an example illustrating otherwise I'd love to see it, but I suspect such examples would actually be composition (where the lifetime of the contained objects is tied to the container).

Link to comment

I don't understand how an aggregation model can ever be implemented purely by value...

If anyone can generate an example illustrating otherwise I'd love to see it, but I suspect such examples would actually be composition (where the lifetime of the contained objects is tied to the container).

Here's how I do it. I think it qualifies as aggregation but I'm interested to hear what you think.

Suppose I have a two classes, Car and Engine. I don't want to instantiate a Car object without a valid Engine implementation, so on Create Car.vi I add a required input for Engine (which I instantiate with its own creator method.) Furthermore, the Car.Destroy method return the Engine object that was present when the car was destroyed. I can create an Engine object and manipulate it before creating the Car object. I can also manipulate the Engine object after the Car object is destroyed.

What I can't do is directly manipulate the state of the Engine object inside the car via DVR or references. If the Engine needs to be manipulated I either write Car methods that delegate to Engine methods, or I write accessors for the Engine object. I prefer delegation as it preserves encapsulation, but if discover I'm writing delegate Car methods for every Engine method I likely have a design flaw. At that point I'd probably go back to accessors for the Engine object (or perhaps have an Engine input terminal on the relevant Car methods.) The user calls Car.GetEngine to retrieve the object, performs operations on it, and calls Car.SetEngine to put it back in the Car.

Link to comment

Gotcha, that makes sense.

I actually see an error in my post, I was trying to say I expect the alternatives to be containment. All these silly semantics. Aggregation and containment are different forms of composition. I trip over these words all the time.

Anyways, to me what you describe is containment, because the Engine becomes attached to a Car, and once attached is only accessible through the Car. I completely agree though that the line gets blurry, because you definitely can have an Engine before you have the Car, and it could also be removed, or returned when you're done with the car...

Link to comment

All these silly semantics. Aggregation and containment are different forms of composition.

I thought containment was just a general term encompassing aggregation and composition. Wikipedia implies containment is a specific form of composition, which is in turn a specific form of aggregation...

In UML, composition is depicted as a filled diamond and a solid line...

The more general form, aggregation, is depicted as an unfilled diamond and a solid line.

Composition that is used to store several instances of the composited data type is referred to as containment.

(Italics mine.)

I'm not particularly fond of that topology and I'm not sure where the author got it from. The two linked references don't appear to support it. MSDN says,

Aggregation is actually a specialized case of containment/delegation.

though their definitions appear to be linked to specific feature available in COM.

I trip over these words all the time.

Me too. I feel like sometimes I get nit-picky with terminology but it sure gets hard to understand what others are talking about when we can't agree on the definitions of fundamental concepts.

Link to comment

The first time this came up, the only thing I replied to was the factual error -- I'm tired of that garbage collector myth being propagated.

What I avoided was any commentary on the main post because I didn't feel like it, but today, I feel like asking:

Why do your objects have lifetime? Processes (VIs and communications channels) have lifetime. Data does not. These patterns that you're developing in your code come about because of the co-joining of these two concepts, an artifact of other programming languages that are piss poor programming models for a parallel environment.

  • Like 1
Link to comment
Why do your objects have lifetime? Processes (VIs and communications channels) have lifetime. Data does not. These patterns that you're developing in your code come about because of the co-joining of these two concepts, an artifact of other programming languages that are piss poor programming models for a parallel environment.

:thumbup1:

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.