How to "force" my object never been changed by other other objectsusing it?

C

Chris Dollin

www said:
I have a class, Data.java. Once an object of this class is created,
several other kind objects will use it. For example:

Data myDataObj = new Data(...//params);

UserA userA = new UserA(myDataObj); //pass the object to another object.
double ave = userA.getAverage();

UserB userB = new UserB(myDataObj);
...

I want myDataObj state never been modified by users.

Make all its variables private (possibly: protected) and provide
no setters. [Recurse if necessary.]
 
W

www

Hi,

I have a class, Data.java. Once an object of this class is created,
several other kind objects will use it. For example:

Data myDataObj = new Data(...//params);

UserA userA = new UserA(myDataObj); //pass the object to another object.
double ave = userA.getAverage();

UserB userB = new UserB(myDataObj);
....


I want myDataObj state never been modified by users. Right now, I just
open my eyes widely to make sure that my code does not do it. I am
wondering if I can use some key words to enforce it, like:

final Data myDataObj = new Data(...); //my feeling this is wrong: it
just make sure the reference myDataObj (the memory address in heap being
referenced) cannot be changed. Or, the street address cannot be changed.
But the house can be modified. Am I correct?


How about ...?

public class UserA()
{
public UserA(final Data data) //constructor
{
...//code
}

}

Thank you.
 
W

www

Chris said:
Make all its variables private (possibly: protected) and provide
no setters. [Recurse if necessary.]

Sorry. My posting was not clear. I don't want Data.java completely
immutable. Occasionally, I want to change the object state, like:

dataObj.setName("bar"); //before it was "foo", now it is bar

My first posting and my purpose is: I don't want dataObj being modified
by other objects without my awareness. Such change could be burried in
UserA class' some method somewhere and I am totally not aware of it.
 
C

Chris Dollin

www said:
Chris said:
Make all its variables private (possibly: protected) and provide
no setters. [Recurse if necessary.]

Sorry. My posting was not clear. I don't want Data.java completely
immutable. Occasionally, I want to change the object state, like:

dataObj.setName("bar"); //before it was "foo", now it is bar

My first posting and my purpose is: I don't want dataObj being modified
by other objects without my awareness. Such change could be burried in
UserA class' some method somewhere and I am totally not aware of it.

Make all its variables private (possibly: protected) and provide
setters which report the change to you (or your representative code).

If you make the setters [package] protected, and put your "safe" code
in the same package, users can't see the setter anyway.

If you don't mind the value changing, then I'm not sure why you mind
someone else changing it. But then, I don't know what your design goals
are.
 
W

www

For another better example to show my point:

public class Car
{
final private Engine engine = new Engine(); //even the final word here
may does not serve my purpose

public void drive()
{
...//code
}

public void stop()
{
...//code
}

public void turnLeft()
{
...//code
}

...

}

I do not want Engine being completely immutable. I want to be able to
tune or fix Engine sometimes by myself. But I do NOT want when Car is
doing something(driving, stopping, or ...), Car is tuning or modifying
Engine. That is dangerous!
 
O

Oliver Wong

www said:
Hi,

I have a class, Data.java. Once an object of this class is created,
several other kind objects will use it. For example:

Data myDataObj = new Data(...//params);

UserA userA = new UserA(myDataObj); //pass the object to another object.
double ave = userA.getAverage();

UserB userB = new UserB(myDataObj);
...


I want myDataObj state never been modified by users. Right now, I just
open my eyes widely to make sure that my code does not do it. I am
wondering if I can use some key words to enforce it, like:

final Data myDataObj = new Data(...); //my feeling this is wrong: it just
make sure the reference myDataObj (the memory address in heap being
referenced) cannot be changed. Or, the street address cannot be changed.
But the house can be modified. Am I correct?

You're right that "final" won't do it. I'm assuming that you want to
make Data immutable mainly for reducing bugs, and not for security purposes.

Simply provide two interfaces for your Data object, one which provides
getters, and one which provides setters.

public interface ReadableData {
int getX();
}

public class Data implements ReadableData {
private int x;
public int getX() {
return this.x;
}

public void setX(int newValue) {
this.x = newValue;
}
}

And when you want to make your object immutable, upcast it from Data to
ReadableData:

Data myDataObj = new Data();
ReadableData immutableViewOfMyDataObj = myDataObj;
immutableViewOfMyDataObj.setX(5); /*This causes a compile error, setX() is
not defined on the interface.*/

If you have "malicious programmers" on your team, they can get around
this by downcasting from ReadableData to Data, which is why I say this only
works for bug detecting and not security. If you need something stronger
than this, then you'll probably need to make proxy classes which can read
from the original data object, but never provides a mean to write to it.

- Oliver
 
O

Oliver Wong

www said:
For another better example to show my point:

public class Car
{
final private Engine engine = new Engine(); //even the final word here
may does not serve my purpose

public void drive()
{
...//code
}

public void stop()
{
...//code
}

public void turnLeft()
{
...//code
}

...

}

I do not want Engine being completely immutable. I want to be able to tune
or fix Engine sometimes by myself. But I do NOT want when Car is doing
something(driving, stopping, or ...), Car is tuning or modifying Engine.
That is dangerous!

With this new information, I have a different recommendation. I had thought
you wanted to pass off an object which *you* can mutate, but no one else
could. Now it sounds like you want an object which can mutate, but only in
certain states:

public class Car {
public boolean isSafeToTune() {
return !driving && !turning;
}

public boolean isSafeToTurn() {
return !driving && ! tuning;
}

/**
* PRECONDITIONS: none
* POSTCONDITIONS: driving
*/
public void drive() {
driving = true;
}

/**
* PRECONDITIONS: none
* POSTCONDITIONS: !driving
*/
public void stop() {
driving = false;
}

/**
* PRECONDITIONS: isSafeToTurn()
* POSTCONDITIONS: turning
*/
public void turnLeft() {
if (!isSafeToTurn()) {
throw new IllegalStateException();
}
turning = true;
}

/**
* PRECONDITIONS: isSafeToTune()
* POSTCONDITIONS: car is tuned.
*/
public void tune() {
if (!isSafeToTune()) {
throw new IllegalStateException();
}
/*tune car somehow*/
}
}

Users of your class will have to read the javadocs carefully and understand
when and why they are allowed to call certain methods, and not other
methods.

- Oliver
 
C

Chris Uppal

www said:
I want myDataObj state never been modified by users.

You can't do it with any combination of "final" flags (unless you want to make
your object completely immutable -- and even then you can't /quite/ manage it).

The closest you can come is to hand out an immutable proxy for the object
instead of the object itself. E.g.

public interface Data
{
int getWidth();
String getName();
int[] getMoreData();
}

public class RealData
implements Data
{
private int m_width;
private String m_name;
private int[] m_moreData;

public int getWidth() { return m_width; }
public String getName() { return m_name; }
public int[] getMoreData() { return m_moreData; }
public void setWidth(int width) { m_width = width; }
public void setName(String name) { m_name = name; }
public void setMoreData(int[] data) { m_moreData = data; }

public Data asImmutableData() { return new ImmutableData(); }

class ImmutableData
implements Data
{
public int getWidth() { return RealData.this.getWidth(); }
public String getName() { return RealData.this.getName(); }
public int[] getMoreData() { return
RealData.this.getMoreData().clone(); }
}
}

Note that there may be nothing to gain in making RealData public, and there may
be nothing to gain in making it implement Data and have public get/set methods.
But I've made it public here to illustrate how, even if it is public, a user of
the object returned by asImmutableData() cannot change it.

Note also the call to clone() in the immutable version of getMoreData() -- we
don't want to hand out a reference to the real array, since arrays (or
references to arrays) cannot be made immutable.


As something of an aside, I would be tempted to add asImmutableData() to the
Data interface, and make ImmutableData.asImmutableData() just
return this;
The idea is that something can pass on an immutable proxy for a possibly
mutable object to a third party by using asImmutableData() whether originally
had a reference to a RealData or an ImmutableData.


A simpler solution, and in many cases a better solution, is just not to worry
about it. If the other code changes your object then it must have been given
the relevant access permissions -- and if you've given it that then you
obviously trust it not to do anything stupid.

-- chris
 
E

Eric Sosman

www wrote On 02/08/07 11:21,:
For another better example to show my point:

public class Car
{
final private Engine engine = new Engine(); //even the final word here
may does not serve my purpose

public void drive()
{
...//code
}

public void stop()
{
...//code
}

public void turnLeft()
{
...//code
}

...

}

I do not want Engine being completely immutable. I want to be able to
tune or fix Engine sometimes by myself. But I do NOT want when Car is
doing something(driving, stopping, or ...), Car is tuning or modifying
Engine. That is dangerous!

public void tuneOrFix() {
if (engineIsRunning())
throw new IllegalStateException("pphhht!");
removeSparkPlugs();
...
}

You might also want to modify some of the other methods:

public void drive() {
if (maintenanceInProgress())
throw new IllegalStateException(
"not while it's on the lift!");
if (! engineIsRunning())
start();
...
}
 

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
474,433
Messages
2,571,683
Members
48,796
Latest member
Greg L.

Latest Threads

Top