Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (538)
Games in Android Showcase (132)
games submitted by our members
Games in WIP (601)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  immutable, cloned, or mix?  (Read 5031 times)
0 Members and 1 Guest are viewing this topic.
Offline 2playgames

Junior Devvie





« Posted 2007-07-08 21:27:36 »

I have a class Vector2 (which contains a double "x", a double "y" and some methods), which is widely used by my game. At the moment some methods that use it accept/return a clone, and some a reference. This is confusing and inconsistent, so I want to change it. Now, however I don't know which system to choose:

- Make the class immutable and always pass a reference
++ Good for memory
-- Many new allocations if a vector needs to be changed every frame (because it can't be modified)

OR

- Keep the class mutable, but always make a clone when accepting/returning an instance of it
++ It can be modified directly (for example, the position of an object which changes every frame
-- Every copy takes memory, and clones need to be made every time it's passed to another method

So, what do you think is the fastest/best way?

Offline Jackal von ÖRF

Junior Devvie





« Reply #1 - Posted 2007-07-08 21:44:57 »

Try both, profile the code and compare the results? I doubt there will be any big difference in performance. I would choose the one which produces cleaner code.

AFAIK, the JVM can optimize unnecessary defensive copies away, so using return new Vector2(v); might not allocate a new object if the returned object is not modified by the caller. Could somebody verify this assumption?

Offline Abuse

JGO Knight


Medals: 14


falling into the abyss of reality


« Reply #2 - Posted 2007-07-08 21:48:23 »

Not sure what you mean by 'and some a reference', everything is passed by value in java - even references...

Do you mean that some of your methods mutate their parameters, and others return a new object, leaving the paramters unchanged?

A common approach to this is to add an additional 'target' (output) parameter into which the results of the operation will be stored.
This approach eliminates both the object creation overhead, and the mutating of method input parameters.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline 2playgames

Junior Devvie





« Reply #3 - Posted 2007-07-08 21:56:24 »

Quote
Not sure what you mean by 'and some a reference', everything is passed by value in java - even references...
You pass a reference to a Vector2 instance. The reference is the value
Quote
Do you mean that some of your methods mutate their parameters, and others return a new object, leaving the paramters unchanged?
No, I have for example a method like this:
1  
2  
3  
public void setPosition(Vector2 v) {
  this.pos = v.clone();
}

but also
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
public Vector2 getPosition() {
  return this.pos;
}

public void add(double x, double y) {
  this.pos.x += x;
  this.pos.y += y;
}

public void move() {
  someobject.getPosition.add(23,34);
}

I'm sure you will agree this isn't very consistent

Quote
I would choose the one which produces cleaner code.
Then I would choose the first, since a programmer creating methods involving my class will not need to think about making a clone

Offline Abuse

JGO Knight


Medals: 14


falling into the abyss of reality


« Reply #4 - Posted 2007-07-08 22:23:02 »

In your example I'd say the following is where your problem stems:-

1  
2  
3  
public void setPosition(Vector2 v) {
  this.pos = v.clone();
}


If getPosition() is going to return a reference to the encapsulatede member, then setPosition(...) must not re-assign the object to which said member refers.
The above should be changed to something like:-

1  
2  
3  
public void setPosition(Vector2 v) {
  this.pos.set(v);
}

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline 2playgames

Junior Devvie





« Reply #5 - Posted 2007-07-08 22:30:26 »

there are more problems in my current approach. for example:
- getposition returns a reference so it can be modified directly (e.g. to move an object)
- however, an unaware programmer might store the return value somewhere, then later modify it (not knowing that this is the original instance) and get inexplainable bugs

i wish there were C# structs in Java...Roll Eyes

Offline Abuse

JGO Knight


Medals: 14


falling into the abyss of reality


« Reply #6 - Posted 2007-07-09 01:17:13 »

- however, an unaware programmer might store the return value somewhere, then later modify it (not knowing that this is the original instance) and get inexplainable bugs

You can't write library code that prevents errors in user code.
The above scenario could occur in *any* application, written against *any* api.

The important thing is that your API must have a clearly and concisely defined specification, and it must meet said specification precisely.

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline keldon85

Senior Devvie


Medals: 1



« Reply #7 - Posted 2007-07-09 07:40:38 »

It depends on the usage of the type. I personally make vectors mutable with a copy constructor.

Offline Don Kiddick

Junior Devvie





« Reply #8 - Posted 2007-07-09 09:02:44 »

The most 'correct' way would be to make Vector2 immutable because it has value-type semantics - like String, Integer etc... As opposed to reference-type semantics like Player, Level etc.

This will impose some performance overhead, but then we do ok with String, Integers being immutable.

This way your code would look something like this :

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
public void setPosition(Vector2 v) {
  this.pos = v; //Vector2 is immutable so no need for defensive copying
}

public Vector2 getPosition() {
  return this.pos; //Vector2 is immutable so no need for defensive copying
}

public void add(double x, double y) {
  this.pos = this.pos.add(x, y); //Vector2.add() creates a copy
}


thanks, D.
Offline keldon85

Senior Devvie


Medals: 1



« Reply #9 - Posted 2007-07-09 09:12:40 »

The most 'correct' way would be to make Vector2 immutable because it has value-type semantics - like String, Integer etc... As opposed to reference-type semantics like Player, Level etc.

This will impose some performance overhead, but then we do ok with String, Integers being immutable.

But you're not making 1,000's of string calculations per frame and for larger operations of concatenating strings you would instead use a string buffer! And what benefit is there to making a vector immutable? It is a type of object that is referred back to, so it's usage does not encourage immutability and instead encourages mutability.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline princec

« JGO Spiffy Duke »


Medals: 429
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #10 - Posted 2007-07-09 09:36:27 »

I "solved" this problem by having Vector2f (etc) implement two interfaces, ReadableVector2f and WriteableVector2f. When I don't want people to think they can edit the vector directly I return a ReadableVector or pass a ReadableVector in and copy it into an existing Vector2f. I use this consistently throughout nearly all of my code.

The technique is not foolproof (a cast will thwart it) but it avoids cloning and avoids garbage, and so it's good and fast.

Cas Smiley

Offline keldon85

Senior Devvie


Medals: 1



« Reply #11 - Posted 2007-07-09 09:50:05 »

Interesting approach!

Offline bahuman

Junior Devvie





« Reply #12 - Posted 2007-07-09 12:08:58 »

From what little I know of the Sun Swing API, Sun circumvented the problem by not getting/setting objects. The "Coordinates" class has been deprecated.
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #13 - Posted 2007-07-09 12:33:53 »

This will impose some performance overhead, but then we do ok with String, Integers being immutable.
Strings get special handling by the VM though (both from object pooling and operator overloads). Without them the overhead of your custom vector class is going to be larger, and the syntax to manipulate them is considerably more unweildy.

I use the same method as Cas, it might not be foolproof (especially if you hang on to an ImmutableVector2f for longer than expected) but it does a very good job of signally the intentional use. Bung in a simple copy c'tor to create a mutable version from an immutable vector and it's quite clean syntactically too.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline Abuse

JGO Knight


Medals: 14


falling into the abyss of reality


« Reply #14 - Posted 2007-07-09 14:43:29 »

Does hotspot optimise away the invokeinterface instructions?
to the cheaper a) invokevirtual,
and cheapest b) invokespecial ?

