LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
JackDunaway

Selectable Class Data Member Scope

Status: New

Currently, a class is created with a Class Private Data Cluster. Each data member of that cluster is scoped exclusively to the owning class. For access to these data members outside class member VIs, getters and setters (accessors) must be established individually for each element. These accessors can then be scoped accordingly, allowing access to the private class data through the accessor VI, or in 2010, through an accessor Property Node.

 

Quite frankly, I like the IDE interface for accessing class data members using Unbundle/Bundle by Name. This feels natural for LVOOPers who have a background with clustered typedefs (which means everybody). Unfortunately, this method of data access is reserved for callers within the scope of the class data. Currently, since all class data is private, you can only use Unbundle/Bundle within Class Member VIs themselves.

 

I would suggest an options interface for setting the scopes of Class Data Members. Having non-private data member scopes has benefits:

 

  1. Obviates all the piddly accessors clogging your project tree and SCC server
  2. Allows quick visual recognition that a data member is being read/written directly (no data transformations were snuck into the accessor)
  3. Cleaner interface for callers of the class using the Bundle/Unbundle
  4. Best of all, saves considerable development and maintenance cost
Here's an example of what the Data Member Scope Configuration screen might look like:
 
ClassMemberScope.png
 
Note that clusters can have "Custom" scope, which means the cluster's elements have different scopes. Also note that when a cluster's scope is set explicitly, it's members inherit the same scope and cannot be set (note how 'x' and 'y' are greyed under 'Center of Mass'). The default behavior (which is equivalent to today's default scope) is for 'Class Data Cluster' to be 'Private' with all descendants greyed.
This Idea is one product of a discussion which has taken several routes.
11 Comments
jgcode
Active Participant

Note: This is a cross post from the NI Forum Thread to centralize info as requested by Jack.

I am very interested to hear other opinions on this Idea.

Cheers

-JG

 

-----------

 

I would love to see this point discuss in greater details - as it has come up before in local discussions, so I have no links.

Additional to this discussion, I have heard for example "It is painful to have to create Accessor methods just to access data in Inherited Classes..."

 

Obviously there is more overhead/work in using LVOOP but it is worth it. 

Using scoped (private) data with subVIs provide data protection, which is one of the great benefits of LVOOP IMHO.

I think its goes against the principle of LVOOP to do what you are suggesting.

 

Off the top of my head: If you have public data as suggested, I can see that you would lose some benefit of encapsulation - what happens if I need to run some private methods before or after Data Member access? I.e. precheck data in range before exposing.

If this is required then I currently do this in the Accessor Method VI, so this is transparent to calling code.

So if in future something changes and I need to call a private method, and all I had simple bundle/unbundles in my code, I would have to troll through all my code and replace them with subVIs.

Which I could have avoided by using subVIs in the first place. 

What about inheritance? E.g. This Accessor always does this to the data before exposing the data...

 

How would replacing/renaming/changing Data-Members cope?

If I break an Accessor can I quickly go into that subVI fix it (and elsewhere in my Class) and my calling code is protected?

Or am I stuck having to update every bundle/unbundle node in my code - just like I would a type-def?

 

In 2010, the PN kind of provides this feature - easy access but you need have Accessors created for it.

Maybe you could have a PN automatically available for Data Members when a new Data Member is created?

The developer could have the ability to create a custom Accessor Method VI if it was warranted in the future.

This would maintain a consistent API, that would stand up to changes and not break calling code.

 

Certified LabVIEW Architect * LabVIEW Champion
JackDunaway
Trusted Enthusiast

>> what happens if I need to run some private methods before or after Data Member access? I.e. precheck data in range before exposing.

 

That type of class data member should be marked "Private". Accessors would then handle data manipulation/integrity checking/etc.

 

>>So if in future something changes and I need to call a private method, and all I had simple bundle/unbundles in my code, I would have to troll through all my code and replace them with subVIs.

 

If you mark a data member 'public' to get the "easy accessor", you'd better consider the risk of needing to change it to "private" later on down the road because greater encapsulation/protection is needed.

 

>> Maybe you could have a PN automatically available for Data Members when a new Data Member is created? The developer could have the ability to create a custom Accessor Method VI if it was warranted in the future. This would maintain a consistent API, that would stand up to changes and not break calling code.

 

