Reputation: 337
The problem is I can't modify an object I'm writing from inside itself and thus made a clone that I modify and return. However, in other functions I directly modify the object calling the method. I would like, and people have recommended, to keep things consistent and so the user can do anything they like with the returned clone without modifying the actual object.
I HAVE to return a modified clone of the object in certain functions because there's no way around it (that I'm aware of). Along with the question in the title, is it better to have a consistent way of doing things (causing me to return clones for even the slightest change) or is it okay if I have different ways to respond to the user in the same class?
[Edit] Is it better to do this:
public Image fillWithColor(Color fillColor) {
Image newImage = new Image(this.getWidth(), this.getHeight(), this.getType());
for (int x = 0; x < this.getWidth(); x++) {
for (int y = 0; y < this.getHeight(); y++) {
newImage.setPixel(x, y, fillColor.getRGB());
}
}
return newImage;
}
or this:
public void fillWithColor(Color fillColor) {
for (int x = 0; x < this.getWidth(); x++) {
for (int y = 0; y < this.getHeight(); y++) {
this.setPixel(x, y, fillColor.getRGB());
}
}
}
Upvotes: 4
Views: 1450
Reputation: 2937
The only correct answer is:
"It depends".
This is what engineering is all about. Making the right trade offs. Suppose you are an engineer working for the city and tasked with designing new garbage bins for the city center. You have some decisions to make. You want to make them big, so they can contain lots of garbage and don't overflow on busy days. But you also want to make them small so they don't take up a lot of space on the sidewalk. You want to make them light so they can be handled easily when emptying them, and heavy so they aren't blown over by the wind or kicked over by hooligans. So it's not a question of big or small and heavy or light, but how big and how heavy.
In software engineering there are also many mutually exclusive qualities for which you have to make the right choice in your project. Some examples:
Immutable vs mutable
The advantage of immutable types is that they are thread-safe. Thread A and B can both have a reference to the same object and still be sure its value will not change unexpectedly without using locks. If thread A wants to change the value then it can do so by changing the reference it holds to a new object; thread B is still happily holding onto the original object.
Having the value of an object change unexpectedly is not only a problem in concurrent programming, but can also occur when the users of your class are not expecting it. That is why there is the concept of defensive copying.
The stock Date
class in java is mutable. So consider a Person
class with a getBirthDate()
getter and setBirthDate()
setter. As a user of the Person
class you would expect to only be able to change the birth date of the person by using the setter, but if your getter does not return a defensive copy then the user of the class can also change it unexpectedly by changing the Date
object it received from getBirthDate()
.
So immutable types make programs thread-safe(r) and less error prone and are therefore generally a good idea but you can not always use them. Your fillWithColor()
function is an example where it isn't really feasible.
A Canvas
class is a mutable object. You would have a fillWithColor()
function, but also drawLine()
, drawElipse()
, drawText()
, and many more. Building up a drawing with these functions can take many calls. Consider drawing a 'no parking' traffic sign:
If your Canvas
class is immutable you would need five times the amount of memory and process five times the number of pixels. And this is really a trivial example. Consider this SVG image of a cheetah. Every spot on its back is a call to a draw function.
It depends
I would say that you should use immutable types where you can and use immutable where you can not. Generally this divide falls along small vs big data types.
If your type references a data structure that is small enough to be a value type then it should probably be an immutable reference type. Like the java Date
should have been immutable, after all it's only 8 bytes.
If your type references something big and you need to allow many operations on it, then you will have to be pragmatic and make it a mutable type. Like your Canvas
example, after all images can be megabytes big.
Mutable and immutable are both needed.
Upvotes: 1
Reputation: 36349
A mega-trend is to treat as much data as possible as read-only, for various important reasons. Even databases do this today.
You obviously have recognized that uncontrolled modification of data can get you into trouble. Very good. Try to design your data as immutable objects, and many things will be much easier in the long run. Just note that certain data structures in Java are inherently mutable (arrays, hash-tables, and so on), and are meant and expected to get mutated.
In the exmple above, I'd choose the first variant. Why? It might cost a few microseconds and some RAM to copy the image, instead to update in-place. But you can keep the old image around, and depending on your application, this may be beneficial. Also, you could color the same image with 10 different fill colors in 10 different threads in parallel, and there would be no locking problems.
That being said, it is still not possible to answer your question like "It is always better ...". It depends on your problem, on your environment, programming language, libraries you are using and many factors more.
So let's say, immutable data are preferrable most of the time, unless there are serious reasons against it. (Saving a few micro-seconds on execution time is usually not a serious reason.)
To put it differently, there should be good reasons to make a data type mutable, while immutability should be the default. Unfortunately, Java is not the language that supports this approach, to the contrary, everything is mutable by default, and it takes some effort to make it different.
Upvotes: 5