【问题标题】:How to create multiple varieties of a Class using the Builder Pattern?如何使用构建器模式创建多种类?
【发布时间】:2021-08-11 07:52:31
【问题描述】:

我的目标是使用构建器模式根据产品类别创建产品详细信息字段。

无论产品类别如何,这是基本的ProductDetails 属性。

int stock;          // required
String ships_from;  // required

String brand;       // optional

计算机类可以有

String model;           // optional
String screen_size;     // optional
String warranty_period; // optional

食品类可以有

String shelf_life;      // required

String expiration_date; // optional

这是我使用的基本构建器模式

public class ProductDetails {
  // attributes

  private ProductDetails(ProductDetailsBuilder builder) {
    this.attribute1 = builder.attribute1;
    this.attribute2 = builder.attribute2; // and so on.
  }

  // getters

  public static class ProductDetailsBuilder {
    // attributes

    public ProductDetailsBuilder(//required attributes) {
      this.attribute1 = attribute1;
    }

    // setters

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

当我尝试将ProductDetails 类扩展为例如ProductDetails_ComputerProductDetails_Food 类时出现问题。

public class ProductDetails_Computer extends ProductDetails {
  // attributes

  private ProductDetails_Computer(ProductDetails_ComputerBuilder builder) {
    this.attribute1 = builder.attribute2;
  }

  // getters

  public static class ProductDetails_ComputerBuilder {
    // attributes.

    // setters

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

我的预期结果:我可以做到public class ProductDetails_Computer extends ProductDetails

我的实际结果:因为ProductDetails 构造函数是私有的/受保护的,所以我不能扩展。有些网站禁止使用公共构造函数以避免直接初始化,我同意这种做法。

【问题讨论】:

  • 也许这thread 有帮助?

标签: java builder builder-pattern


【解决方案1】:

Effective Java 建议在这种情况下使用构建器的并行层次结构

抽象类具体类如下所示:

ProductDetails(基础抽象类):

abstract public class ProductDetails {
    int stock;
    String ships_from;

    String brand;
    
    // Recursive generics
    abstract static class Builder<T extends Builder<T>> {
        int stock;
        String ships_from;

        String brand;

        public Builder(int stock, String ships_from) {
            this.stock = stock;
            this.ships_from = ships_from;
        }

        // returns the reference of implementation (child class)
        T brand(String brand) {
            this.brand = brand;
            return self();
        }
        
        // force implementation to return child class' builder
        protected abstract T self();
         
       // force implementation to return itself while holding "Code to interface" practice
        abstract ProductDetails build();
    }

    protected ProductDetails(Builder<?> builder) {
        this.stock = builder.stock;
        this.ships_from = builder.ships_from;

        this.brand = builder.brand;
    }
    
}

实现可能如下所示:

ProductDetails_Computer

public class ProductDetails_Computer extends ProductDetails {
    String model;
    String screen_size;
    String warranty_period;

    private ProductDetails_Computer(Builder builder) {
        super(builder);
        this.model = builder.model;

        this.screen_size = builder.screen_size;
        this.warranty_period = builder.warranty_period;
    }

    public static class Builder extends ProductDetails.Builder<Builder> {
        String model;
        String screen_size;
        String warranty_period;

        public Builder(int stock, String ships_from) {
            super(stock, ships_from);
        }

        // child class attributes setter
        public Builder model(String model) {
            this.model = model;
            return this;
        }

        public Builder screen_size(String screen_size) {
            this.screen_size = screen_size;
            return this;
        }

        public Builder warranty_period(String warranty_period) {
            this.warranty_period = warranty_period;
            return this;
        }

        @Override
        protected Builder self() {
            return this;
        }

        @Override
        ProductDetails build() {
            return new ProductDetails_Computer(this);
        }

    }

    @Override
    public String toString() {
        return "ProductDetails_Computer [stock= " + stock + ", brand= " + brand + ", model=" + model + ", screen_size="
                + screen_size + ", warranty_period=" + warranty_period + "]";
    }

}

TestClass


public class TestBuilder {
    public static void main(String[] args) {
        var computerBuilder = new ProductDetails_Computer.Builder(20, "XYZ");
        computerBuilder.brand("Some_brand")
                       // able to invoke child class methods because of recursive generics
                       .screen_size("200").model("some_model");
        
        System.out.println(computerBuilder.build().toString());
        
    }
}

输出:

ProductDetails_Computer [stock= 20, brand= Some_brand, model=some_model, screen_size=200, warranty_period=null]

你可以阅读关于递归泛型here

【讨论】:

  • 是否有理由实现递归泛型而不是直接使用extends ProductDetails.Builderpublic static class ProductDetails_ComputerBuilder
  • @kidfrom 递归泛型是 Builder&lt;T extends Builder&lt;T&gt;&gt;。从public static class ProductDetails_ComputerBuilder extends ProductDetails.Builder 开始,试着看看你面临的问题。 :) 即使您找到了解决方法,您也应该知道构建器模式旨在separate construction from representation
  • 我真的很困惑。我使用 JUnit 来测试 ProductDetailsProductDetails_ComputerProductDetails_Food 对象的每个属性,它们在没有递归泛型的情况下通过了测试。请解释为什么您推荐使用递归泛型,我会将其标记为答案。
  • @kidfrom 是不是因为你按这个顺序调用了computerBuilder.screen_size("200").model("some_model").brand("Some_brand")(最后是brand())?您的brand() 方法实现必须有一个返回类型ProductDetails.Builder。这就是递归泛型的用武之地:您不必在父类方法之前链接子类方法。
  • @kidfrom 如果不是上述情况,您应该添加您的实现作为答案。这将对每个人都有帮助:)
猜你喜欢
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-07
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多