Jump to content

A class within a class - breaking the private data boundary


Recommended Posts

This is one that I've had in the back of my mind for a while. Imagine that you have an object like a chess board*, and that object has objects in it, like the chess pieces. Let's say I create a chessgame object who's data has some info about the board (like the colour and size of the squares) and an array of chesspeice objects. Then I want to create a method that saves the current game state - the colours and sizes of the squares, and the locations and types of the peices. I also want a load method that recalls all of these from file as well. So, I want chessgame to have a chessgameFile as an aggregation - basically it's just another chunk of encapsulated data in the chessgame object.

So, I was originally going to create a chessgameFile class that contains the file location (and formatiing info), and could get all of the data from the chessboard (and, hence, [chesspeice]) then save it all to file. Problem is, object data is private its class, so it's not like I can feed a chessboard object into a chessgameFile method and unbundle the chessgame and [chesspeice] object data.

What to do? My first thought is to create methods for all the classes that have data in them that I want to save out to the file (which is, essentially, the right way to do things), but the chessgame and chesspiece classes already exist and a quite complex. I certainly don't want to make methods for each of the classes that expose the data in them, because, well, that's just plain dirty. Anyone got any pearls of wisdom to impart?

* No, I'm not programming a chess program :)

Link to comment

QUOTE (crelf @ Mar 2 2009, 04:24 PM)

This is one that I've had in the back of my mind for a while. Imagine that you have an object like a chess board*, and that object has objects in it, like the chess pieces. Let's say I create a chessgame object who's data has some info about the board (like the colour and size of the squares) and an array of chesspeice objects. Then I want to create a method that saves the current game state - the colours and sizes of the squares, and the locations and types of the peices. I also want a load method that recalls all of these from file as well. So, I want chessgame to have a chessgameFile as an aggregation - basically it's just another chunk of encapsulated data in the chessgame object.

So, I was originally going to create a chessgameFile class that contains the file location (and formatiing info), and could get all of the data from the chessboard (and, hence, [chesspeice]) then save it all to file. Problem is, object data is private its class, so it's not like I can feed a chessboard object into a chessgameFile method and unbundle the chessgame and [chesspeice] object data.

What to do? My first thought is to create methods for all the classes that have data in them that I want to save out to the file (which is, essentially, the right way to do things), but the chessgame and chesspiece classes already exist and a quite complex. I certainly don't want to make methods for each of the classes that expose the data in them, because, well, that's just plain dirty. Anyone got any pearls of wisdom to impart?

* No, I'm not programming a chess program :)

If you have a chessboard object, and its private data includes objects of other classes, then you could simply wire the chessboard object to a datalog file. When you read from the datalog file, it will load all of the data that was saved, including the other classes included in chessboard. Datalog files work really well with classes. I may not have completely understood your question, so let me know if this is what you are looking for.

Chris M

Link to comment

I had thought Labview automatically saved the object's state when writing to disk and would restore the object to that state when loaded. I can't say I've ever tried it though and seeing you ask the question makes me think I was wrong...

Link to comment

QUOTE (crelf @ Mar 2 2009, 02:24 PM)

Well I would think it really is the responsibility of your class to figure out how to transform itself for file reading and writing. This is called Serialization (sorry if you already knew that). If any other code knows how to serialize your data, then you've broken encapsulation because that other code has to know the structure and contents of your object.

QUOTE (Cmal @ Mar 2 2009, 02:50 PM)

If you have a chessboard object, and its private data includes objects of other classes, then you could simply wire the chessboard object to a datalog file. When you read from the datalog file, it will load all of the data that was saved, including the other classes included in chessboard. Datalog files work really well with classes. I may not have completely understood your question, so let me know if this is what you are looking for.

Remember that if your private class data is ever changed, the datalog file functions have to try to figure out how to map the old data into the new data. I would read the "Mutating Data" section of the LabVIEW Help Files before deciding if you want to use the built-in functionality, which seems quite powerful, but also seems like it might not be safe enough for a big multi-developer project in a production environment. I would just write a "Serialize as XML Fragment" method for each class (and the Deserialize method too) and then test the bejeebers out of them, especially if you have to change the data type.

