Serializing objects that are only available through a factory

H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

I have some classes (wrappers around a string, each with their own
semantics) which can only be created through a factory method, which
ensures that there is always only one object of the given class with the
given name (String). I want to make these classes Serializable. What
is the best way to do this?

Example class:

import java.util.HashMap;
import java.util.Map;
public class FirstOrderVariable {
private static final long serialVersionUID = -6957388990994668892L;
/**
* Factory method to get a variable. If a variable with the given name
already
* exists, it is returned.
*
* @param name
* The name of the variable.
* @return The unique variable with the given name. If the name is not
* effective, a variable with a random name is returned. | if
( name
* != null ) | then result.getName() == name
*/
public static FirstOrderVariable getVariable(final String name) {
FirstOrderVariable result;
if (name == null) {
result = new FirstOrderVariable();
} else {
result = FirstOrderVariable.variables.get(name);
}
if (result == null) {
result = new FirstOrderVariable(name);
FirstOrderVariable.variables.put(name, result);
}
return result;
}
private static Map<String, FirstOrderVariable> variables = new
HashMap<String, FirstOrderVariable>();
private final String name;
private FirstOrderVariable() {
name = "v_" + FirstOrderVariable.getNumber();
}
private static int getNumber() {
return FirstOrderVariable.totalNumber++;
}
private static int totalNumber = 0;
private FirstOrderVariable(final String name) {
this.name = name;
}
}

I know I should introduce the method readObject(ObjectInputStream in),
but I am unsure what to do in there. Add the name - var mapping to the
static map, yes, but what if there already is a variable with that name?
I can’t return another object from readObject.

Of course, if I deserialize a Formula in which the variable is
contained, inside of that formula, there will only be one variable with
the given name, but if I deserialize multiple formulas, even if
originally they had the same variable, I cannot guarantee that they will
use the same variable now, can I? Even when, maybe the variable has
been created in my application already (likely for names like “x†and
“yâ€), these have to be the same too.

Grateful for suggestions.

H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iEYEARECAAYFAkk1LfQACgkQBGFP0CTku6N9AgCfcQldWPfmj0K4TU17eD0PuXjB
oR0An2DgawVz06c6xu2mPlwy+r2J/32e
=nrpG
-----END PGP SIGNATURE-----
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hendrik Maryns schreef:
Hi,

I have some classes (wrappers around a string, each with their own
semantics) which can only be created through a factory method, which
ensures that there is always only one object of the given class with the
given name (String). I want to make these classes Serializable. What
is the best way to do this?

Example class:

import java.util.HashMap;
import java.util.Map;
public class FirstOrderVariable {
private static final long serialVersionUID = -6957388990994668892L;
/**
* Factory method to get a variable. If a variable with the given name
already
* exists, it is returned.
*
* @param name
* The name of the variable.
* @return The unique variable with the given name. If the name is not
* effective, a variable with a random name is returned. | if
( name
* != null ) | then result.getName() == name
*/
public static FirstOrderVariable getVariable(final String name) {
FirstOrderVariable result;
if (name == null) {
result = new FirstOrderVariable();
} else {
result = FirstOrderVariable.variables.get(name);
}
if (result == null) {
result = new FirstOrderVariable(name);
FirstOrderVariable.variables.put(name, result);
}
return result;
}
private static Map<String, FirstOrderVariable> variables = new
HashMap<String, FirstOrderVariable>();
private final String name;
private FirstOrderVariable() {
name = "v_" + FirstOrderVariable.getNumber();
}
private static int getNumber() {
return FirstOrderVariable.totalNumber++;
}
private static int totalNumber = 0;
private FirstOrderVariable(final String name) {
this.name = name;
}
}

I know I should introduce the method readObject(ObjectInputStream in),
but I am unsure what to do in there. Add the name - var mapping to the
static map, yes, but what if there already is a variable with that name?
I can’t return another object from readObject.

Of course, if I deserialize a Formula in which the variable is
contained, inside of that formula, there will only be one variable with
the given name, but if I deserialize multiple formulas, even if
originally they had the same variable, I cannot guarantee that they will
use the same variable now, can I? Even when, maybe the variable has
been created in my application already (likely for names like “x†and
“yâ€), these have to be the same too.

To answer my own question: I found
http://java.sun.com/developer/JDCTechTips/2002/tt0205.html and
consequently added the following method to the above class:

/**
* If there is already a variable with the name of the deserialized
* object, return that variable, otherwise register this variable
* with the class.
*
* @return The variable registered in this class with the name of
* the deserialized variable. May be the deserialized
* variable itself.
* @throws ObjectStreamException
*/
@SuppressWarnings("unused")
private FirstOrderVariable readResolve()
throws ObjectStreamException {
if (!FirstOrderVariable.variables.containsKey(this.name)) {
FirstOrderVariable.variables.put(this.name, this);
}
return FirstOrderVariable.getVariable(this.name);
}

I think this is the proper solution. Feedback still welcome of course,
and does anyone have a suggestion as to what to put in the comment to
the throws clause?

TIA, H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.9 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iEYEARECAAYFAkk1NG8ACgkQBGFP0CTku6MRKgCfeJ1GL9YFLj/kKrHpEZ4Sq+ql
piUAoJB5Fo52DfnuRHvYRtciWqGthOR9
=ViOA
-----END PGP SIGNATURE-----
 
J

John B. Matthews

[...]
To answer my own question: I found
http://java.sun.com/developer/JDCTechTips/2002/tt0205.html and
consequently added the following method to the above class:
[...]
private FirstOrderVariable readResolve() [...]

I think this is the proper solution. Feedback still welcome of course,
and does anyone have a suggestion as to what to put in the comment to
the throws clause?
[...]

The article cites Joshua Bloch's item 57 from the first edition of
Effective Java, "Provide a readResolve method when necessary". The
second edition, item 77, adds to that advice, saying one "should use
enum types to enforce instance control invariants wherever possible."

For comparison, I looked at the JScience library, which implements
Serializable for Polynomial Functions of one or more Variable(s). The
authors appear to be planning to use XML to manage variables by name;
sadly, the feature is marked TODO in version 4.3.1.

My proposed exception message: "The variable name <" + this.name + ">
was a complete surprise to me at this point." Cf.:

<http://www.frivolity.com/teatime/Computers/MPW_errors.html>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top