java - 使用 Builder 也可以构建封装对象

标签 java design-patterns encapsulation builder lombok

假设我有一个封装了另一个类的类:

@Builder
public class Dragon {

  private Dimensions dimensions;
  private String name;

  public static class ParentBuilder {
    DimensionsBuilder innerBuilder = Dimensions.builder();

    public DragonBuilder height(double height) {
      this.innerBuilder.height(height);
      return this;
    }

    public DragonBuilder length(double length) {
      this.innerBuilder.length(length);
      return this;
    }

    public Dragon build() {
      return Dragon.builder()
        .dimensions(this.innerBuilder.build())
        .name(this.name)
        .build();
    }
  } 
}

@Builder
public class Dimensions {
  private double height;
  private double length;
}

请记住,这是一个非常简化的示例,真正的代码(不幸的是,与龙无关)将很多属性委托(delegate)给innerBuilder.

这样,我可以像这样实例化类:

Dragon dragon = Dragon.builder()
  .height(12.0)
  .length(25.0)
  .name("Smaug")
  .build();

而不是像这样:

Dragon dragon = Dragon.builder()
  .dimensions(Dimensions.builder()
    .height(12.0)
    .length(25.0)
    .build())
  .name("Smaug")
  .build;

添加构建器方法来直接构建内部类也是好的编码习惯吗?或者它是否违反了某些设计原则,因为它可能耦合得太紧了?

我已经遇到的一个问题是,在对内部类进行重构时,我还必须对父类应用大部分相同的重构。

最佳答案

在我看来,从样式/设计的角度来看,您的方法没有根本性的错误。但是,正如用户 JB Nizet 在评论中所解释的那样,存在两个主要问题:

  1. 您遇到维护问题,因为您必须复制每个外部构建器方法。 (Lombok 的 @Delegate 在这里帮不了你,因为它对 Lombok 自己生成的类不起作用。)
  2. 您的构建器的用户可以同时调用 dimensions(Dimensions)和委托(delegate)方法,这非常令人困惑。

从用户的角度来看,我希望这样使用构建器:

Dragon dragon = Dragon.builder()
    .dimensions()
        .height(12.0)
        .length(25.0)
        .back()
    .name("Smaug")
    .build();

这是实现它的方法(使用 Lombok 1.18.8):

@Builder
public class Dragon {
    private Dimensions dimensions;
    private String name;

    public static class DragonBuilder {

        private Dimensions.DimensionsBuilder innerBuilder = 
                new Dimensions.DimensionsBuilder(this);

        // If a method of the same name exists, Lombok does not generate
        // another one even if the parameters differ.
        // In this way, users cannot set their own dimensions object.
        public Dimensions.DimensionsBuilder dimensions() {
            return innerBuilder;
        }

        // Customize build() so that your innerBuilder is used to create 
        // the Dimensions instance.
        public Dragon build() {
            return new Dragon(innerBuilder.build(), name);
        }
    }
}

Dimensions 的生成器持有对容器的引用 DragonBuilder :

// Don't let Lombok create a builder() method, so users cannot 
// instantiate builders on their own.
@Builder(builderMethodName = "")
public class Dimensions {
    private double height;
    private double length;

    public static class DimensionsBuilder {
        private Dragon.DragonBuilder parentBuilder;

        // The only constructor takes a reference to the containing builder.
        DimensionsBuilder(Dragon.DragonBuilder parentBuilder) {
            this.parentBuilder = parentBuilder;
        }

        // Provide a method that returns the containing builder.
        public Dragon.DragonBuilder back() {
            return parentBuilder;
        }

        // The build() method should not be called directly, so 
        // we make it package-private.
        Dimensions build() {
            return new Dimensions(height, length);
        }
    }
}

这种方法可以扩展,因为 Lombok 会自动在构建器中生成所有必要的剩余 setter 方法。 此外,由于用户提供自己的 Dimensions 可能不会有任何意外。实例。 (你可以允许这样做,但我强烈建议对潜在的冲突进行运行时检查,例如检查是否已调用这两种方法。)

缺点是Dimensions.builder()不再可用,因此不能直接使用,也不能在具有 Dimensions 的其他类的构建器中使用 field 。但是,也有一个解决方案:使用 @SuperBuilder Dimensions并定义一个 class NestedDimensionsBuilder extends Dimensions.DimensionsBuilder<Dimensions, NestedDimensionsBuilder>DragonBuilder 内.

关于java - 使用 Builder 也可以构建封装对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56865787/

相关文章:

java - 巡航控制批处理文件构建从未完成

java - 如何从控制台启动我的应用程序作为服务器?

java - 使用java中的其他正常数据值将图像从servlet检索到jsp

java - 构建器模式精度

oop - 封装、抽象和多态是否需要继承?

design-patterns - 使用策略模式和命令模式

java - 在 OpenCV JavaCameraView 上更改帧大小

.net - 构建器、工厂方法和抽象工厂模式之间有什么区别?

c++ - 具有不同访问权限的类 : read-only to the many, 可写入特权

java - 在不破坏封装的情况下保存和加载对象