Even if you do use the built-in datalog functions, I think you should only call them from methods owned by your class. Then at least if you run into problems, you have a way to track down all the places where it might be happening.

Jason

Link to comment

QUOTE (crelf @ Mar 2 2009, 05:24 PM)

* No, I'm not programming a chess program :)

To keep with the analogy, I'd make a public Save method for each of your classes so they could be called independantly. "Chess Board" would save itself and call public "Save Chess Piece" for each of the pieces in its array, then call public "Save Board Properties". You wouldn't really need the ChessGameFile class in this paradigm.

Link to comment

Assuming that you have stayed entirely by-value and don't have anything that is by reference, you should be able to

1. Drop the "Flatten To String" node

2. Wire your "Chess Game" object to the data input.

(Or the datalog write, as suggested earlier, or the XML write)

For read, use the Unflatten From String node, with your Chess Game as the input type.

Link to comment

QUOTE (crelf @ Mar 2 2009, 05:24 PM)

I'd argue the opposite, doing otherwise is plain dirty. A second class, or another VI should never need knowledge of the class exposed to it at all, that's part of the reason a class has a private and public interface.

QUOTE (jdunham @ Mar 2 2009, 06:20 PM)

Well I would think it really is the responsibility of your class to figure out how to transform itself for file reading and writing. This is called Serialization (sorry if you already knew that). If any other code knows how to serialize your data, then you've broken encapsulation because that other code has to know the structure and contents of your object.

This. Keeping your data encapsulated in one place is very important, and one reasons for adopting an objective design to many projects. Only your class should know how to rebuild it's internal structure based off data it deserializes from disk, similarly what to throw away when serializing to disk.

Define an interface (preferably an actual dynamic dispatch) that is called by your chess game. The game will in turn call it on each contained piece, etc, until it's all done. Keeping it dynamic helps a lot too when you have long inheritance chains...each class handles it's own data then passes the request onto it's ancestor.

Link to comment

QUOTE (Cmal @ Mar 2 2009, 05:50 PM)

If you have a chessboard object, and its private data includes objects of other classes, then you could simply wire the chessboard object to a datalog file. When you read from the datalog file, it will load all of the data that was saved, including the other classes included in chessboard.

That is, indeed, an awesome solution, but it doesn't acheive what I want. That saves everything that's in the object, and nothing that isn't. I should have tried to be more clear - this is no ordindary pattern due to some limitations in how LabVIEW controls are implemented. But that's not important right now.