If not, I can imagine such an approach would have a significant runtime overhead,

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline krausest

Junior Devvie


Exp: 15 years


I love YaBB 1G - SP1!


« Reply #15 - Posted 2007-07-09 16:13:03 »

I "solved" this problem by having Vector2f (etc) implement two interfaces, ReadableVector2f and WriteableVector2f. When I don't want people to think they can edit the vector directly I return a ReadableVector or pass a ReadableVector in and copy it into an existing Vector2f. I use this consistently throughout nearly all of my code.

The technique is not foolproof (a cast will thwart it) but it avoids cloning and avoids garbage, and so it's good and fast.

Cas Smiley
I used that too, but in comparison to immutable types I think it's still too mutable. It works fine for types you create and return (which shouldn't be changed by clients), but it doesn't free you completely from making defensive copies and it doesn't make those objects threadsafe.
Offline 2playgames

Junior Devvie





« Reply #16 - Posted 2007-07-09 16:57:29 »

I'm using the immutable way now, and it seems to work fine Smiley

Offline mabraham

Junior Devvie





« Reply #17 - Posted 2007-07-09 20:49:35 »

I used that too, but in comparison to immutable types I think it's still too mutable. It works fine for types you create and return (which shouldn't be changed by clients), but it doesn't free you completely from making defensive copies and it doesn't make those objects threadsafe.
Correct.  If you receive an object the interface semantics of which indicate immutability then that doesn't say anything about what could happen in reality.  Which is why immutable types must be final.

