LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Preserving Parent Class Data When Instantiating a Child Class

Solved!
Go to solution

Hi all,

 

It has been brought up before (at least here and here if not in other places) that when using "To More Specific Class" to cast a child to a parent class, the parent's class data is not copied to the child class.  I have a use case where I won't know which child to use until a while into the code, but I still want to access parent data and run parent methods before that cast to a specific child.

 

The only suggested solutions in the above forums were to copy the original parent instance's data to the new child instance.  This works, but it's kind of cumbersome given that the only way I know to do this is with bundle/unbundle functions or property nodes or accessor functions, all of which won't accommodate automatically including new class data items and will break if you remove class data items.  (The former is a much bigger problem than the latter since it will silently fail to include the added class data.)  Basically, I want a stable cast function that can copy the existing parent's data to the new child. 

 

I looked into it a bit more and wrote a quick, hacky workaround involving the flatten/unflatten XML functions, where I get the new child object's XML representation and the parent's XML representation.  I take the portion of the parent's XML that represents the class data I want to maintain and simply copy that into the right part of the child's XML class data.  I then unflatten the hacked together XML back to the child object type, and it actually works.  I can read the original parent's data and can call my abstract parent methods and see that the correct child is dynamically dispatched and returns the child's class data.  Of course, this just proves that what I want can be done.  It is not a tested or production-worthy solution.  It also doesn't answer the question of should it be done.

 

I hope (but can't seem to prove) that there is a more elegant way to programmatically get all of the values of a parent class's data and write them to a child class.  I tried playing with the Data Type Parsing palette, looked in Hidden Gems and Open G, and otherwise poked and prodded to see if there was a better way to do this and didn't get far.  (In fairness, those palettes would likely all just be doing XML manipulation, too, but at least I could blame bugs on others!)

 

Could I architect my code to not write to class data until I've determined which child to call?  I mean, yeah, but is there actually a good, compelling comp sci reason why I shouldn't be able to choose which child I want to use after having called parent methods and written to class data?  Is there a nicer way to copy all of my parent data to the child at once without me having to update the method if class data contents change?  One side of me thinks:  "If it's this awkward, you probably should do this," and the other thinks, "But why not? Are you missing something really simple?"  Hoping to get your thoughts.

 

Thanks!

David

0 Kudos
Message 1 of 8
(4,875 Views)

I looked into this at one point too.  I ended up just doing a manual copy from read accessors of the parent to write accessors of the child.

 

Your XML copying method was one I did look into.  I discovered two flaws in it that you might not have run across yet.  First, any floating point number with a unit attached always unflattens as the default value unless it's nested in a cluster (most people don't use units, so not a big deal).  Second, and more likely to be a thing, all Variant data is lost when unflattened from inside of a class.

 

The one thing I did find was a way to make a "trap" that catches when someone adds something to the parent class data that isn't in the "copy" function.  You can run this code:

Check private class control.png

This generates a complete list of the parent class's elements, whether or not they have accessors.  Call it on the parent class, and then convert the output arrays into constants.  Then generate an error during run-time if the "live" arrays don't match the constants you generated in both size and contents.  Every time you edit the "copy" function from parent to child, update the constants so it doesn't fail during run-time. 

 

I suppose you could run similar code on the XML generated after you flatten it to get the names and data types of all of the XML nodes and compare, but the general method is the same.

 

Or, if you use unit tests, put this in the unit tests to catch it earlier...

Message 2 of 8
(4,845 Views)

When you say you use "to more specific" to cast a child to a parent, I presume you mean casting a parent to a child, right?

0 Kudos
Message 3 of 8
(4,826 Views)

It also seems to me like you need to use the children as wrappers of the parent as opposed to being linked by inheritance.  Then you can simply pass the object from one child to another. As data.

Message 4 of 8
(4,825 Views)
Solution
Accepted by croohcifer

Intaris is onto something here. Your use case sounds more like being a classical case where composition could be used instead of inheritance.

 

Read a bit on the various OOP patterns. There are virtually dozens or more of them but there is a fairly well agreed group of half a dozen or so OOP patterns that can solve common and very often occuring problems. The decision to use composition instead of inheritance is a very common one and one every OOP programmer has at some point struggled with (and some keep struggling 😀).

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 5 of 8
(4,822 Views)

Thanks everyone for the comments!

 

Kyle:  Thanks, that seems to be a solid way to catch errors during run-time, but, as you stated, my unit tests could explicitly catch the mismatch, too.  Seems a little more elegant and robust than the hacky XML approach.

 

Rolf and Intaris:  I do use composition extensively as-is, though I didn't know the name of the design pattern, so thanks for introducing me to that!  In my mind, the functionality I was implementing in the parent before deciding on which child to use was of-a-set with the functions that my children were to call, but I could definitely see a way to encapsulate that initial functionality (and later manipulation of that data) in a separate class not in that inheritance chain.

 

I'll play with both approaches and see what works best for this particular application.

0 Kudos
Message 6 of 8
(4,801 Views)

@Intaris wrote:

When you say you use "to more specific" to cast a child to a parent, I presume you mean casting a parent to a child, right?


Yes, good catch!

0 Kudos
Message 7 of 8
(4,797 Views)

You're not casting classes, you're casting objects.

 

You can only cast an object to something specific if the object is that specific thing.

 

If you have a parent object, and want to change it to a specific child, you have to make methods for that. For instance, give each class in the hierarchy a "To Child" method, where it copies it's private data for a non-DD input, to a 2nd DD input. Then call "To Child" on the new instantiated child, let itself copy the private data from the original object to the itself.

 

There are often other solutions, but we'd need a lot more details. EDIT: Usually composition, as mentioned.

0 Kudos
Message 8 of 8
(4,792 Views)