【问题标题】:Java: Enforcing doubly linked objectsJava:强制双重链接对象
【发布时间】:2011-11-20 20:49:04
【问题描述】:

我正在用 Java 设计一个游戏引擎。

该引擎的核心存在两个类 Asset 和 Attribute,其中 Asset 具有属性列表。大多数属性不需要链接到它们的属性,这意味着属性可以并且经常出现在多个资产的列表中。但是,有一个属性扩展名为 UniqueAttribute,它是针对特定于其资产的那些实现,并利用返回链接。

  1. 理想情况下,如果我删掉其他代码,我的 Asset 的 addAttribute 方法会如下所示:

    public void addAttribute(Attribute attribute){
      if(attribute instanceof UniqueAttribute)
        ((UniqueAttribute)attribute).setAsset(this);
      attributeList.add(attribute);
    }
    

    不幸的是,由于它们存在于不同的包中,UniqueAttribute.setAsset() 必须是公共的。这使得该方法对引擎的外部用户开放,可以搞乱,虽然我可以直接说直接使用这种方法是一个错误,但我可以直接放弃它 - 这似乎相当草率。

  2. 第二个选项是在构造时为 UniqueAttribute 提供资产,这意味着创建时的代码将如下所示:

    asset.addAttribute(new UniqueAttribute(asset));
    

    虽然我可以添加一个 check-and-throwable 或 assert 来确认传入了正确的资产,但我基本上是依靠用户来连接这两者,我也不希望这样做。

  3. 第三种选择是硬着头皮把 50 个 java 文件放在同一个包中,这样我就可以使用标准的可见性了。

是否有某种模式或某种东西可以帮助将这两者连接在一起而不会暴露电线,或者强迫我将所有东西都放在一个大包中?

无关紧要的咆哮:我一直不喜欢 java 中子包的概念没有真正以任何有意义的方式扩展。就 java 而言,子包只是一个不同的包,在很多情况下我可以使用更多与此直接相关的可见性修饰符。

【问题讨论】:

  • 也许选项 2 带有某种了解独特属性和资产之间关系的工厂?
  • 虽然我认为对子包有特殊规则可能会奏效,但几乎没有必要(因为您可以使用 Spring 或 OSGi 等系统来强制接口和实现之间的分离)并且会事情要复杂得多,收益却很少。一个简单、可靠的实现比一个“可爱”但复杂的功能更重要。

标签: java reference hyperlink unique


【解决方案1】:

我的建议是 Asset、Attribute 和 UniqueAttribute 都应该在同一个包中(可能与其他一些核心“引擎”类一起)。然后,您可以对 UniqueAttribute.setAsset 使用标准包可见性。

您不需要将所有其他类放在同一个包中 - 您的 Asset.addAttribute 方法应该是公共的并且可以从其他包中访问,以便您的应用程序的其余部分可以直接使用它。

所以解决方案可以在您的分类中称为“3-”。

作为一些更一般的观点,还可以考虑:

  • 您是否真的需要 Attributes 和 UniqueAttributes 的复杂性 - 我不确定您是否真的需要,之前已经实现了一个相当复杂的游戏对象模型,而不需要任何看起来像 UniqueAttribute 的东西。如果 UniqueAttribute “需要返回链接”,那么它可能是试图太聪明/做得太多?
  • 即使您确实需要两者,您是否真的要编写代码以相同的方式/将它们视为同一对象层次结构的一部分?它们在概念上似乎完全不同,如果将两者混为一谈,您最终会编写大量条件代码.....
  • 属性一致共享和不可变还有许多其他优点——它在内存使用、并发性和可测试性等方面更好。由于它们可能非常小,因此在您需要它的情况下,写时复制语义的成本是微不足道的。

【讨论】:

  • 之前我使用的是上面的方法一,所以 UniqueAttribute 不是新的——我只是在看一些核心改进。你的第二点:我觉得如果我将两者分开,我会有太多的条件代码,因为有很多点会针对特定的一个或类型迭代属性列表。但它很有见地 - 我会考虑更多。
  • 啊,我明白你为什么现在想要反向链接了!如果迭代具有特定属性的所有对象是问题,那么您可能需要考虑为所有具有重要属性的对象保留一个单独的索引(可能是 HashSet?)。您可以在添加/删除对象或更改属性时更新此集合。尽管您必须保持此索引同步,但这是高效的,非常容易测试,并且避免了将复杂逻辑放入属性本身的需要。
