Jump to content

How to make self referencing objects?


Recommended Posts

I was disappointed that the additional ornaments on the VI icons were missing (remembering the cell phone and baby example).

Much of the Bag implementation was done in 8.6 and I simply didn't bother digging up my ornaments. Still, I do switch back and forth on which ornaments I'll use. The only ones I'm sure I like are the scope indicators. I'm still undecided on the folded corners for by-ref classes (mostly because it makes the object cube look funny) and the green/red markers for get/set operations.

Because I am so focused on 'DVR the data', I would have DVR'd the data somewhere. My first thought was to do this in the Bag class.

Since I cannot predict what a user will want to do with a Bag collection, it is better to leave it a by-value class. This way the user has the flexibility to wrap it in their own by-ref class or simply carry the Bag object around in a DVR in their app. If I were to release it as a by-ref class, there is no way for the user to give it by-value behavior.

It would be very cool to also make this part of a largeĀ® framework. I am trying to take my group into the OOP realm and need to make things as easy as possible. I'd be willing to help out on this. We should probably start a separate thread. If others join in, that would be great.

Come on over.

Link to comment

Just to keep this updated, I'm moving away from the original topic.

Today I found the 'Delegate' design pattern on wikipedia, and it was mentioned to be a solution for the multiple-inheritance. Well, coded the MultiplicityElement as a by-val class (first attemt was by-ref) and put this one in the by-ref private data of the 'child'-class (so it is already by-ref, that's why I implement it by-val). Then write the Accessor VI's and voila. Looks good so far.

I think the wrapping of the 'parent'-class can be automated with scripting. Which means, that we propably are a very short step away from interfaces and multiple inheritance of our own brew...

I'll dig through the interface implementation thread on LAVA tonight, just to see if I miss something...

Felix

Link to comment

Today I found the 'Delegate' design pattern on wikipedia, and it was mentioned to be a solution for the multiple-inheritance.

I have a hard time thinking of delegation as a pattern. If someone says "bridge pattern," "facade," or "proxy" I can visualize how the various classes are related to each other. "Delegate pattern" doesn't do that for me. It seems more like a fundamental principle of OOP. Objects delegate (verb) responsibilities by passing requests on to a delegate (noun) that is designed to provide that functionality. The wikipedia articles refer to languages that implement "true" delegation but I'm not familiar with them so I'm sure I'm missing some of the finer points.

Seems to me patterns define the relationships between the calling object and the delegate object. Different relationships create different dependency trees between the classes and ultimately dictate where you can split your application into different modules. If a pattern doesn't define a relationship, what good is it? Then again, it could be I've just missed the boat on this.

Which means, that we propably are a very short step away from interfaces and multiple inheritance of our own brew...

As far as I know interfaces and multiple inheritance are different solutions for the same problem. You don't need to implement both of them.

Since the language itself doesn't support either of these concepts the best we can do is implement a framework that simulates that functionality. That means we have to strike a balance between functionality and ease of use. My gut feeling is a multiple inheritance framework would be pretty heavy. I can't think of a way to get a native object to successfully wire into methods from two separate parent classes. You might be able to do it if you create a second "virtual" inheritance mechanism and make all the user's classes children of the same "real" parent class , but that adds a lot of complexity to developing a project.

Interfaces are more managable, but the framework I put together is still too complicated for most people to understand and I'm not that happy with it. This isn't to say people are too stupid to understand it, just that with the level of native OO functionality currently in Labview it requires a lot of code to implement something that behaves like interfaces. (Ironically after spending a year or so getting the Interface Framework to work reasonably well, I discovered Interfaces aren't the right choice for my original use case. They can still be useful, just not for what I intended.)

I'm interested in seeing what you come up with. thumbup1.gif

Link to comment

Ok, here we go. Not all accessors are implemented. But the StructuralFeature by-ref class contains a MultiplicityElement by-val class. Exactly: The ME is part of the TD that is inside the DVR of the private data.

What needs to be done (and this is where I'd code some scripting VIs) is writing the accessors VIs. They do implement all public methods of the delegate and pass the calls to the delegate.

So actually, this isn't an interface, because the code is in the delegate, but a kind of multiple-inheritance. For an interface, you will have empty methods, inherit them in an abstract parent class and use the must-override flag to force any descendent to implement them.

Type casting the class to the implemented interfaces is an objectiv I didn't think about yet. But propably with the correct kind of by-ref, the cast would just pass the 'interface-class'.

The nice thing is, that inheritance works in favour of this concept. The delegate will carry it heriatage (so you can design an interface hierarchy), as well as the class will carry the heritage of all interfaces.

Felix

post-15343-127437581413_thumb.png

post-15343-127437581576_thumb.png

Link to comment

I was a bit experimenting in the field of generics. I'll at first give a description of the requirements I have in the uml design, and later go to the LVOOP part.

uml:

A lot of attributes or assiciations are defined as 'derived union'. They are similar to an abstract declaration, as they will be defined by child class(es) -> derived. Furthermore they are composed of all subsets -> union. The child classes define there own (derived or non-derived) attributes and declare them as a subset of the parent attribute.

To make things a bit more complicated, sucvh an attribute can have a multiplicity of [0..1], which is valid if all subsets are either emty or of the same value (the union of all subsets will evaluate to emty or 1 value).

LVOOP:

I realize the derived union/subset concept by overriding the derived properties accessor VI to add the subsets that are declared in this class. (Q1)

So these Accessors (for a correct multiplicity of [0..1]) return an array which is either empty or contains n elements of the same value. So I wrote a vi 'remove duplicates' (I will also need to check for a valid Multiplicity). So input and output of this VI is an array of LVObject. As far as I got, I will always need a for-loop with a to more specific prim. (Q2)

So far I have only used an array of objects. I haven't explored other concepts of managing the List/Set/Bag. The most simple idea would to use an array of DVR's to the objects. Others would be a by-ref framework class such as linked lists instead of the arrays. (Q3)

Q1: As far as I know, we don't have any 'abstract' statements in LVOOP. Can you point me to same concepts of implementing this?

Q2: If I only have a scalar of object, not an array, I can propagate the class type to the output either by dynamic dispatching or the Preserve Run-time Class prim. Any chance I could do this for an array of objects? The array prim's work fine if I'd inline the code... If we can't do this, I'll go and write something for the idea exchange.

Q3: Will I be able to avoid the type casting if I use any of these concepts?

Felix

Link to comment

Thanks Ton for the reminder. But I'm pushing back the design patterns to a later stage of this project.

First of all, I'm not ignorant of these. I read the GoF book 10 years ago and I frequently re-read them on wikipedia (and if they seem to be relevant, I can cross-check the german and english wikipedia + the linked articles/implementations).

Second I use the eclipse implementation (it's OpenSource) as a cross-reference. I guess they did a better design job than I am able right now.

So I'm more struggling with getting these things done in LVOOP with the native by-val behaviour and a lot of things lacking (interfaces, abstrac statement, generics).

Looking at the java eclipse implementation, I really think that interfaces would do us a favour in the design process (which I really think is a big reason to migrate to OOP, I mean the better 'tools' to do design before coding). In the eclipse code, all uml classes are implemented as interface first, which (in this specific case) is nothing else than designing all function/procedures/methods/properties as a prototype first. In a second step (this is a different folder in the project called 'impl') they do actually implement all these objects (because there is only this single implementation, the other use-cases of interfaces are not applicable).

They also have another folder in the project called 'operations' (or was it 'util'), in which all static declarations go (named <Interface>Operation, the implementations have <Interface>Imple as name). I must confess that until now, I never thought about using static-dispatch (what are the advantages over dynamic dispatch?); furthermore it seems alien to me to have all these static methods in a different hierarchy (there are calls mainly/only? forth and back between <thisClass>Impl and <thisClass>Operations).

Felix

Link to comment

Felix, I don't quite understand all the issues you're talking about, but I'll answer the questions as best as I can. (They're not very good answers though...)

Q1: As far as I know, we don't have any 'abstract' statements in LVOOP. Can you point me to same concepts of implementing this?

Nope, no way to define something as abstract in LV. In Java if a class is abstract it cannot be instantiated. In LV everything you drop on the BD is instantiated, so abstract classes don't really fit into Labview's paradigm. You can do some things that give you a few of the same behaviors, but I've never run across a situation where I thought it was worth the effort.

In LV'09 you can mark all the methods as Must Override in the class properties dialog. This doesn't stop someone from dropping the object itself on a BD which you obviously wouldn't be able to do with an abstract class. That means you may need to raise an error in all the abstract class methods in case someone sends an abstract class object through your algorithm.

If you really want a class that cannot be instantiated by users, you can put the class in a .lvlib, make the class private, then provide wrappers at the lvlib level for each method in the class. That will prevent users from dropping objects of that class on the BD but still gives them access to the class methods. Unfortunately this also prevents users from creating subclasses of your abstract class (since it is private to the library), which kind of defeats the purpose of creating it in the first place.

In general if I have a class I expect users will subclass I'll provide a simple default implementation so there's at least *some* functionality there. I think that simplifies the api a lot.

So input and output of this VI is an array of LVObject. As far as I got, I will always need a for-loop with a to more specific prim.

Q2: If I only have a scalar of object, not an array, I can propagate the class type to the output either by dynamic dispatching or the Preserve Run-time Class prim. Any chance I could do this for an array of objects? The array prim's work fine if I'd inline the code... If we can't do this, I'll go and write something for the idea exchange.

There's nothing (afaik) that stops you from iterating through an array of objects and calling PRC on each of them, as long as each object in the array is an instance of or child of the target class. I'd need to better understand the context of what you're trying to do to say whether or not it would work in your specific situation. It's not clear to me why you need it in first place. Can you provide an example of how you expect users to use your code?

So far I have only used an array of objects. I haven't explored other concepts of managing the List/Set/Bag. The most simple idea would to use an array of DVR's to the objects. Others would be a by-ref framework class such as linked lists instead of the arrays.

Q3: Will I be able to avoid the type casting if I use any of these concepts?

In general no. Collections are just different ways to store objects in memory so you can easily store and retrieve them. If an upcast or downcast is required then it's required--you have to put it in there somewhere. Your only choice is in where to put it... are you going to do for the user or require them to do it themselves? The way I've implemented the Bag framework it's pretty easy to create front ends for each specific class so users don't have to do the downcasting themselves, but if there are lots of different classes it may not make sense to implement a front end for each one.

In the eclipse code, all uml classes are implemented as interface first, which (in this specific case) is nothing else than designing all function/procedures/methods/properties as a prototype first.

Just to clarify, they are defined as abstract classes, not Interfaces, right?

I must confess that until now, I never thought about using static-dispatch (what are the advantages over dynamic dispatch?)

If you have a method that you don't want child classes to override, then you make it static dispatch. I use them on occasion but most of my public methods are dynamic dispatch.

Link to comment

Nope, no way to define something as abstract in LV. In Java if a class is abstract it cannot be instantiated. In LV everything you drop on the BD is instantiated, so abstract classes don't really fit into Labview's paradigm. You can do some things that give you a few of the same behaviors, but I've never run across a situation where I thought it was worth the effort.

Thanks for this overview. The concepts (Must Override, private in a lib) cover the ideas I came up with. Your experience did add some new aspects. Mainly this assured me, that I didn't miss anything.

'Abstract' wasn't completely meant in the sense of the 'abstact' statement in other languages, but in the use of all this meta-modelling im into. I could think about instanciating an abstract class but not being able to call the abstract methods. This would be the other way round. I see that you might want to accept the abstract parent class and call the abstract methods of this class because the call dispatches to the child class that has these methods implemented. But I already can do this with interfaces.

So an 'abstract' statement is just one kind of realization. I'll dig & think further on this topic.

Just to clarify, they are defined as abstract classes, not Interfaces, right?

They are interfaces. I guess this is to support for multiple inheritance.

But I also see a design advantage in this approach. You focus on inheritance and the method declaration only. The implementation is done in a step further down the road.

If you have a method that you don't want child classes to override, then you make it static dispatch. I use them on occasion but most of my public methods are dynamic dispatch.

Doesn't make sense for the implementation of eclipse.uml2: the static methods are in the <Class>Operations class and not in the <Class>Impl class. We might be missing something.

Felix

Link to comment

There's nothing (afaik) that stops you from iterating through an array of objects and calling PRC on each of them, as long as each object in the array is an instance of or child of the target class. I'd need to better understand the context of what you're trying to do to say whether or not it would work in your specific situation. It's not clear to me why you need it in first place. Can you provide an example of how you expect users to use your code?

Both vis just pass the input to the output. One with LVObject and one with an array of LVObject. Using an object, I get the output of the same class. Using an array, the class type doesn't get propagated. Also, the array Prims handle the class-type propagation.

Felix

post-15343-127488970847_thumb.png

Link to comment

Both vis just pass the input to the output. One with LVObject and one with an array of LVObject. Using an object, I get the output of the same class. Using an array, the class type doesn't get propagated. Also, the array Prims handle the class-type propagation.

Ahh... I see. AQ calls the input and output "thralled" and that is what allows the class type to propogate through the sub vi at edit-time when it otherwise would not. There is a bit of discussion here and here about thralling. I think it's a convenience more than anything.

To restate this using my terminology... "If the input terminal is successfully propagated to the output terminal then the output terminal is thralled to the input terminal. Given this guarantee, when a subVI is written to take a parent class but the subVI node's input terminal is wired with a child class, automatic downcasting can convert the output terminal to match the input terminal."

It's not completely clear to me what is and isn't allowed for the terminals to be considered thralled. Some things, like putting a new object constant on the wire, break thrall. I believe object arrays are not thralled because it's much harder to guarantee all the objects in the array are able to be automatically downcasted. In your example, if inside the array sub vi you were to append a LVObject to the array, that would cause the downcast at the output terminal to fail. Rather than write mounds of type checking code and require Labview to go through the time consuming process of tracing through all the execution paths to make sure your object array output terminal is thralled, I suspect they just decided to disable thralling for arrays.

Link to comment

I had read some of the posts where AQ was talking about the thralling. I really like this feature, because I get a design-time type-check instead of a run-time type-check (errors when I cast to a wrong class).

I was thinking about which structures would need a thralling-feature and a came up with - only arrays.

Also, the array-prims will determine the output-type if they are 'inlined'. So it seams to be almost-there. What I didn't think about is nested subVis with thralled array terminals.

If I find time, I'll place this in the Idea Exchange.

Felix

Link to comment

I did make some progress concerning multiple-inheritance. Because I won't have time to continue on this the next weeks, I want to share my current approach.

The first thing is inspired a lot by Daklu's Interface Framework.

Basically I just have an array of objects called step-parents. When I want to cast to a step parent, I check through this array with the PRC if I find it. Then my Child just needs to wrap the calls and pass them to the step-parent class. Here it's simpler than the Interface Framework, where the Interface needs to call back to the class. Also I need an inheritance step to make the protected vi's available. I think it's easy to understand this from the uml draft.

Now we come to the real tricky thing: the diamond problem. For the methods, it's a decsion of the designer weather to wrap the function from the step-parent or call it's own ancestor. Much more difficult is dealing with the class private data. So we go: the class private data is DVR to the real data. In each Initialization call-chain (going with call parent through the inheritance chain and then creating the step-parent objects), I check weather this object is already present genalogy-array. If yes, the PRC is returning my an object of the same type as 'this', so I can (that's really cool!) access the private (!) data of the object and use it as the value of my DVR ref to the private data.

So all object's through the multi-inheritance tree that or of the same type have the same 'shared' private data!

The nasty thing is, that at the moment it seems to be necessary to code the Initialization for really every class, but this can be automated.

Still an issue is this: If you cast to a step-lineage, dynamic dispatching is not working.

Felix

post-15343-127515033457_thumb.png

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.