【问题标题】:Generic Java Framework to Manage Bidirectional Associations and Inverse Updates用于管理双向关联和反向更新的通用 Java 框架
【发布时间】:2008-10-04 00:26:29
【问题描述】:

我一直在寻找一种处理双向关联的通用方法以及一种在手动编写的 Java 代码中处理反向更新的方法。

对于那些不知道我在说什么的人,这里有一个例子。下面是我目前(不满意)解决方案的结果。

public class A {
    public B getB();
    public void setB(B b);
}

public class B {
    public List<A> getAs();
}

现在,当更新关联的任何一端时,为了保持一致性,另一端也必须更新。每次都手动

a.setB(b);
b.getA().add(a);

或通过将匹配代码放入 setter / getter 并使用自定义 List 实现。

我发现了一个过时的、未维护的项目,其依赖项不再可用 (https://e-nspire-gemini.dev.java.net/)。它通过使用用于自动注入必要代码的注释来解决问题。

有谁知道另一个以通用、不显眼的方式处理这个问题的框架吗?

乔, 埃尔玛

【问题讨论】:

    标签: java associations bidirectional inverse


    【解决方案1】:

    google 集合(来自 google 的内部代码)——http://code.google.com/p/google-collections/ 兼容 Java 泛型(不仅兼容,还很好地使用泛型)

    类 BiMap -- http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/package-summary.html 允许双向关联。

    其中一些类有望进入 JDK 7。

    【讨论】:

      【解决方案2】:

      除非您抽象出设置器,否则您将不得不提供某种事件通知机制。如果您的对象是 JavaBean,那么您正在考虑使用 PropertyChangeSupport 并触发属性更改事件。

      如果你这样做(或有一些其他机制来检测更改),那么 Glazed Lists 会提供一个ObservableElementList,它可以很容易地用于处理来自列表端的关联同步(即自动将 A 添加到 List调用 a.setB(b))。使用属性更改监控(或等效)可以轻松处理另一个方向。

      我意识到这不是一个通用的解决方案,但它似乎是一个简单的基础。

      请注意,这样的事情需要在 B 类中实现一个特殊的列表 - 在一般情况下您可以处理它的 AOP 类型解决方案(即使用 ArrayList 或类似的东西)绝非易事)。

      我还应该指出,您要实现的目标是数据绑定的圣杯。在字段级别有一些不错的绑定实现(例如 getter 和 setter)(参见 JGoodies 绑定和 JSR 295 示例)。列表类型绑定还有一个非常好的实现(Glazed Lists,上面提到过)。我们几乎在所有应用程序中都同时使用这两种技术,但从未尝试过像您所询问的那样抽象。

      如果我正在设计这个,我会看这样的东西:

      AssociationBuilder.createAssociation(A a, Connector< A> ca, B b,  Connector< B> cb, Synchronizer< A,B> sync)
      

      连接器是一个接口,它允许单一接口用于各种更改通知类型。 Synchronizer 是一个被调用的接口,用于确保两个对象在其中一个对象发生更改时保持同步。

      sync(ChangeInfo info, A a, B b) // make sure that b reflects current state of a and vice-versa.  
      

      ChangeInfo 提供有关哪些成员发生了变化以及实际发生了什么变化的数据。我们是。如果你想真正保持这个通用性,那么你几乎必须把它的实现交给框架用户。

      有了上述条件,就有可能拥有许多满足不同绑定标准的预定义连接器和同步器。

      有趣的是,上面的方法签名与 JSR 295 createAutoBinding() 方法调用非常相似。属性对象等价于连接器。 JSR 295 没有同步器(相反,它们具有指定为 ENUM 的绑定策略 - 加上 JSR 295 仅适用于 property->property 绑定,试图将一个对象的字段值绑定到该对象在另一个对象中的列表成员资格甚至不在他们的桌子上)。

      【讨论】:

        【解决方案3】:

        为了有意义,这些课程将是对等的。我建议使用包私有机制(在没有朋友的情况下)来保持一致性。

        public final class A {
            private B b;
            public B getB() {
                return b;
            }
            public void setB(final B b) {
                if (b == this.b) {
                    // Important!!
                    return;
                }
                // Be a member of both Bs (hence check in getAs).
                if (b != null) {
                    b.addA(this);
                }
                // Atomic commit to change.
                this.b = b;
                // Remove from old B.
                if (this.b != null) {
                    this.b.removeA(this);
                }
            }
        }
        
        public final class B {
            private final List<A> as;
            /* pp */ void addA(A a) {
                if (a == null) {
                    throw new NullPointerException();
                }
                // LinkedHashSet may be better under more demanding usage patterns.
                if (!as.contains(a)) {
                    as.add(a);
                }
            }
            /* pp */ void removeA(A a) {
                if (a == null) {
                    throw new NullPointerException();
                }
                as.removeA(a);
            }
            public List<A> getAs() {
                // Copy only those that really are associated with us.
                List<A> copy = new ArrayList<A>(as.size());
                for (A a : as) {
                    if (a.getB() == this) {
                        copy.add(a);
                    }
                }
                return Collection.unmodifiableList(copy);
            }
        }
        

        (声明:未经测试甚至编译。)

        大多数情况下是异常安全的(在异常情况下可能会泄漏)。线程安全、多对多、性能、库化等,留给感兴趣的读者作为练习。

        【讨论】:

        • 感谢您的回答,但我正在寻找一种以通用、非代码指导方式处理此问题的解决方案。
        【解决方案4】:

        感谢所有建议。但是没有一个能接近我所寻找的,我可能以错误的方式提出了这个问题。

        我一直在寻找双子座的替代品,因此想找到一种以不显眼的方式处理此问题的方法,而不会因无休止的检查和特殊的 List 实现而污染代码。正如 Kevin 所建议的,这当然需要基于 AOP 的方法。

        当我环顾四周时,我在 cnet 上发现了一个包含所有源和与源的依赖关系的 Gemini 包。依赖项的缺失来源是阻止我使用它的唯一问题。由于现在所有资源都可用,因此可以修复错误。 如果有人寻找这个:http://www.download.com/Gemini/3000-2413_4-10440077.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-02
          • 1970-01-01
          • 2015-06-06
          • 1970-01-01
          • 2013-03-07
          • 1970-01-01
          • 2013-08-27
          • 1970-01-01
          相关资源
          最近更新 更多