The "automatically available" you refer to would apply to the non-private scopes selected by Radio Buttons above. This is a great idea for keeping the API from breaking (whereas, you're right, a Bundle/Unbundle would be harder to manage). I think PN's might be a better "easy accessor" than the Bundle/Unbundle.

 

 

Going back to my original post: "3. Allows quick visual recognition that a data member is being read/written directly (no data transformations were snuck into the accessor)" - maybe scratch that. It's up to the the class developer to know what's going on behind the scenes, and typically no business of the class user. 

 

 

Finally, keep in mind: this Idea does not supersede or antiquate accessors as they exist today. It's simply a means for a cleaner language when all you need is simple data access, yet you want the features of classes that typedefs cannot offer.

Daklu
Active Participant

With all due respect, I think allowing direct class data access via bundle/unbundle is a bad idea.  I'll grant that it is more familiar to most LV programmers than accessor methods, but it violates one of the fundamental concepts of object-oriented programming--encapsulation.  It is a short cut that on the surface appears helpful but in fact has consequences that may not become apparent for weeks, months, or even years, when it's too late to fix the real problem and you have to resort to ugly hacks and work arounds.

 

The whole point of data encapsulation is separate the actual data from how it is presented to the class user.  Encapsulation is what makes OOP so flexible.  If you bypass that via bundle/unbundle nodes you are exposing the class to the same problems typedefs have and end up with an application that is very rigid and brittle.

 

If you want the simplicity of unbundling a cluster, use a cluster and accept the consequences of the decision.  If you want the flexibility and protection of classes, use a class and accept those consequences.  But please don't poke holes in class encapsulation for the sake of syntantic convenience.  A better solution is to make accessor methods easier and faster to write.  (Proprety nodes are a step in that direction.)

 

 

"If you mark a data member 'public' to get the "easy accessor", you'd better consider the risk of needing to change it to "private" later on down the road because greater encapsulation/protection is needed."

 

I can't speak for others, but I've never written a piece of code I know won't need to be changed later.  By exposing the underlying data directly you are essentially putting a stake in the ground and saying, "this won't change... ever."  Given the evolving nature of software requirements I don't see how a prudent programmer can make that claim.

 

In my quest to learn OOP I struggled with figuring out how to correctly design applications.  I have seen others struggle with the same things I did.  It took me well over a year and several bad designs before I started seeing any of the claimed benefits.  Because it is familiar and easy direct access to class data via bundle/unbundle will be an obstacle to learning and creating good object-oriented designs.

JackDunaway
Trusted Enthusiast

OK, this Idea needs some major tweaking to make it more palatable.

 

I'm not going to disagree with Daklu about all the extra accessor VIs. "Clogging your project tree and SCC server" is probably overcome by the benefits he outlined, so perhaps there's not a better alternative than a 1:1 relationship between a VI on disk and an accessor method.

 

Using Bundle/Unbundle in scopes other than "Private" probably violates syntactical analogies of a "scope resolution operator" in established languages. That being said, a Property Node is a better candidate for "easy accessor" since it has a precedent as "syntactical sugar".

 

So, let's keep the dialogue (picture in original post) for now, but let's turn it into a Class Properties tab. When non-private bubbles are selected, it triggers the scripting of accessor property nodes scoped as chosen. Basically, it becomes a way to quickly create or modify property node accessors to class data members.

 

Comments?

 

(I also acknowledge my participation in this conversation falls under heavy jurisdiction of "Dak's First Law of Problem Solving")

PJM_Labview
Active Participant

I do like the idea of being able to directly access public class data just using an unbundler.

 

I understand Daklu argument, but one might say that if you want to run a private method (like a range checking) on a public data before exposing it that the previous data you run your check on was private data. Alternatively, you could associate (a little bit how you create property on XControl) a method to be run (behind the scene) when a user drop an unbundler.

 

Note: It is possible that this unbundler node may need to be different (might need to have error terminals).

 

I also like the idea of specifying the scope of individual class data member.



  


vipm.io | jki.net

TylerDurden
Member

I agree with Jack. When you have the possibility for public data you get closer to C# OOP, where nearly every cluster/struct is a class. When you're simply using a cluster for bundling information you don't want to write accessors for every variable inside. It's clear to me, that this 'shortcut' isn't the correct way of implementation, but could save a lot of time when you know what you're doing.

Certified LabVIEW Architect
Intaris
Proven Zealot

I don't agree with this idea.

 

Yes, I see the time it would save.  Yes there have been times where I have wanted to do exactly what is shown here.  Yes I feel there could be -something- done in this direction....

 

BUT

 

I feel that this will simply muddy the waters when people are trying to move to LVOOP, something which still really needs to be taken account of.  I would be more in favour of having default class accessors (without the need for an actual VI being saved to disk).  Simple unbundle like you suggest (without error clusters since it's essentially a no-op aside from a buffer allocation) but with the option of being public, community or private.

 

You then have the choice to implement a different accessor in the base class (for static accesors) or override it in a sibling class (Overriding).  This would significantly lower the number of "piddly accessors" on disk without watering down the principles of OOP.

 

Another problem I have with the idea is if, during development, you want to change a data member from public to private.  You then have to go through your code and replace all the unbundles with accessors.  That's surely a PITA.

JackDunaway
Trusted Enthusiast

Three years after suggesting this, having learned a little about object-oriented design in the meantime, I still like the spirit of this idea: offload burden from the application layer to the language level where possible.

 

One good example: Protected Accessors. Why must ancestors create protected accessors for their descendents, rather than simply marking certain fields as protected-accessible?

 

That said; I've learned that highly-stateful objects with many fields are a code smell (not saying 'wrong'; just saying 'investigate'), and as statefulness of objects has decreased substantially, so has this burden.

BriceS
NI Employee (retired)

It seems to me this discussion got bogged down in how this could cause problems when making class data as public. But the biggest problem this could solve is what Jack refers to in his last comment: marking class data as protected would be a huge benefit to developers of LV classes. It's very frustrating and cumbersome to have be forced to make protected accessors for every data member that you want to allow child classes to access. And LVOOP has so many cumbersome things you have to do to work around the language that anything we could do to make it easier should be done.

Intaris
Proven Zealot

Allowing access to data directly from unbundle within a class hierarchy is not as simple as it seems.  It must actually follow exactly the same rules as accessing class data from completely outside the class hierarchy.  This is, I think, also the reason why child classes are actually encapsulated with regard to parents and vise versa.

 

Why?

 

Children need to be ancapsulated from their parents due to the possibility of loading a class at run-time.  While you might know the structure of a parent at compile time (compile time of the CHILD class), at run time when it's finally loaded, the parent may have changed.  How to handle this? Encapsulation and accessors.  Otherwise even renaming an internal field would lead to problems and would be overly brittle.  Even worse, what if the definition of the parent class has changed so that the datafield is not private but has an associated accessor.  If we used a default accessor notation (where "public" data fields were still accessed via virtual accessors which don't actually exist on disk) then we would have a handle to actually deal with the differences.  If we have previously accessed via unbundle, there is NO appropriate to migrate from public to private access.  If we would even consider going the route of "default accessors" (which would be direct accessors to data fields) then it would be much much easier of the notation was via VI for both private and public data fields.