9

在开发游戏项目时,我已经开始使用 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/Tuple(使用不可变的 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,但我对您的意见、论点和直觉很感兴趣 :)

4

6 回答 6

7

如你所说。团队可以在不同的情况下出现。这些情况是赋予团队额外属性的背景。

因此,我建议对添加数据的每个不同上下文使用组合。

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;
    }    
}
于 2012-08-04T17:24:45.020 回答
2

组合听起来像是添加需要可变的上下文数据的好选择。

在 Java 中,不可变类通常被标记final并且不能被扩展。以字符串为例。这排除了选项 2。

对使用 Pairs 感到厌烦。没有将 Pair 类型添加到 Java 中有很多很好的理由。在这种情况下,您的数据可以通过创建新的数据类型(即通过组合)更好地建模。

创建不可变类的推荐最佳实践:http ://www.javapractices.com/topic/TopicAction.do?Id=29

于 2012-08-04T17:23:51.297 回答
1

如果一个不可变的类是可继承的,或者如果包含的方面的类型可能是可变的,那么它就没有防止恶作剧的安全性。“不可变”接口同样没有安全性。同样,如果一个对象具有任何可扩展类型的成员,并且对象被认为包含这些成员所引用的对象中的任何信息。由您决定这是否是一个问题。不可变接口和可扩展的可变类可能比深度密封的接口更加通用;如果一个类不会被深度密封,那么将其部分地密封起来几乎没有安全优势。

请注意,如果字段的语义指定它标识而不是包含有问题的对象,则深度不可变对象可能具有可变类型的字段。例如,拥有一个“快照”对象可能很有用,它包含对一些可变对象的引用,并且对于每个对象,它的当前状态的不可变副本;然后,“快照”对象将公开一种将每个对象恢复到生成快照时的状态的方法。尽管快照对象引用了可变对象,但快照对象将是“深度不可变的”,因为快照对可变对象的引用纯粹是为了识别它们而存在,并且这些对象的身份不会

我在 .net 中喜欢的一种模式,它也应该在 Java 中工作,是具有类似IReadableFooand的接口IImmutableFoo;后者将继承前者但不添加任何新成员。IReadableFoo除了要读取其属性的成员之外,该接口还应包含一个AsImmutable()返回IImmutableFoo. 这种方法允许代码在方便时使用可变类型,同时最小化冗余的防御性复制(想要持久化某些东西的代码必须调用AsImmutable()它;如果有问题的对象是可变的,那么该操作将创建一个不可变的副本,但如果对象已经不可变的,不需要新的副本)。

于 2012-08-06T17:17:08.790 回答
0

您可以考虑将这条信息保留在 Team 对象之外。例如,你可以有一个 Map 或 Map

这样,您可以使用许多附加值来丰富您的对象,而无需引入新类或修改现有类。此外,它将保留其他对象的不可变属性。

于 2012-08-05T08:04:04.670 回答
0

我推荐装饰者模式。

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;
    }
}

http://en.wikipedia.org/wiki/Decorator_pattern

于 2012-08-06T17:46:51.220 回答
0

我通常会尽量避免将所有东西都放在一个类中(选项 1),这给我们留下了 4 个选项

  • 如果我需要类是线程安全的,我喜欢使用不可变元组的 volatile/AtomicReference(选项 4)而不是使用锁和其他阻塞同步(我们称之为选项 5)
  • 如果线程安全不是问题,那么我会选择组合(选项 3)而不是继承(选项 2),除非 IS-A 关系自然存在

有色团队毕竟是一个团队,所以我很乐意利用这种关系。如果我是你,我会这样编码:

public final class ColoredTeam extends AbstractTeam implements Team {
    // the rest comes along intuitively
}

因为“AbstractTeam”是抽象的,所以基于内容的 equals() 实现非常好,只要“ColoredTeam”本身没有子类,我将 ColoredTeam 设为 final 来实现这一点,但我可以将承包商设为私有,引发异常等。

于 2019-01-25T18:23:41.857 回答