Jump to content

Problems with child classes containing data of a different type to


Recommended Posts

I have a class: "diagnostic" to represent a diagnostic in an experiment, such as a camera or oscilloscope, derived from this base class are more specific classes such as "camera", which overrides the basic methods of "diagnostic" such as "initialise" and "capture data". In the private data of "diagnostic" there is a field "data". The problem is that different diagnostics have different types of data, say an image or a 1-D array of floats.

Which diagnostics are used depends on the experiment and what's happening in the experiment. So what I want to do is simply have an array of objects of a type which inherits from "diagnostic" and use dynamic dispatching to call all the relevant "capture data" methods. However, because the data is of different types I can't store the data in the parent class easily. At the moment I am converting the data to variant type and storing it in the "diagnostic" class' private data. So my first question is whether this is the right approach for solving this particular problem?

Not only do I want to capture the data, I also want to store it. Now, each diagnostic should know nothing about how its data is to be stored, as different experiments will want to store data differently but might want to use the same diagnostics as another experiment. So I have another "Storage" object which can be inherited from for a particular experiment. So somehow I need to get the data from the diagnostic object to the storage object and have the storage object save it. Again I would like to do this in a loop where each diagnostic passes its data to a "save data" method of the storage object. Here I hit a problem, different types of data need to be saved in different ways, say to a .bmp file, or a csv file, but I want to only call one VI so I can do it in a loop.

I have thought of some solutions to this, but I can't decide which is the best way to go, if any. I thought of having a lot of different storage objects to deal with the different types of data, but then I need some way of identifying what type of data is being wired in and dynamically dispatching on that (don't really know how that would work). That idea also conflicts with the idea of what a storage object is, as it is supposed to be specific to the experiment, with information such as the root directory and run number, this data would then have to be common to all the derived storage classes.

Another way to go would be to have each diagnostic be responsible for saving its own data, but then this loses the flexibility of being able to save the data differently for different experiments. Also the diagnostic shouldn't have to know about storage specific information, such as the filename and save location of the data, this should be passed to it from the storage object.

I haven't really come up with a satisfactory solution to this problem. I have had a look at the LVOOP Design Patterns, and I don't think they deal with my specific problem, although I could be wrong.

Can anyone put forward ideas to help me solve this problem?

Thanks,

Tom

Link to comment

QUOTE(ibbuntu @ Nov 28 2007, 11:56 AM)

Hi Tom: I was just learning about this stuff recently, and I went through the same confusions. You are on the right track, but you have to think in a slightly different way.

Diagnostic should be an abstract class. It probably won't have any private data. Actually I woud probably call it "instrument", but I will stick with your terms.

Each of your derived objects should inherit from Diagnostic, and override its methods. Since the methods of your abstract class should never get called, you may want to do nothing on their block diagrams except throw an error. None of the standard error codes quite fit, but I chose error 1394 since it's close enough.

QUOTE(ibbuntu @ Nov 28 2007, 11:56 AM)

Which diagnostics are used depends on the experiment and what's happening in the experiment. So what I want to do is simply have an array of objects of a type which inherits from "diagnostic" and use dynamic dispatching to call all the relevant "capture data" methods. However, because the data is of different types I can't store the data in the parent class easily. At the moment I am converting the data to variant type and storing it in the "diagnostic" class' private data. So my first question is whether this is the right approach for solving this particular problem?

No, you store the data in the child classes, which are different, and you don't need any variants. The whole point of this type of design is to get away from using variants. Objects let you use strong typing and still decide at runtime which routines to run.

QUOTE(ibbuntu @ Nov 28 2007, 11:56 AM)

You have to define the interface somewhere. Usually I would imagine you do this in the derived class. If you have a camera, it's going to give you an image, and it's reasonable for the camera object to understand the handful of image file formats you want to support. If you need metadata which is specific to your experiment, you probably don't want your camera object to know about it, but that is probably a separate object anyway (the experimental data) which hopefully does not need to know what kind of instrument generated the data, otherwise your loop is going to get really messy no matter how you slice it.

This common data could be in your parent class, or else it could just be in a different class entirely and passed into the saving routines as a separate input.