There's certainly a case for either approach, but immutable objects are usually easier to live with and less problematic, whereas mutable objects are usually somewhat faster (and maybe a bit more convenient in some situations of limited scope).

A side note on value types in C#, they're probably kinda nice with regards to performance, but they're one reason why C# is quite a bit more complex to use than Java.
Offline Don Kiddick

Junior Devvie





« Reply #18 - Posted 2007-07-10 20:25:31 »

But you're not making 1,000's of string calculations per frame and for larger operations of concatenating strings you would instead use a string buffer! And what benefit is there to making a vector immutable? It is a type of object that is referred back to, so it's usage does not encourage immutability and instead encourages mutability.

The main benefit is robustness, particularly in a multithreaded environment.

Ultimately it's an OO *guideline* that classes with reference semantics should be immutable. There will be a performance penalty (particularly if there are more reads than writes, although if many more writes than reads, immutability can be faster because of less need of synchronization) and games are a perfomance sensitive domain, so it may be a good decision to make them mutable.  Your choice.

thanks, D.
Offline keldon85

Senior Devvie


Medals: 1



« Reply #19 - Posted 2007-07-10 21:13:03 »

The main benefit is robustness, particularly in a multithreaded environment.

Ultimately it's an OO *guideline* that classes with reference semantics should be immutable. There will be a performance penalty (particularly if there are more reads than writes, although if many more writes than reads, immutability can be faster because of less need of synchronization) and games are a perfomance sensitive domain, so it may be a good decision to make them mutable.  Your choice.

thanks, D.
Well that's not entirely true since there are many cases where synchronization is not needed. One thing that many people tend to do is over synchronize ensure thread safety for things that will not be used in such a way where they will be of any concern. Bad design can make you enforce strategies in the wrong time, like overusing design patterns!

Offline zero

Junior Devvie





« Reply #20 - Posted 2007-07-11 09:20:58 »

I agree that clean is the most desirable here, since I doubt performance will be that different. Personally, I prefer immutables because of that and threading issues...
Offline keldon85

Senior Devvie


Medals: 1



« Reply #21 - Posted 2007-07-11 09:49:10 »

Just be careful not to give unclear advice, or misleading ones. Mutable structures have their uses and has served the professional gaming industry fine for years. Read "to mutate or not to mutate" by IBM. It is a programming approach, so whether it turns out to be the be all end all solution to data storage you must know why you are not using a mutable data structure.

Having said that I am involved in GBA programming so you tend to have to learn to work without many OO design practices in favour of something that can actually run in real time. I do design and prototype for readability and simplicity and only change the implementation when need be, but see patterns as tools and not parts of your body.

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

rwatson462 (29 views)
2014-12-15 09:26:44

Mr.CodeIt (20 views)
2014-12-14 19:50:38

BurntPizza (42 views)
2014-12-09 22:41:13

BurntPizza (76 views)
2014-12-08 04:46:31

JscottyBieshaar (37 views)
2014-12-05 12:39:02

SHC (50 views)
2014-12-03 16:27:13

CopyableCougar4 (47 views)
2014-11-29 21:32:03

toopeicgaming1999 (114 views)
2014-11-26 15:22:04

toopeicgaming1999 (102 views)
2014-11-26 15:20:36

toopeicgaming1999 (30 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!