Category: JavaSpaceIdioms
Intent:
Utilize a wrapping object to serve as a proxy between an Entry object suitable for storage and retrieval in a JavaSpace and the rest of the world.
Motivation:
DirectFieldAssignment on JavaSpace Entries violates encapsulation but Entry attributes are required to be public to enable Associative Lookup.
Solution:
EntryWrapping is the process of utilizing a wrapping object to serve as a proxy between an Entry object suitable for storage and retrieval in a JavaSpace and the rest of the world. Simply put, an wrapping object will contain an Entry, and provide an interface to the Entry in the chosen convention (ie get/set methods for a JavaBeans convention). The wrapping object can also provide for calculated field values, provided that it has access to the appropriate data in the Entry itself. When a write operation needs to take place, a getEntry() method is called on the wrapping object, which returns the actual Entry. On a read or take, either a new wrapping object is created with a constructor that takes the corresponding Entry type, or a setEntry() method is provided to accomplish the same task.
Using the Message class from the first chapter of JavaSpacesPrinciplesPatternsAndPractice as a base, I came up with the following new class which implements EntryWrapping through the use of a nested static class. Note, if you wish to use an inner class as an Entry as I have here, it must be public and static to provide a public default constructor that can be called without an enclosing class instance.
Example (forgive my lack of comments)
public class Message { //instance of the actual Entry, hidden from the outside world private MessageEntry myMessageEntry; //constructors, including one that takes an instance of the //MessageEntry, which becomes important on read and take methods public Message(){ myMessageEntry = new MessageEntry(); } public Message(String content, int initVal){ myMessageEntry = new MessageEntry(content, initVal); } public Message(Message.MessageEntry me){ myMessageEntry = me; } public String toString(){ return myMessageEntry.content + " read " + myMessageEntry.counter + " times."; } public void increment(){ myMessageEntry.counter = new Integer(myMessageEntry.counter.intValue() + 1); } public Entry getEntry(){ return myMessageEntry; } public static class MessageEntry implements Entry{ public String content; public Integer counter; public MessageEntry() { //default constructor required for Entry } public MessageEntry(String content, int initVal){ this.content = content; counter = new Integer(initVal); } } }
EntryWrapping can just as easily be accomplished without the nested classes. By writing the rest of your code to use the wrapping class instead of the Entry class, you are shielding yourself from DirectFieldAssignment while staying in line with the requirements of AssociativeLookup?.
Any comments? The changes to the Hello World and Hello World Client programs can be seen at WrappedHelloWorld? and WrappedHelloWorldClient?.
-- TroyHill?
Just at a quick glance, this looks interesting. I'm not sure though that it is useful to use EntryWrapping solely to avoid DirectFieldAssignment. Why couldn't I just write accessors/mutators and decide to only use those? (Not rhetorical question) On the other hand, EntryWrapping would seem very useful if there is some kind of shared behaviour that you want a particular JavaSpace setup to have. -- JasonYip
True, you can just write accessor/mutators into the Entry and use those, but it still leaves the temptation to somewhere do something like "let's speed up this code a bit by directly accessing this field just this once", and then you've defeated the whole purpose of things. By only giving out the wrapper as an interface to other developers, you are less likely to have this happen.
However, having said that, I must admit that working with EntryWrapping can be awkward. I worked on an implementation of the DistribArray? example from JavaSpacesPrinciplesPatternsAndPractice and found the code to be less than savory. Later on it occurred to me I might be taking things to overkill...after all, the DistribArray? itself is a form of wrapper too, so wrapping its internal components (the Index class, for instance) really isn't necessary, as the "outside" world should never really see these underlying support mechanisms.
So, it comes back to deciding when and where to enforce certain OO practices like encapsulation. Being over-zealous can be a bad thing too. I usually say that if you have a good reason to break a rule, go ahead and do it, but document your reason, and try to minimize its effects on other people's code.
After looking at some more of the Jini 1.1 alpha classes, I noticed that they follow what appears to be this same sort of pattern in the net.jini.lookup.entry package. For instance, net.jini.lookup.entry.Address is the Entry class with the public fields, and net.jini.lookup.entry.AddressBean? is the wrapper to use with the rest of the code. They have makeLink(Entry) and followLink() methods to get and set the wrapped Entry.
-- TroyHill?
A good example representing EntryWrapping is presented above. But this has a problem. Consider the following piece of code that uses the above Message class.
Message.MessageEntry myMessageEntry = new Message.MessageEntry(); myMessageEntry.content = new String("hello"); myMessageEntry.counter = new Integer("1000");This piece of code shows that it is not useful to use EntryWrapping class Message as presented above to avoid DirectFieldAssignment.
We can solve this problem by always having a space entry as an inner class that's private and not public and modifying one of the Message class constructor slightly. The key here is that the Message class returns (getEntry() method) and takes in (constructor) only an Entry. This disables a user from creating an object of MessageEntry type and passing it to the constructor of the Message class. It also disables the user from accessing the public variables of the entry by type casting the entry returned from the JavaSpaces take() or read() operation. But these modifications allow the user to create a Message object with the entry as a private member variable. Only the accessors and mutators provided by the Message class can be used to read or set the values of an entry.
The modified code for the Message class is presented below and the associated Hello world client can be seen at WrappedHelloWorld? and WrappedHelloWorldClient?. I have added accessors/mutators to Message class and modified Entry for the sake of keeping the example simple. The modified Message class is presented below.
Please excuse lack of comments. Modified Message class:
____________________________________________________
public class Message { //instance of the actual Entry, hidden from the outside world private MessageEntry myMessageEntry; //constructors, including one that takes an instance of the //MessageEntry, which becomes important on read and take methods public Message(){ myMessageEntry = new MessageEntry(); } public Message(int counter){ myMessageEntry = new MessageEntry(counter); } public Message(Entry me){ myMessageEntry = (MessageEntry) me; } public String toString(){ return myMessageEntry.counter + " times."; } public int getCounter(){ return myMessageEntry.counter.intValue(); } public void setCounter(int counter){ myMessageEntry.counter = new Integer(counter); } public void increment(){ myMessageEntry.counter = new Integer(myMessageEntry.counter.intValue() + 1); } public Entry getEntry(){ return myMessageEntry; } private class MessageEntry implements Entry{ public Integer counter; public MessageEntry() { //default constructor required for Entry } public MessageEntry(int counter){ this.counter = new Integer(counter); } } } //End of Message class._________________________________________________________________________
These modifications to the Message class avoids DirectFieldAssignment. Any comments?
The problem with this new style of EntryWrapping is that it doesn't provide the necessary mechanisms to freely construct the Entry. One of the requirements for an Entry object is a public constructor. With the Entry here being privately declared, and non-static, you can't freely construct one, or, more importantly, the JavaSpace can't call a default constructor when it de-serializes it. Being non-static, an instance of the outer class (Message) needs to be used to construct the Entry instance. And being private, it can't be called anywhere but from within Message. So, while it's a good idea, and one I tried too, it doesn't work. -- TroyHill?
-- MonalDaxini? (mailto:mdaxini@hotmail.com or mailto:monal@cc.usu.edu)
See also WrappedHelloWorld?, WrappedHelloWorldClient?, DirectFieldAssignment, EntryAsMetaObject
Smells like MementoPattern.