I have started playing around with immutable value objects in Java while working on a game project, following the "public final fields" approach:
public class Team {
public final String name, flag;
public Team(String name, String flag) {
this.name = name;
this.flag = flag;
}
}
This works pretty well for me so far, but I need different sets of extra information about the team in different circumstances. For example, a team has a set color during a match. The question is, what is the best way to deal with these sets of extended information? I know this is a fairly general question, but I want to keep using immutable objects and that might influence the solution.
Here are the options I have come up with. Most of them are probably "good enough", but I'd like to learn some arguments for and against them for future reference.
Option 1: Everything in one class
public class Team {
public final String name, flag, colorName;
public final int colorRgb;
public Team(String name, String flag, String colorName, int colorRgb) {
this.name = name;
this.flag = flag;
this.colorName = colorName;
this.colorRgb = colorRgb;
}
}
This takes only one class for all uses, but there is no type-based indication of what extra data is expected/provided.
Option 2: Subclassing
public class TeamWithColor extends Team {
public final String colorName;
public final int colorRgb;
public Team(String name, String flag, String colorName, int colorRgb) {
super(name, flag);
this.colorName = colorName;
this.colorRgb = colorRgb;
}
}
This might make a content-based equals() implementation impossible.
Option 3: Composition
public class TeamWithColor {
public final Team team;
public final String colorName;
public final int colorRgb;
public Team(Team team, String colorName, int colorRgb) {
this.team = team;
this.colorName = colorName;
this.colorRgb = colorRgb;
}
}
Less copying / boilerplate code if the team data and extra data often change independently.
Option 4: Pair/Tuple (using an immutable Pair class)
public class TeamColor {
public final String colorName;
public final int colorRgb;
public Team(String colorName, int colorRgb) {
this.colorName = colorName;
this.colorRgb = colorRgb;
}
}
Pair<Team, TeamColor> teamWithColor = Pair.create(team, teamColor);
... or with a custom class that ties Team and TeamColor together.
I tend toward option 3 or 4, but I'm interested in your opinions, arguments and gut feelings :)
In Immutable Objects in Java, the four authors (Haack, Poll, Schafer, and Schubert) include a section on "enforcing immutability." In this section they maintain that classes which extend immutable classes must also themselves be immutable.
Immutable class/object is the one whose value cannot be modified. For example, Strings are immutable in Java i.e. once you create a String value in Java you cannot modify it.
If you want to enforce immutability, you cannot have subclasses. See for example java. lang. String, which is a final class for this reason: To prevent people from subclassing String to make it mutable.
As you said. The team can appear in different circumstances. These circumstances are the context giving the team the additional attributes.
Therefore I suggest using composition for each different context that add's data.
public class TeamWithColor {
public final Team team;
public final TeamColor teamColor;
public Team(Team team, TeamColor teamColor) {
this.team = team;
this.teamColor = teamColor;
}
}
Maybe you'll have :
public class TeamDuringOlimpics{
public final Team team;
public final TeamColor teamColor;
public final TeamFlag teamFlag;
public Team(Team team, TeamColor teamColor, TeamFlag teamFlagTeamFlag teamFlag) {
this.team = team;
this.teamColor = teamColor;
this.teamFlag = teamFlag;
}
}
Composition sounds like a good option for adding contextual data that is required to be mutable.
In Java immutable classes are usually marked final
and cannot be extended. See String as an example. That rules out option number 2.
Be weary of using Pairs. There are many good reasons the Pair type has not been added to Java. In this case your data is better modeled by creating a new data type (i.e. thru composition).
Recommended best practices for creating immutable classes: http://www.javapractices.com/topic/TopicAction.do?Id=29
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With