Which one do you prefer when you need to modify a map/list or object, which is a better approach?, I mean in the perspective of;
Let's say that I have a map that returned from the method;
public Map<Integer, User> getUsers() {
Map<Integer, User> names = getNames();
return names;
}
So in the same function, I need to add prefixes to them;
public Map<Integer, User> getUsers() {
Map<Integer, User> names = getNames();
addPrefixes(names);
return names;
}
In the immutability perspective, which one is better? Should I use this way;
public Map<Integer, User> getUsers() {
Map<Integer, User> names = getNames();
Map<Integer, User> prefixedNames = getPrefixed(names);
return prefixedNames;
}
If so, how to implement getPrefixed function, deep copy every User and add them to the new Map and return it, can you give an example?
Let's take a look at the code:
public Map<Integer, User> getUsers() {
Map<Integer, User> names = getNames();
// addPrefixes or names=getPrefixes
return names;
}
getNames
is a method from the same class ad getUsers
. If getNames
is public, it should not expose an internal field of the class. I mean, if you have something like:
public Map<Integer, User>() getNames() { return this.names; }
It's time to make a copy, since the state of your object may be corrupted by any user. But even if getNames
is a private and returns an internal field, what happens if someone calls getUsers
(once, twice, ...)? The prefixes will be added to the internal field.
Hence, I assume that getNames
returns a copy of the names (e.g. retrieved from a database). The local variable names
is yours and is a work copy of the names.
Now, you have three options:
void addPrefixes(Map<Integer, User> names) { // 1
// update names
}
Map<Integer, User> getPrefixed(Map<Integer, User> names) { // 2
// update names
return names;
}
Map<Integer, User> getPrefixed(Map<Integer, User> names) { // 3
// create copy, e.g. use Java8 stream;
return namesCopy;
}
The options 1 and 2 are basically the same (it's a matter of signature) whereas option 3 is a functional version that makes copy.
I think the option 2 and 3 are easier to read: give an input and get an output. The option 1 is harder to grasp: addPrefixes
to what? To the names
or to the object itself? Are there other side effects (now or later, in an improved version)?
Plus, if the process of adding a prefix gets more complex, you can easily replace:
return getPrefixed(names);
By:
return prefixer.getPrefixed(names);
You can start with option 3 (safer) and switch to option 2 (with comments) if the function is a bottleneck of your application. Option 1 and 2 have the same cost.
If you need to test if the prefixes are correctly added, make getPrefixed
a public method of a small Prefixer
class (Single Responsibility Principle).
The option 3 is the best. What happens if getNames
is modified to return an internal field. Example:
// before
public Map<Integer, User>() getNames() {
// query the db
// create the map
return names;
}
// after: the method was to slow, cache the result
public Map<Integer, User>() getNames() {
if (this.names == null) {
// query the db
// create the map
}
return this.names;
}
Option 1 and 2 will corrupt the field names
.
Important: whatever you decide, document your choice: when a parameter is modified, you should let the user know because the initial expectation (in Java) is that parameters are not modified. If a function returns a copy, let the user know.