简体   繁体   English

不变性和图形模型 - 如何创建它们?

[英]Immutability and Graphs Models - How to create them?

I will use example as its easier to show what I want to do. 我将使用示例,因为它更容易显示我想要做的事情。 I have three classes X, Y and Z, and I would like to be able to have them as immutable objects. 我有三个类X,Y和Z,我希望能够将它们作为不可变对象。 Here they are. 他们来了。

Class X : X级:

public class X{
    private int id;
    private String dataX;

    private Collection<Y> collectionOfY;
    private Collection<Z> collectionOfZ;

    /* Constructors */
    /* Getters */
    /* "with" functions */

}

Class Y : Y级:

public class Y{

    private int id;
    private String dataY;

    private Collection<X> collectionOfX;
    private Collection<Z> collectionOfZ;

    /* Constructors */
    /* Getters */
    /* "with" functions */

}

Class Z : Z级:

public class Z{

    private int id;
    private String dataZ;

    private Collection<X> collectionOfX;
    private Collection<Y> collectionOfY;

    /* Constructors */
    /* Getters */
    /* "with" functions */

}

So they are linked with each other and form a graph with a cyclic structure. 因此它们彼此链接并形成具有循环结构的图形。 I would like to have immutable object if possible. 如果可能的话我想拥有不可变对象。 I've implemented "With" functions which will send back a copy of a given object with just one property changed. 我已经实现了“With”函数,这些函数只发送一个属性就会发回一个给定对象的副本。

But, if I implement a total immutability, I will possibly cause a replication of every data linked (directly or indirectly) to the object I changed : Changing "dataZ" usually means that I want to replace the old object by the newly created one in linked X and Y. Which will be replicated due to the change, etc ... 但是,如果我实现完全不变性,我可能会复制链接(直接或间接)到我更改的对象的每个数据:更改“dataZ”通常意味着我想用新创建的对象替换旧对象链接的X和Y.由于变化等原因将被复制...

Another solution would be to render Collections which link the three classes not immutable (and only have a partial immutability) but then I lost almost all interest of trying to have immutability. 另一个解决方案是渲染集合,这三个类不是不可变的(并且只有部分不变性),但后来我几乎失去了尝试不变的所有兴趣。

Anyone have a good idea ? 有人有个好主意吗? Or am I asking for the impossible ? 还是我要求不可能的? Should I return to the old setter (even if it means more complex concurrency management) ? 我应该回到旧的setter(即使它意味着更复杂的并发管理)?

Thanks in advance ;) 提前致谢 ;)

C# is able to get around this with its built-in Lazy<T> . C#能够通过内置的Lazy<T>解决这个问题。 A Lazy is an object whose actual value has not yet been computed - when you construct a Lazy , you pass a factory function as a parameter. Lazy是一个尚未计算其实际值的对象 - 在构造Lazy ,将工厂函数作为参数传递。

You can ask a Lazy for its value with the Value property. 您可以使用Value属性向Lazy询问其值。 If the factory function has already run, then the already-constructed value is returned. 如果工厂函数已经运行,则返回已构造的值。 If the factory function hasn't run, then it will run at this time. 如果工厂功能尚未运行,则此时将运行。 Whatever the case, the return of the Value property is always the same. 无论如何, Value属性的返回始终是相同的。 Once a particular Lazy 's value has been determined, it sticks. 一旦确定了一个特定的Lazy的价值,它就会坚持下去。

A lazy is an immutable reference to a possibly-not-yet constructed object. 惰性是对可能尚未构造的对象的不可变引用。 It uses mutability internally, but from the outside, it appears to be immutable. 它在内部使用可变性,但从外部看,它似乎是不可变的。

In your case, if you had a Java equivalent to Lazy , you would change your collections from, say, Collection<Y> to Collection<Lazy<Y>> . 在您的情况下,如果您的Java等效于Lazy ,则可以将集合从Collection<Y>更改为Collection<Lazy<Y>> This would enable an instance of X to refer to some not-yet-constructed instances of Y. And the code which constructs the X s, Y s, and Z s would not directly build those instances, but would instead build instances of Lazy . 这将使X的实例能够引用一些尚未构造的Y实例。构造X s, Y s和Z s的代码不会直接构建这些实例,而是构建Lazy实例。 These instances would take factory functions as parameters; 这些实例将工厂函数作为参数; these factory functions, in turn, would need to reference to some of the Lazy values. 反过来,这些工厂函数需要引用一些Lazy值。 This means that, within the context of the function that's constructing and wiring these things together, you'll need to have mutable references to Lazy instances. 这意味着,在构造和连接这些东西的函数的上下文中,您需要对Lazy实例进行可变引用。

To see what I mean, if you try to create a cycle of two objects (I'm not completely up on Java 8, so I might have syntax errors): 要明白我的意思,如果你试图创建一个两个对象的循环(我不完全掌握Java 8,所以我可能有语法错误):

Lazy<X> a;
Lazy<Y> b;

a = new Lazy<X>(() -> {
    List<Y> ys = new ArrayList<Y>();
    ys.add(b.getValue());
    return new X(ys);
});

b = new Lazy<Y>(() -> {
    List<X> xs = new ArrayList<X>();
    xs.add(a.getValue());
    return new Y(xs);
});

In practice, I don't think that will work. 在实践中,我认为这不会起作用。 I think closed-over variables need to be final in Java (this isn't the case in C#). 我认为封闭变量需要在Java中是最终的(在C#中不是这种情况)。 So I think you'd need to actually do this: 所以我认为你需要实际做到这一点:

final Lazy<X>[] a = new Lazy<X>[1];
final Lazy<Y>[] b = new Lazy<Y>[1];

a[0] = new Lazy<X>(() -> {
    List<Y> ys = new ArrayList<Y>();
    ys.add(b[0].getValue());
    return new X(ys);
});

b[0] = new Lazy<Y>(() -> {
    List<X> xs = new ArrayList<X>();
    xs.add(a[0].getValue());
    return new Y(xs);
});

This works because the two lambdas aren't evaluated immediately. 这是有效的,因为不会立即评估两个lambda。 As long as a[0] and b[0] get set to valid values before these lambdas are executed, everything will work fine. 只要在执行这些lambda之前a[0]b[0]设置为有效值,一切都会正常工作。

Note that this uses mutability, but mutability in very limited scopes. 请注意,这使用了可变性,但在非常有限的范围内具有可变性。 There's mutability inside the Lazy instances, but these instances appear to be immutable. Lazy实例中存在可变性,但这些实例似乎是不可变的。 There's mutability in the wire-up function, but that function will run up front and terminate, whereas the constructed objects can live for much longer. 在连线功能中存在可变性,但该功能将在前面运行并终止,而构造的对象可以存活更长时间。 This constrained mutability is, at least for me, an acceptable tradeoff. 至少在我看来,这种受约束的可变性是可接受的权衡。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM