【问题标题】:How can I ensure my bean is built correctly?如何确保正确构建我的 bean?
【发布时间】:2013-05-15 11:43:32
【问题描述】:

我正在使用构建器模式构建一个 JavaBean(只有字段和 getter/setter)。

为了这个例子,假设这是我们的 bean:

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

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.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);
    }
  }
}

取自here

现在我一直在努力确保Pizza 中的所有字段都是非空的,通过反射,遍历Pizza 的字段并检查它们不为空,但它出现了(而且我这里可能是错的)我的字段在检查发生之前没有设置。 This code by Jon Skeet 是我为了检查我的字段的非空性而改变的(而不是计数,我抛出异常)。

然后我尝试检查构建器的字段,但构建器中有额外的字段(例如,我有一个可能为空的 XMLParser 字段)。通过比萨字段对构建器字段进行子集不起作用,因为它们具有不同的“包路径”(?),例如org.GiusepesPizzaria.pizza.size vs org.GiusepesPizzaria.builder.size

有没有更好的方法来检查这个?在实现反射方法之前,我使用了这种构造:

if(builder.size ==null){
    throw new BadPizzaException("Eh, what're ya doin'?"+
             " Pizza Size was not set correctly");
}else{
    size=builder.size;
}

但是,如果您说要检查大约 10 个字段,那么它就会结束,冗长且杂乱无章,应该是一个简单的类。

这就是我尝试过的。有没有更好的方法?

【问题讨论】:

  • 字段是否设置不应该是“可能”的情况。一旦你设置它们,它们就设置好了。不过,请确保您正在测试正确的字段:Builder 中的字段,而不是 Pizza 中的字段。
  • @MarkoTopolnik 我正在从内存中复制方法。当我有机会时,我会更新我的问题。不过,一般问题仍然存在。
  • 一般来说,反射是处理大量null-checking 的好方法。

标签: java javabeans builder


【解决方案1】:

确保设置所有变量的有趣模式是使用Step Builder Pattern,其中第一个设置器只允许设置第二个,第二个只允许设置第三个,依此类推。当您处于最后一步时,您可以构建类,然后您就会知道所有方法都已被调用。

该帖子的简短摘录:

Panino solePanino = PaninoStepBuilder.newBuilder()
        .paninoCalled("sole panino")
        .breadType("baguette")
        .fish("sole")
        .addVegetable("tomato")
        .addVegetable("lettece")
        .noMoreVegetablesPlease()
        .build();

你必须从 panino 的名称开始,然后是面包类型。

【讨论】:

    【解决方案2】:

    试试这个:

    public class Pizza
    {
        private final boolean bacon;
        private final boolean cheese;
        private final boolean pepperoni;
        private final int size;
    
        private Pizza()
        {
            throw new UnsupportedOperationException();
        }
    
        Pizza(
            final int theSize,
            final boolean theCheese,
            final boolean thePepperoni,
            final boolean theBacon)
        {
            bacon = theBacon;
            cheese = theCheese;
            pepperoni = thePepperoni;
            size = theSize;
        }
    }
    
    // new file.
    public class PizzaBuilder
    {
        private boolean bacon;
        private boolean cheese;
        private boolean pepperoni;
        private int size;
    
        public PizzaBuilder()
        {
            size = 9; // default size.
        }
    
        public void setHasBacon()
        {
            bacon = true;
        }
    
        public void setHasNoBacon()
        {
            bacon = false;
        }
    
        public void setHasCheese()
        {
            cheese = true;
        }
    
        public void setHasNoCheese()
        {
            cheese = false;
        }
    
        public void setHasPepperoni()
        {
            pepperoni = true;
        }
    
        public void setHasNoPepperoni()
        {
            pepperoni = false;
        }
    
        public void setSizeNineInch()
        {
            size = 9;
        }
    
        public void setSizeTwelveInch()
        {
            size = 12;
        }
    
        public Pizza buildPizza()
        {
            return new Pizza(size, cheese, pepperoni, bacon);
        }
    }

    使用上面的构建器,构建器不可能生产出无效的比萨饼。

    假设:仅支持 9 英寸和 12 英寸的比萨饼。根据需要添加更多 setSize。

    构建器使用我所说的 NMSeters。此样式设置器允许您设置值,但不公开所述值的实现。这似乎不是我的原创发明。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-06-15
      • 2014-06-08
      • 2015-09-07
      • 1970-01-01
      • 2014-09-13
      • 2016-01-31
      • 1970-01-01
      相关资源
      最近更新 更多