具有嵌套对象和构建器的不可变值对象

Immutable value objects with nested objects and builders

我们有一个大型系统,其中包含许多值对象,这些对象目前被记录为不可变的,并且不提供改变对象的方法。在重写代码的某些部分时,我认为不仅将 类 记录为不可变的,而且将它们声明为不可变的,这将是一个很好的练习。

具有嵌套对象的值对象如下所示:

immutable class Nested
{
    int i;

    public this(int i) immutable
    {
        this.i = i;
    }
}

immutable class Value
{
    private Nested nested_;

    public immutable(Nested) nested() const
    {
        return this.nested_;
    }

    package this(immutable(Nested) nested)
    {
        this.nested_ = nested;
    }
}

每当需要对任何值进行更改时,我们都会使用构建器来创建副本并修改属性。到目前为止一切顺利 - 但是当涉及到嵌套对象时,我 运行 遇到了麻烦。使用原始对象从构建器创建副本只能获取不可变的嵌套对象并存储它。但是构建器如何将嵌套对象更改为新对象?

我从 std.typecons 想到了 Rebindable - 但我不确定这是否是好的做法。

class Builder
{
    import std.typecons : Rebindable;

    Rebindable!(immutable(Nested)) nested_;

    this()
    {
        this.nested_ = null;
    }

    this(immutable(Value) value)
    {
        this.nested_ = value.nested;
    }

    public void nested(immutable(Nested) nested)
    {
        this.nested_ = nested;
    }

    public immutable(Value) value() const
    {
        return new immutable Value(this.nested_);
    }
}

void main()
{
    import std.stdio : writefln;

    immutable value = new immutable Value(new immutable Nested(1));

    writefln("i = %d", value.nested.i);

    auto builder = new Builder(value);
    immutable newNested = new immutable Nested(2);
    builder.nested = newNested;

    writefln("i = %d", builder.value.nested.i);
}

我是不是对不变性和常量正确性想得太多了?

此致,

鲁尼

您使用 Rebindable 的解决方案没问题,我认为这是实现此目标的最佳方式。另一种可能的解决方案是使 nested_ 可变并使用强制转换,但这不是那么优雅和安全:

class Builder
{
    import std.typecons : Rebindable;
    union
    {
        private Nested m_nested_;
        immutable(Nested) nested_;
    }

    this()
    {
        this.nested_ = null;
    }

    this(immutable(Value) value)
    {
        this.nested_ = value.nested();
    }

    public void nested(immutable(Nested) nested)
    {
        this.m_nested_ = cast(Nested)nested;
    }

    public immutable(Value) value() const
    {
        return new immutable Value(this.nested_);
    }
}