QUOTE(ibbuntu @ Nov 28 2007, 11:56 AM)

I have thought of some solutions to this, but I can't decide which is the best way to go, if any. I thought of having a lot of different storage objects to deal with the different types of data, but then I need some way of identifying what type of data is being wired in and dynamically dispatching on that (don't really know how that would work). That idea also conflicts with the idea of what a storage object is, as it is supposed to be specific to the experiment, with information such as the root directory and run number, this data would then have to be common to all the derived storage classes.

Another way to go would be to have each diagnostic be responsible for saving its own data, but then this loses the flexibility of being able to save the data differently for different experiments. Also the diagnostic shouldn't have to know about storage specific information, such as the filename and save location of the data, this should be passed to it from the storage object.

I would think that each diagnostic shouldn't know about the common experiment data. You could make each of them call a method of the parent and cast their data to the parent before writing it in the header of the file. Another way is more of a database-type approach where each stored object is listed in a master file listing the common data, along with a file name which points to a child-specific file which could be images or text files or whatever.

I hope that helps,

Jason

Link to comment

Tom,

I just took a quick look at your post, but from your description this sounds like a great opportunity to apply the "strategy" design pattern. Then a particular "save data" algorithm would operate depending on which type of diagnostic data applies.... Ch. 1 of Head First Design Patterns has a good example....

(You may also apply the template method pattern--ch. 8--as well in the larger context of taking and storing data; and the adapter pattern could apply if you wanted to fit everything into one format, but I don't think that is what you have in mind....)

Paul

Link to comment

Hi Jason,

Thank you for your reply, it was most helpful. I have a few comments ...

QUOTE(jdunham @ Nov 28 2007, 10:08 PM)

I agree that "diagnostic" isn't a good name, I called it that simply because in our experiments we refer to our cameras and diodes and such-like as diagnostics, but I agree "instrument" is a better name.

QUOTE(jdunham @ Nov 28 2007, 10:08 PM)

No, you store the data in the child classes, which are different, and you don't need any variants. The whole point of this type of design is to get away from using variants. Objects let you use strong typing and still decide at runtime which routines to run.

I agree, when I first came up with it I meant it to be an abstract class, but clearly forgot about that in the implementation. I have one problem, which is to do with a "get data" or "set data" function. Now, as each instrument can set its own data internally as in the "capture data" method I mentioned earlier, there isn't a problem. However with the "get data", which would be a method to return the data, there would be a problem as each seperate instrument would output a different data type, which isn't allowed. So they couldn't override a "get data" method from the abstract "diagnostic" class and consequently I couldn't have a dynamically dispatched "get data" method to send my data to a "Storage" class.

Now whether I would/should want to do this is another question. I suspect I still have some fundamental misunderstanding on this front.

QUOTE(jdunham @ Nov 28 2007, 10:08 PM)

I imagine that due to the problem I described above I will have to go along the route of each "diagnostic" saving its own data, using metadata given to it by the "Storage" class.

QUOTE(jdunham @ Nov 28 2007, 10:08 PM)

I would think that each diagnostic shouldn't know about the common experiment data. You could make each of them call a method of the parent and cast their data to the parent before writing it in the header of the file. Another way is more of a database-type approach where each stored object is listed in a master file listing the common data, along with a file name which points to a child-specific file which could be images or text files or whatever.

I hope that helps,

Jason

Finally, I'm not sure I understand what you mean by "cast their data to the parent before writing it".

Again thank you very much for you reply,

Tom

Link to comment

QUOTE(ibbuntu @ Nov 28 2007, 02:49 PM)

Clarity and English will help your design. For example, now you can say, "My 'Instrument' is going to be required to save the common run data from my experiment." Now in the real world, your oscilloscope or your camera are not going to know anything about your specific apparatus, so in the real world, the previous sentence doesn't make any sense. That should tip you off that expecting your child camera class to save your metadata doesn't make much sense either. I'm not trying to be totally harsh about your design, but just trying to make the point that using good names can help clarify your design.

QUOTE(ibbuntu @ Nov 28 2007, 02:49 PM)

I agree, when I first came up with it I meant it to be an abstract class, but clearly forgot about that in the implementation. I have one problem, which is to do with a "get data" or "set data" function. Now, as each instrument can set its own data internally as in the "capture data" method I mentioned earlier, there isn't a problem. However with the "get data", which would be a method to return the data, there would be a problem as each seperate instrument would output a different data type, which isn't allowed. So they couldn't override a "get data" method from the abstract "diagnostic" class and consequently I couldn't have a dynamically dispatched "get data" method to send my data to a "Storage" class.

...

I imagine that due to the problem I described above I will have to go along the route of each "diagnostic" saving its own data, using metadata given to it by the "Storage" class.

This is why your data should be private and Aristos Queue was right to design LabVIEW such that public data is not allowed. If you have a routine which needs your class's data, and that data type is going to keep changing, then that routine should be a member of the child class and should be called via dynamic dispatch.

In other words, what's the point of "returning the data" if the caller doesn't know how to handle it because it could be anything. You should only return a common type, like a Pass/Fail boolean, to a routine which doesn't understand your data. If you need to display the data, then pop up a window which is a member of the class, or use SubPanels which are filled in with a dynamic dispatch VI (I haven't tried that, but it should work). Rendering/Saving/Evaluating the data should be the responsibility of your class, whenever there's no way the caller can know what to expect.

