我在开始游戏项目时开始使用Java中的不可变值对象,遵循“公共最终字段”方法:
public class Team {
public final String name, flag;
public Team(String name, String flag) {
this.name = name;
this.flag = flag;
}
}
到目前为止,这对我来说非常有效,但我需要在不同情况下提供有关团队的各种额外信息。例如,团队在比赛期间具有设定的颜色。问题是,处理这些扩展信息集的最佳方法是什么?我知道这是一个相当普遍的问题,但我想继续使用不可变对象,这可能会影响解决方案。
以下是我提出的选项。他们中的大多数可能“足够好”,但我想学习一些支持和反对它们的论据以供将来参考。
选项1:一个类中的所有内容
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;
}
}
对于所有用途,这只需要一个类,但没有基于类型的指示,表明预期/提供了多少额外数据。
选项2:子类化
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;
}
}
这可能会使基于内容的equals()无法实现。
选项3:合成
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;
}
}
如果团队数据和额外数据经常独立变化,那么复制/样板代码就会减少。
选项4:配对/元组(使用不可变的Pair类)
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);
...或者将Team和TeamColor联系在一起的自定义类。
我倾向于选择3或4,但我对你的意见,论点和直觉感兴趣:)
答案 0 :(得分:6)
正如你所说。团队可以出现在不同的情况下。 这些情况是给团队带来额外属性的背景。
因此,我建议在添加数据的每个不同上下文中使用合成。
public class TeamWithColor {
public final Team team;
public final TeamColor teamColor;
public Team(Team team, TeamColor teamColor) {
this.team = team;
this.teamColor = teamColor;
}
}
也许你会:
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;
}
}
答案 1 :(得分:2)
组合听起来像是添加需要变化的上下文数据的好选择。
在Java中,不可变类通常标记为final
,不能扩展。请参阅String作为示例。这排除了第2号选项。
厌倦了使用Pairs。没有将Pair类型添加到Java中有很多好的理由。在这种情况下,您可以通过创建新数据类型(即通过合成)更好地建模数据。
创建不可变类的推荐最佳做法:http://www.javapractices.com/topic/TopicAction.do?Id=29
答案 2 :(得分:1)
如果一个不可变类是可继承的,或者如果包含其类型可能是可变的方面,那么它将没有防止恶作剧的安全性。 “不可变”接口同样没有安全性。同样,如果一个对象具有任何可扩展类型的成员,并且该对象被视为包含这些成员引用的对象中的任何信息。您可以自行决定是否存在问题。不可变接口和可扩展可变类可能比深度密封类更通用;如果一个班级不会被深深地封闭,那么部分地制作它就没有什么安全优势。
请注意,如果字段的语义指定标识,而不是包含<,则深度不可变对象可能具有可变类型的字段。 / i>,有问题的对象。例如,拥有一个“快照”对象可能是有用的,该对象包含对某些可变对象的引用,并且对于每个对象,包含对其当前状态的不可变副本;然后,“snapshot”对象将公开一种方法,将每个对象恢复到生成快照时的状态。尽管快照对象引用了一个可变对象,但它仍然是“深度不可变的”,因为快照对可变对象的引用纯粹是为了识别它们,并且即使这些对象的标识也不会改变他们的国家呢。
我喜欢.net中的一个模式,也应该在Java中工作,是具有IReadableFoo
和IImmutableFoo
等接口;后者将继承前者但不添加任何新成员。除了读取其属性的成员之外,接口IReadableFoo
还应包含将返回AsImmutable()
的成员IImmutableFoo
。这种方法允许代码在方便的时候使用可变类型,同时最大限度地减少冗余防御性复制(想要持久化的代码必须在其上调用AsImmutable()
;如果有问题的对象是可变的,那么该操作将创建一个不可变的副本,但是如果对象已经是不可变的,则不需要新的副本。)
答案 3 :(得分:0)
您可以考虑将此信息保留在Team对象之外。例如,您可以拥有地图或地图
这样,您可以使用许多其他值来丰富对象,而无需引入新类或修改现有类。而且,它将保留其他对象的不可变属性。
答案 4 :(得分:0)
我推荐Decorator模式。
public class TeamWithColor extends Team {
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;
}
}
答案 5 :(得分:0)
我通常会尽量避免将所有内容放在一个类中(选项1),这样我们就有4个选项
一个有色团队毕竟是一个团队,所以我很乐于利用这种关系。如果我是你,我会这样编码:
public final class ColoredTeam extends AbstractTeam implements Team {
// the rest comes along intuitively
}
因为“ AbstractTeam”是抽象的,所以只要“ ColoredTeam”本身没有子类,基于内容的equals()实现就可以了,所以我将ColoredTeam最终实现了这一点,但是我可以将承包商私有化,引发异常,等