06-03-2013 01:48 AM - edited 06-03-2013 01:48 AM
Hi, all.
My users want classes that correspond to physical measuring devices. This is fine so long as the device does one thing: temperature measuring devices all inherit "GetTemperature.vi" from the parent "Thermometer" class, and same with pressure measuring devices. But what happens when a device measures both temperature and pressure (and the four or five other things)?
So far I've been shoving all types of measurements into a single parent class, and any measurement that the child doesn't support gives a runtime error through overriding the "Get___.vi" methods that it doesn't support. Not only is that clumsy to program (most devices are single-measurement devices), but it relies on a runtime error for something that's known at compile time!
How are other people handling this sort of situation?
06-03-2013 06:49 AM
Parent: Device Class
Child class 1: Thermometer
Child class 2: Pressure gauge
Child class 3: Thermometer + Pressure gauge
VI Get_Temperarure must be defined twice, for child class 1 and 3.
I don't see any simpler solution.
Maybe you'd need multiple inheritance, but it's not handled by labview and probably never will.
Any other idea?
Marco
06-03-2013 07:53 AM
As they say, down that road is madness 🙂
There are probably five or six measurement types: temperature, pressure, gas flow, voltage, current. Off the top of my head, I can think of nine different classes that would be needed for combinations of these measurements.
But the worst part is that the benefits of superclassing is lost: once my meter is of class temp+pressure it can no longer be in an array of temp objects or inherit code from either temp or pressure.
06-03-2013 08:48 AM
You can have a class that have different classes as its private data. This is what you typically do when your class system does not allow for multiple inheritance.
Class Container
Data
a <- Temperature
b <- Pressure
Etc.
06-03-2013 09:32 AM
Aggregation will be your friend in this case, as Joseph_Loo talks about, since multiple inheritance is not permitted. In this case, if your device is called "Molly", you can create a temperature measurement class called TempMolly and a pressure measurement class called PressMolly. Both of these will have an object "RealMolly" in their attributes (aggregation). RealMolly will represent the actual device while Temp and PressMolly will represent the measurements.
Now, RealMolly would ideally be the same object shared between TempMolly and PressMolly, hence this would need to a referenced object. There are a couple of techniques for this available. Alternatively you need to make sure that the data needed for RealMolly is shared between two standard ByValue objects.
When it comes to your Get____.vi, if your users only use the top level device class I would suggest that you implement the error generation in this class and leave the implementation out in those classes that don't have any implementation for the method. If an object then is called with a method it hasn't implemented the device class will generate the error (e.g. "call to abstract method").
There are more elaborate patterns for these cases as I'm sure you are aware of, but this could be a good starting point for your users.
06-03-2013 10:07 AM
Thanks for the suggestions, guys, but so far none of these really fit the needs.
The container class pattern prevents both the bookkeeping of inheritance and the functionality of dynamic dispatch, while the "share object between classes" pattern prevents dynamic dispatch, requires a lot more programming, and lets users commit what should be compile-time erros.
Right, LabVIEW does not and will not have multiple inheritance. Fine, but what is the National Instrument alternative?
06-03-2013 04:49 PM
If I understand you correctly your users want one class per instrument, but are using the top "device" class for all interaction with these instruments, which leads to the possibility of calling e.g. "GetPressure.vi" on a temperature device, resulting in a runtime error. If this is the case, and you want to continue with the "a class is an instrument" paradigm, the use of the "device" class is the problem. If you could make your users use the classes "Thermometer" and "PressureSensor" instead you would be able to get edit time errors instead, while using the "device" class for methods and attributes that exist for all device classes.
Now this doesn't address your issue with the device of many measurements where you would need the multiple inheritance. In this case, the device does not fit in the design pattern of one class per instrument, so something needs to be done. NI:s alternative to multiple inheritance would be the same as for e.g. Java, it's a matter of how you design your application. If you or your users don't want to change the design you'll either have to live with the shortcomings of it and create a new family of devices, use a shoehorn to come up with a solution like using aggregation in this particular case, or refactor to a design that addresses this problem from the start.
06-04-2013 01:17 AM - edited 06-04-2013 01:17 AM
Hi!
Interfaces could be a useful tool to achieve your goal.
(So we'd better Kudos this idea: http://forums.ni.com/t5/LabVIEW-Idea-Exchange/LVOOP-Interfaces/idi-p/960890)
Marco
06-04-2013 12:26 PM
M_Peeker, I'd take exception to the suggestion that our paradigm is the problem here. The customer is always right! 🙂
Right, my users have specified that it must be one object per device. They aren't LabVIEW experts, and "you call call the same GetTemperature.vi on any temperature object" is about as far down the road they'll go since they see that as neat enough to ignore the confusion of how it actually works. Any design that involves multiple objects on the screen for a single physical device brings the confusion back (and gets me yelled at).
I see other forum threads and articles from NI saying multiple inheritance isn't needed because anything that requires it can be done in other ways. So, I was hoping I was missing something.
So it looks like here's an example of where LabVIEW is coming up short for not providing that functionality.
06-05-2013 12:20 AM - edited 06-05-2013 12:31 AM
@crcarlin wrote:
M_Peeker, I'd take exception to the suggestion that our paradigm is the problem here. The customer is always right! 🙂
Right, my users have specified that it must be one object per device. They aren't LabVIEW experts, and "you call call the same GetTemperature.vi on any temperature object" is about as far down the road they'll go since they see that as neat enough to ignore the confusion of how it actually works. Any design that involves multiple objects on the screen for a single physical device brings the confusion back (and gets me yelled at).
This statement may not be liked, but I disagree...the customer is not always right. You say it yourself, they aren't LabVIEW experts; they have hired you to be that. Why are they limiting your architecture? Why do they care about how your classes are defined? Normally the user is just concerned with functionality and what the UI looks like, but not how you get that functionality. You may find that having a candid discussion with them proves beneficial. I know this doesn't directly answer your question, but it is definitely something to consider. I often find customers can be very understanding if you can be confident and convince them that you are making the right decision.
My initial thought is you are not differentiating between a device, and the measurement it takes. I think you need a device class, and a measurement class. Each device holds in its private data the different types of measurements that it can take. This doesn't give you an explicit, detailed answer but is this more in line what you are looking for?
I will also say sometimes we get so caught up in dynamic dispatching that we try and force it where it may not be the best choice. It seems great: you have all these measurements you need to take, just loop through them and call dynamic dispatching on each one...but when you start getting empty methods in parent classes just so a few children can DD them while other classes do nothing it can be a code smell that you are forcing something. Just something to consider, I'm not saying it's the case here.