【问题标题】:Builder pattern: nested objects created through other builders构建器模式:通过其他构建器创建的嵌套对象
【发布时间】:2016-01-17 15:11:31
【问题描述】:

假设我有两个对象,都是通过构建器模式创建的,一个嵌套在另一个对象中:

class Parent {
    private final Child child;

    private Parent(Child child) {
         this.child = child;
    }

    public static class Builder {
         private Child child;         

         public Builder() {}

         public Builder child(Child child) {
             this.child = child;
             return this;
         }

         public Parent build() {
             return new Parent(child);
         }
    }
}

class Child {
    private final long id;

    private Child(Builder builder) {
         this.id = builder.id;
    }

    public static class Builder {
         private long id;         

         public Builder() {}

         public Builder id(long id) {
             this.id = id;
             return this;
         }

         public Parent build() {
             return new Child(this);
         }
    }
}

所以,显而易见的用法很简单:

Person.Builder parentBuilder = new Person.Builder().child(new Child.Builder().id(10).build());

制作很常见吗

public static class Builder {
     private ChildBuilder child;         

     public Builder() {}

     public Builder child(ChildBuilder child) {
         this.child = child;
         return this;
     }

     public Builder resetChildId() {
          child.id(0);
          return this;
     }

     public Parent build() {
         Child childToPass = child.build();
         return new Parent(childToPass);
     }
}

这样以后仍然可以更新 child#id,但是由于后期绑定,最近在 Parent.Builder#build() 方法期间会抛出错误。

【问题讨论】:

  • dp Builder 允许您构建一个不可变对象,您无需修改​​,因此类成员上的 final 修饰符。因此,一旦创建了对象,就无法进行任何修改。另外,您需要更正 public Builder id(long id) { this.id = id; return this;} 和 public Builder child(Child child) { this.child = child;返回这个;}
  • 很常见我不知道,我可以告诉你的是,在 Parent Builder 中,child() 方法接受一个 Child 对象,子对象是不可变的,你以后不能resetChildId,你需要为了保留 Child 构建器,这样您就可以稍后设置 id 并仅在您确定状态不再更改时构建。
  • @VadimKirilchuk 为什么不简单地传递子实例而不是构建器。如果你想改变孩子的一些东西,你可以替换整个孩子实例。如果您只想更改孩子的单件,则可以执行parent.child(new ChildBuilder(parent.child()).setSomeProperty(...).build())之类的操作
  • @VadimKirilchuk 这就是我要说的,允许从现有的孩子构建构建器。因此,如果您想更改子属性,您可以通过从现有子构造 ChildBuilder 来替换整个子实例。 ParentBuilder 将持有 Child 实例而不是 ChildBuilder 实例。
  • @VadimKirilchuk By the way, I wouldn't care about nice and clean error reporting to the UI. That's a UX problem which can be solved in the UI. 此外,如果您正确使用值对象,您可能根本不需要所有这些构建器。例如,您可能有一个强制执行 255 个字符规则的 ParentName 类。然后,您的 Parent 对象可以期待 ParentName 而不是简单的字符串。

标签: java design-patterns builder


【解决方案1】:

我会将Child 实例传递给Parent 而不是ChildBuilder 实例。

如果您想在之后更改Child 属性,那么您可以简单地从parentBuilder.child() 构造一个新的ChildBuilder

但是,当我看到所有这些建筑商时,我对设计感到担忧。 DDD 完全是关于无处不在的语言,“builder”当然不是其中的一部分。有时您别无选择在设计中引入技术概念,但我相信您可能会忘记其他可能有帮助的 DDD 构建块。

我到处都有构建器,因为我必须对每个构建器进行验证 应用程序中的域实体。例如,父母的名字不超过 255,但对于不超过 1000 的孩子。- Tahar Bakir(来自 cmets)

您上面描述的规则可以被封装并在可以作为值对象实现的领域概念(例如ParentNameChildName)中执行。

然后,您的 ParentChild 类可以使用这些概念而不是字符串。

【讨论】:

  • 能否请您给我一些有用的链接到文章的解释和在值对象中完成验证的示例?谢谢
【解决方案2】:

希望对你有帮助

如何使用它的示例在 main 方法中,这将打印 10 0

父类:

public class Parent {
  private final Child child;

  private Parent(Child child) {
     this.child = child;
  }

  public Child getChild(){
    return this.child;
  }

  public static class Builder {
    private Child.Builder childBuilder;

    public Builder() {}

    public Builder child(Child.Builder childBuilder) {
      this.childBuilder = childBuilder;
      return this;
    }

    public void resetChildId() {
      childBuilder = childBuilder.id(0);
    }

    public Parent build() {
      return new Parent(childBuilder.build());
    }
  }

  public static void main (String[] args){
    Parent.Builder parentBuilder = new Parent.Builder().child(new Child.Builder().id(10));

    System.out.println(parentBuilder.build().getChild().getId());
    //Reset the sucker
    parentBuilder.resetChildId();
    System.out.println(parentBuilder.build().getChild().getId());
  }
}

子类:

class Child {
  private final long id;

  private Child(Builder builder) {
    this.id = builder.id;
  }

  public long getId(){
    return this.id;
  }

  public static class Builder {
    private long id;

    public Builder() {}

    public Builder id(long id) {
      this.id = id;
      return this;
    }

    public Child build() {
      return new Child(this);
    }
  }
}

【讨论】:

  • 是的,这正是我关心的代码。在代码中,您仅在调用 parent#build() 时构建 Child,这会延迟所有验证。所以,我的问题是知道其他人通常是怎么做的?是否可以进行后期验证,还是采用完全不同的方法更好?
猜你喜欢
  • 2023-03-20
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多