【解决方案2】:

嗯,基本上你想做三件事:

  1. 使 setAsset 方法在包含 Asset 类的包中可见
  2. 对所有其他包隐藏 setAsset 方法
  3. 不要使用子包来实现这一点

这有点问题:如果您在 Attribute 类中将所有其他类包括该包(我们称之为 AttributePackage)中的该方法声明为公共,您不能阻止用户在某处包含该包。

另一方面,您可以执行以下操作:

  1. 创建一个只包含用户应该使用的Attribute方法的接口,我们称之为AttributeInterface
  2. 使属性实现该接口
  3. 将 AttributeInterface 添加到新包中

想要使用 Attribute 类的用户应该同时通过 AttributeInterface 使用它 Asset 将直接使用 Attribute 类来访问所有方法。

我举个例子:

//attribute interface package
public interface AttributeInterface{
     public void publicAttributeMethodClientShouldUse();
}

//attribute package
public class Attribute{
     public void setAsset(Asset a);
     public void publicAttributeMethodClientShouldUse();
}

Asset 将直接引用 Attribute 同时用户应该引用 AttributeInterface。希望清楚。

【讨论】:

  • 我不确定在这种情况下用户将如何创建带有指向其资产的链接的属性?实现接口意味着它只是一个标准属性,无法访问 getAsset(),扩展类与我在问题中提出的问题相同。
【解决方案3】:

我会在 Attribute 中添加一个回调方法,当 Attribute 实例添加到 Asset 时会调用该方法:

class Attribute {
    protected void addedToAsset(Asset asset) {
        // do nothing
    }
}

这个方法会在addAttribute方法中调用

class Asset {
    public void addAttribute(Attribute attribute) {
       attributeList.add(attribute);
       attribute.addedToAsset(this);
    }

}

并且该方法将在 UniqueAttribute 中被覆盖以控制与 Asset 的链接:

class UniqueAttribute extends Attribute {
    Asset asset;
    protected void addedToAsset(Asset asset) {
        // manage the previous link if needed
        if (this.asset != null) { ... }
        this.asset = asset;
    }
} 

有了这个解决方案,Asset 和 Attribute 应该放在同一个包中。但是 UniqueAttribute 可以在你想要的任何包中。

【讨论】:

    【解决方案4】:

    修改你的第二个选项

    asset.addAttribute(new UniqueAttribute(asset)); 
    

    像这样:

    class UniqueAttribute {
    Asset asset;
         public UniqueAttribute(Asset asset) { this.asset = asset; asset.addAttribute(this); }
    }
    

    对非唯一属性执行类似的方法。这意味着不要从外部使用 addAttribute() ,而只能在构造函数内部使用它。

    另一种选择是向 Asset 添加两个工厂方法:createAttribute() 和 createUniqueAttribute();

    【讨论】:

    • 这样,UniqueAttribute 的资产也可以是最终的,这会降低 UniqueAttributes 的可变性。
    • 虽然这意味着我在上面遇到了同样的问题,除了我将与 addAttribute 而不是 setAsset 的可见性争论。
    【解决方案5】:

    首先,这些实体确实 看起来是如此密切相关,以至于被放置在同一个包中。我会将它们放在同一个包中,并将 addAttribute 方法设为包私有。

    请记住,自从在 Java 中引入 AccessibleObject 以来,该语言中的可见性和访问控制已成为一种装饰......任何使用您的库的人都可以获取您的类并使私有方法和字段可访问和可修改!该死,他们甚至可以修改final 成员!因此,不要过多强调可见性方面,只需确保您的模型和方法流对您的用户有意义并正常工作即可。

    【讨论】:

    • 对不起,我的意思是 UniqueAttribute.setAsset() 方法被设为包私有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-16
    • 1970-01-01
    相关资源
    最近更新 更多