Your Storage class should invoke a dynamic dispatch method of Instrument to get the data written to the file. However, I don't know why Storage should pass the metadata to the Instrument rather than writing it itself. Are you going to store the metadata in some kind of comment field in the BMP file? If so, then any code that is going to recall your metadata is going to have to support all possible file formats. I think you would be better off storing the metadata in a application-specific format (like your own 'spreadsheet' file) and then including the name of the data file (BMP, WAV, CSV) produced by the child object. At that point the child is no longer involved since you can sic your Storage class on the problem of writing out that data, which makes a lot more sense, and supports the point I made in the first paragraph. You can say, "the Storage class is going to store the experiment metadata, and invoke instrument-specific VIs to store whatever data is unique to that instrument," and it sounds sensible.

QUOTE(ibbuntu @ Nov 28 2007, 02:49 PM)

Finally, I'm not sure I understand what you mean by "cast their data to the parent before writing it".

I was having a brain spasm. I was thinking you can convert your object to the parent type inside some of the child methods, but the data is still private so that doesn't work. Some languages let you do that and call it protected data, but not LabVIEW. You can use an accessor method (Get/Set) inside the child VI to get at the common data, but I think you are better off keeping that data away from your child objects.

Link to comment

Thanks, I think I understand now more what I should be doing and am busy re-implementing things. I tried to view data earlier using a sub-panel and a dynamic dispatch VI, but I seem to remember there being problems, due to the VI having to have some re-entrance property that I don't remember at the moment. I am definitely going to want to do this, as popping up a window is not really ideal. I would want to view data from many diagnostics at once, and would like to arrange them nicely. I will have a go once I've got that far in my code and see what happens.

Tom

Link to comment

QUOTE(jdunham @ Nov 29 2007, 08:16 PM)

Ok, I've had a go at creating a "view data" method and using a sub panel to add it to my main front panel and I've found that it doesn't work because a sub-panel requires a reference to a re-entrant VI and Dynamic VIs cannot be re-entrant. Is there another way to achieve this?

[EDIT Ignore this, I was referencing my VI wrongly. I've now got it to work! :)]

Tom

ps. I suspect we have moved on from the original topic of the post. I am new to these forums, how should I go about changing to a new topic whilst still keeping the thread?

Link to comment

QUOTE(ibbuntu @ Nov 30 2007, 03:45 AM)

I suspect we have moved on from the original topic of the post. I am new to these forums, how should I go about changing to a new topic whilst still keeping the thread?

I'm glad your View SubPanel thing is working. Be sure to post if you needed any special tricks. (Or better yet, some example code!)

I would just make a new thread, and put a link to it here, and then in your new thread, start it with another link to this old thread, mentioning that you started a new thread slightly related to the old one.

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.