Let's say that the chesspieces have attributes that aren't part of their object (yes, I know, that's mind-bending) - these rogue properties are saved off into individual files that go along with the datalog file. So, I have one file that defines the chess board and some of each piece's properties, and a bunch of little files that define the additional properties for each object.

OK - this is getting confusing, so I'm gonna tell you the real deal: I have an simple datalogging application where users can dynamically create workspaces, drop controls/indicators on them, change properties of those controls/indicators, etc. At the moment, I have a configuration file that holds info about each workspace and the FP nodes that are on it (like thier captions, location, shared variable bindings) but then I actually save off the ctl files for each FP node to retain things like size, background colour, scale marker placement, etc. So, at the moment, I have a bunch of files for each data logging "project" that the user creates. What I would like is a single file - say, a zip file - that has the configuration file (or datalog file as you suggested) and all the ctl files in it too. That sort of encapsulation makes sense to me.

Link to comment

QUOTE (crelf @ Mar 3 2009, 03:41 PM)

That is, indeed, an awesome solution, but it doesn't acheive what I want. That saves everything that's in the object, and nothing that isn't. I should have tried to be more clear - this is no ordindary pattern due to some limitations in how LabVIEW controls are implemented. But that's not important right now.

Let's say that the chesspieces have attributes that aren't part of their object (yes, I know, that's mind-bending) - these rogue properties are saved off into individual files that go along with the datalog file. So, I have one file that defines the chess board and some of each piece's properties, and a bunch of little files that define the additional properties for each object.

OK - this is getting confusing, so I'm gonna tell you the real deal: I have an simple datalogging application where users can dynamically create workspaces, drop controls/indicators on them, change properties of those controls/indicators, etc. At the moment, I have a configuration file that holds info about each workspace and the FP nodes that are on it (like thier captions, location, shared variable bindings) but then I actually save off the ctl files for each FP node to retain things like size, background colour, scale marker placement, etc. So, at the moment, I have a bunch of files for each data logging "project" that the user creates. What I would like is a single file - say, a zip file - that has the configuration file (or datalog file as you suggested) and all the ctl files in it too. That sort of encapsulation makes sense to me.

Can't you save a link to the file within the class and then have a class method to read the appropriate information on loading?

Shane.

Link to comment

I worked on something similar. But request was: in the future we could change few attribute in child class (add or remove). Parameters for visual inspection.

My solution:

Parent class and all children were in one LV library where in name was version number.

All attributes in children classes were in one big Type definition cluster.

All children classes included Import and Export methods and I unflatted/flatted this one big cluster.

Advantage: I could export old version of parameter to new version. When I need new version I copy whole lv library to new file and rename it.

P.S.: Configuration is saved as string (long string) to SQL server ;-)

Link to comment

Here are screen shots of a project I developed that had a similar (I am guessing) structure.

post-29-1236102978.jpg?width=400

THere is some extra stuff done on the "Save" side since my plug-in object where not by value but actually my own version of "by reference" So the For loops queries each object for its current state. But after I have all of the states they just slip into the class data and are saved as a binary.

The restore side was even easier since I knew the state of teh object (read from file) so I did not have to query them but rather re-create them.

I was very pleased will how well this construct adapted to changes in my class data. The only problem I ran into was when I added a field to fix an issue in the object. THe reading from file was fine but when I restored objects that had been saved before my fix was added, they didn't have the required field so they still suffered from the bug that I had just fixed! The work-around for that situation was exactly the same as when we upgrade a LV app and one of the parts is acting stupid, just delete the object and recreate a new one.

Ben

Link to comment

QUOTE (jdunham @ Mar 2 2009, 06:20 PM)

I understand that, but I can't do it any other way - the attributes specific to the chesspiece objects are outside of the OO paradigm.

QUOTE (MJE @ Mar 2 2009, 10:53 PM)

I'd argue the opposite, doing otherwise is plain dirty. A second class, or another VI should never need knowledge of the class exposed to it at all, that's part of the reason a class has a private and public interface.

Sorry - what I meant to say was that I didn't want methods in all of my classes that exposed all of the class data - that makes me feel dirty. Exposing only the things that you need, through a custom transport - I'm okay with that.

QUOTE (MJE @ Mar 2 2009, 10:53 PM)

I totally agree with all of your comments, but the attributes specific to the chesspiece objects are outside of the OO paradigm. There is no inheritance in this model, because there can't be.

QUOTE (JCC @ Mar 3 2009, 12:37 PM)

I worked on something similar. But request was: in the future we could change few attribute in child class (add or remove). Parameters for visual inspection.

Very nice! But I don't think it really applies to my application.

QUOTE (neBulus @ Mar 3 2009, 12:56 PM)

Very nice! But I don't think it really applies to my application.

QUOTE (shoneill @ Mar 3 2009, 10:24 AM)

Can't you save a link to the file within the class and then have a class method to read the appropriate information on loading?

Now you're talkin'! References to the chesspeices perpetuate within the chessgame - then the open/save/etc does the clean up. That I can do! Thanks - I'll let you know how it goes.

Link to comment

QUOTE (crelf @ Mar 3 2009, 07:47 PM)

Cool, hope it works out.

I remember AQ mentioning something about LVOOP implementation where every operation in a program is done http://forums.lavag.org/A-weird-way-to-improve-your-object-oriented-programming-skils-t11413.html' target="_blank">WITHIN the object, i.e. not using ANY standard functions like "Add" or "search 1D Array" and so on. It really made me think about how to keep EVERYTHING inside the class boundaries. I think his example was really extreme, but it certainly helped me "see" proper encapsulation solutions where it otherwise wouldn't have occurred to me.

Shane.

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.