Writing a method to 'transform' an immutable object: how should I approach this?
- by Prog
(While this question has to do with a concrete coding dilemma, it's mostly about what's the best way to design a function.)
I'm writing a method that should take two Color objects, and gradually transform the first Color into the second one, creating an animation. The method will be in a utility class.
My problem is that Color is an immutable object. That means that I can't do color.setRGB or color.setBlue inside a loop in the method.
What I can do, is instantiate a new Color and return it from the method. But then I won't be able to gradually change the color.
So I thought of three possible solutions:
1- The client code includes the method call inside a loop. For example:
int duration = 1500; // duration of the animation in milliseconds
int steps = 20; // how many 'cycles' the animation will take
for(int i=0; i<steps; i++) color = transformColor(color, targetColor, duration, steps);
And the method would look like this:
Color transformColor(Color original, Color target, int duration, int steps){
int redDiff = target.getRed() - original.getRed();
int redAddition = redDiff / steps;
int newRed = original.getRed() + redAddition;
// same for green and blue ..
Thread.sleep(duration / STEPS); // exception handling omitted
return new Color(newRed, newGreen, newBlue);
}
The disadvantage of this approach is that the client code has to "do part of the method's job" and include a for loop. The method doesn't do it's work entirely on it's own, which I don't like.
2- Make a mutable Color subclass with methods such as setRed, and pass objects of this class into transformColor. Then it could look something like this:
void transformColor(MutableColor original, Color target, int duration){
final int STEPS = 20;
int redDiff = target.getRed() - original.getRed();
int redAddition = redDiff / steps;
int newRed = original.getRed() + redAddition;
// same for green and blue ..
for(int i=0; i<STEPS; i++){
original.setRed(original.getRed() + redAddition);
// same for green and blue ..
Thread.sleep(duration / STEPS); // exception handling omitted
}
}
Then the calling code would usually look something like this:
// The method will usually transform colors of JComponents
JComponent someComponent = ... ;
// setting the Color in JComponent to be a MutableColor
Color mutableColor = new MutableColor(someComponent.getForeground());
someComponent.setForeground(mutableColor);
// later, transforming the Color in the JComponent
transformColor((MutableColor)someComponent.getForeground(), new Color(200,100,150), 2000);
The disadvantage is - the need to create a new class MutableColor, and also the need to do casting.
3- Pass into the method the actual mutable object that holds the color. Then the method could do object.setColor or similar every iteration of the loop.
Two disadvantages:
A- Not so elegant. Passing in the object that holds the color just to transform the color feels unnatural.
B- While most of the time this method will be used to transform colors inside JComponent objects, other kinds of objects may have colors too. So the method would need to be overloaded to receive other types, or receive Objects and have instanceof checks inside.. Not optimal.
Right now I think I like solution #2 the most, than solution #1 and solution #3 the least. However I'd like to hear your opinions and suggestions regarding this.