1

我一直在寻找一种处理双向关联的通用方法以及一种在手动编写的 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/)。它通过使用用于自动注入必要代码的注释来处理该问题。

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

ciao,埃尔玛

4

4 回答 4

5

google collections (from google's internal code) -- 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。

于 2008-10-04T00:47:44.253 回答
0

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

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

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

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

我还应该指出,您要实现的目标是数据绑定的圣杯。在字段级别有一些不错的绑定实现(例如 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 绑定,试图将一个对象的字段值绑定到该对象在另一个对象中的列表成员资格甚至不在他们的桌子上)。

于 2008-10-04T04:50:20.160 回答
0

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

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

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

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

于 2008-10-04T13:56:31.913 回答
0

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

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

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

于 2008-10-05T20:50:55.993 回答