【问题标题】:Joshua Bloch's Builder pattern and PMD warningsJoshua Bloch 的 Builder 模式和 PMD 警告
【发布时间】:2011-05-30 09:22:34
【问题描述】:

我使用 Joshua Bloch 的 Builder 模式编写了一个类,类似于这个 Pizza 示例:

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

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

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

但 PMD 报告了 2 个警告:

  1. (指向方法 Builder.build()) 避免通过私有构造函数从外部实例化 构造函数的类。 通过私有方式实例化 来自外部的构造函数 构造函数的类经常导致 访问器的生成。一个工厂 方法,或非私有化 构造函数可以消除这个 情况。生成的类文件 实际上是一个接口。它给 访问类的能力 调用一个新的隐藏包范围 接受接口的构造函数 作为补充参数。这 变成一个私有构造函数 有效地与包合二为一 范围,并且具有挑战性 辨别。
  2. 类不能被实例化并且 不提供任何静态方法 或字段。一个有私有的类 构造函数并且没有任何 静态方法或字段不能 用过。

我应该忽略这些警告吗?

另一个问题:PizzaBuilder 类中的私有字段是重复的。当私有字段的数量变大时,这会很烦人。有什么办法可以避免吗?

【问题讨论】:

  • for 1. 您可以将您的 Pizza 构造函数定义为包保护。这也应该解决 2.
  • 图案不错; PMD只是愚蠢。我个人会忽略它。
  • 只是一个旁注:我一直在使用一个名为Make-It-Easy 的小型框架,它有助于构建这个builders,并且代码可读性很强。我主要将它用于我的单元测试,但它绝对可以用于生产代码。
  • 我会忽略第一个 PMD 警告。但是,第二个是合理的。您的 Pizza 类,就像您的问题一样,是不可用的,因为它没有任何方法。加入一些吸气剂,PMD 不会再抱怨了(希望如此)。

标签: java pmd builder-pattern


【解决方案1】:

关于如何删除重复。

我会得到更多的反对票 :) 但也许是这样的?

class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;

public static class Builder {
    private Pizza pizza = new Pizza();

    public Builder(int size) {
        pizza.size = size;
    }

    public Builder cheese(boolean value) {
        pizza.cheese = value;
        return this;
    }

    public Builder pepperoni(boolean value) {
        pizza.pepperoni = value;
        return this;
    }

    public Builder bacon(boolean value) {
        pizza.bacon = value;
        return this;
    }

    public Pizza build() {
        return pizza;
    }
}

private Pizza() {
}
}

【讨论】:

  • 我不喜欢这个。您可以在调用 build() 之后通过创建它的 Builder 更改 Pizza 上的字段。构建器模式的最佳用途之一是简化不可变类的创建。
  • 抱歉,但是使用这种样式,您仍然可以获得:1) “避免通过构造函数类外部的私有构造函数进行实例化。” 2)“类不能实例化,不提供任何静态方法或字段”,所以问题没有解决。
【解决方案2】:

Pizza 类中的私有字段和 生成器是重复的。这将是 烦人的时候私数 领域越来越大。有没有 有办法避免吗?

我个人通过使用包含所有字段的第三个私有静态值对象类来解决这个问题,并在构建器和主类中使用它(字段访问由委托处理)。当然,这最终可能会增加行数/类数,但如果您的构建器最终因大量字段和检查而变得复杂,这将是非常宝贵的。

此外,实际上在 Pizza 类上提供一个静态方法,该方法使用必填字段构建一个 Pizza 对象,这并没有什么坏处。当然,除非您不确定必填字段是什么,或者担心必填字段在您的课程演变过程中可能会发生变化。重点是,只要您经过深思熟虑后可以证明自己的行为是正当的(就像 Joshua Bloch 所说的那样),您就可以放心地忽略这些警告,因为您知道自己在做什么。 :-)

一次性sn-p:

public class Pizza {

    private final PizzaVO vo;

    private static class PizzaVO {

        int size;

        boolean cheese;

        boolean pepperoni;

        boolean bacon;
    }

    public static class Builder {

        private final PizzaVO vo = new PizzaVO();

        public Builder(int size) {
            vo.size = size;
        }

        public Builder cheese(boolean value) {
            vo.cheese = value;
            return this;
        }

        public Builder pepperoni(boolean value) {
            vo.pepperoni = value;
            return this;
        }

        public Builder bacon(boolean value) {
            vo.bacon = value;
            return this;
        }

        public Pizza build() {
            return new Pizza(vo);
        }
    }

    private Pizza(PizzaVO vo) {
        this.vo = vo;
    }

    public int getSize() {
        return vo.size;
    }

    // other getter setter methods as per your taste

}

【讨论】:

  • 不值得使用这种风格。问题没有解决。
  • 你好 aloplop85,你能澄清哪些问题没有解决,为什么?
  • @sudocoder 至少会出现 PMD 错误“避免通过构造函数类外部的私有构造函数进行实例化。”仍然存在
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-29
  • 1970-01-01
  • 1970-01-01
  • 2015-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多