使Bloch的构建器模式成为线程安全的:如果从未失效,则在封闭构造器时需要重新检查吗? [英] Making Bloch's builder pattern thread-safe: Rechecking necessary in enclosing constructor if NEVER invalid?

查看:66
本文介绍了使Bloch的构建器模式成为线程安全的:如果从未失效,则在封闭构造器时需要重新检查吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近了解到Joshua Bloch的构建器模式,用于创建带有许多可选字段的对象.多年来,我一直在使用类似的东西,但是直到Bloch的书向我推荐它之前,我从未使用过内部类.我喜欢它.

I have recently learned Joshua Bloch's builder pattern for creating objects with many optional fields. I've been using something like it for years, but never used an inner-class until Bloch's book suggested it to me. I love it.

我了解到另一个线程可能会在实际构建bulider之前更改其配置(使用 build()),因此可能有必要重新验证封闭构造函数中的所有值班级.下面是一个生成器类的示例,该类可以有选择地重新验证其数据.

I understand that another thread may alter the bulider's configuration, before it's actually built (with build()), such that it may be necessary to re-validate all values in the constructor of the enclosing class. Below is an example of a builder class that optionally reverifies its data.

所以我的问题是:假设这是一个足够健壮的设计,当所有值都有默认值时-知道此类对于使用默认值来说是一个糟糕的选择-并且当每个set-attempt都经过验证时,是否-检查必要吗?尽管可能不同,但绝不会无效.正确吗?

So my question is this: Assuming this is a robust enough design, when there are defaults for all values--knowing this class is a poor choice for using defaults--and when every set-attempt is validated, is this re-check necessary? Although it may be different, it would never be invalid. Is that correct?

(尽管这种设计是可管理的,但由于可能需要重新验证,因此肯定使它变得复杂.而且,老实说,我从不使用多线程,但是我不想让这样做的人无法使用我的库.)

(Although this design is manageable, it is certainly complicated by the potential need for re-verification. And, honestly, I never multi-thread, but I don't want to make my library unusable by people that do.)

谢谢您的任何建议.

/**
   <P><CODE>java ReverifyBuilderInEnclosingCnstrXmpl</CODE></P>
 **/
public class ReverifyBuilderInEnclosingCnstrXmpl  {
   public static final void main(String[] igno_red)  {

      //Don't reverify
      ReverifyBuilderInEnclosingCnstrXmpl rvbdx = new ReverifyBuilderInEnclosingCnstrXmpl.Cfg().
         name("Big Bird").age(6).build();

      System.out.println(rvbdx.sName);
      System.out.println(rvbdx.iAge);

      //Do reverify
      rvbdx = new ReverifyBuilderInEnclosingCnstrXmpl.Cfg().
         reverifyInEnclosing().
         name("Big Bird").age(6).build();
   }

   public final String sName;
   public final int    iAge;
   /**
      <P>Create a new <CODE>ReverifyBuilderInEnclosingCnstrXmpl</CODE> with defaults.</P>
   **/
   public ReverifyBuilderInEnclosingCnstrXmpl()  {
      //Does not reverify. No need.
      this(new ReverifyBuilderInEnclosingCnstrXmpl.Cfg());
   }
   private ReverifyBuilderInEnclosingCnstrXmpl(ReverifyBuilderInEnclosingCnstrXmpl.Cfg rbdx_c)  {
      sName = rbdx_c.sName;
      iAge = rbdx_c.iAge;
      ReverifyBuilderInEnclosingCnstrXmpl.Cfg.zcibValues(rbdx_c, sName, iAge, "constructor");
   }
   public static class Cfg  {
      private String  sName   = null;
      private int     iAge    = -1;
      private boolean bReVrfy = false;
      public Cfg()  {
         //Defaults
         bReVrfy = false;
         name("Broom Hilda");
         age(127);
      }
      //Self-returning configuration...START
         //No way to unset.
         public Cfg reverifyInEnclosing()  {
            bReVrfy = true;
            return  this;
         }
         public Cfg name(String s_name)  {
            zcib_name(s_name, "name");
            sName = s_name;
            return  this;
         }
         public Cfg age(int i_age)  {
            zcib_age(i_age, "age");
            iAge = i_age;
            return  this;
         }
      //Self-returning configuration...END
      //Validate config...START
         public static final void zcibValues(ReverifyBuilderInEnclosingCnstrXmpl.Cfg rbdx_c, String s_name, int i_age, String s_clgFunc)  {
            try  {
               if(!rbdx_c.bReVrfy)  {
                  return;
               }
            }  catch(NullPointerException npx)  {
               throw  new NullPointerException("zcibValues: rbdx_c");
            }
            zcib_name(s_name, s_clgFunc);
            zcib_age(i_age, s_clgFunc);
         }
         public static final void zcib_name(String s_name, String s_clgFunc)  {
            if(s_name == null  ||  s_name.length() == 0)  {
               throw  new IllegalArgumentException(s_clgFunc + ": s_name (" + s_name + ") is null or empty.");
            }
         }
         public static final void zcib_age(int i_age, String s_clgFunc)  {
            if(i_age < 0)  {
               throw  new IllegalArgumentException(s_clgFunc + ": i_age (" + i_age + ") is negative.");
            }
         }
      //Validate config...END
      public ReverifyBuilderInEnclosingCnstrXmpl build()  {
         return  (new ReverifyBuilderInEnclosingCnstrXmpl(this));
      }
   }
}

推荐答案

首先-构建器模式本质上不是线程不安全.我不确定您如何得出结论.每个打算使用构建器的线程都将创建自己的 Builder 对象,以Joshua Bloch务实而优美的方式填充该对象,并使用它来构建该对象.在该机制的任何地方都不会影响 static 变量,除非您亲自介绍它,否则不会出现线程不安全的情况.

Firstly - the builder pattern is not inherently thread unsafe. I am not sure how you are concluding that it is. Each thread that intends to use the builder will create its own Builder object, populate it in Joshua Bloch's pragmatic and beautiful way and use it to construct the object. There are no static variables being affected anywhere in that mechanism, there is no thread unsafety unless you introduce it yourself.

在我的拙见中,您对验证的关注是粗略的预优化,它会产生可怕的人为设计和令人and肿的代码.没有理由仅仅因为您知道数据有效就尝试避免验证.验证几乎总是微不足道的,通常只需要几条指令即可.通过使用这些可怕的静态验证方法使类膨胀,与为避免验证而节省的时间相比,仅是为了加载此膨胀的代码,您所添加的cpu周期可能要多数千倍.

Your concern about validation is - in my humble opinion - a gross pre-optimisation that produces hideously contrived and horribly bloated code. There is no reason to try to avoid validation just because you know the data is valid. Validation is almost always trivial and often takes little more that a few instructions. By bloating the class with these horrible static validation methods you are probably adding thousands of times more cpu cycles just to load this bloated code than you are saving by avoiding the validation.

使用清晰,简洁,专利正确的和线程安全代码比较您的人为设计和肿代码,看看我的意思:

Compare your contrived and bloated code with this lucid, succinct and patently correct and thread safe code and see what I mean:

public class Thing {

    public final String name;
    public final int age;

    public Thing() {
        this(new Thing.Builder());
    }

    private Thing(Thing.Builder builder) {
        name = builder.name;
        age = builder.age;
    }

    public static class Builder {

        private String name = null;
        private int age = -1;

        public Builder() {
            name("Broom Hilda");
            age(127);
        }

        public Builder name(String name) {
            if (name == null || name.length() == 0) {
                throw new IllegalArgumentException("Thing.Builder.name (" + name + ") is null or empty.");
            }
            this.name = name;
            return this;
        }

        public Builder age(int age) {
            if (age < 0) {
                throw new IllegalArgumentException("Thing.Builder.age (" + age + ") is negative.");
            }
            this.age = age;
            return this;
        }

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

这篇关于使Bloch的构建器模式成为线程安全的:如果从未失效,则在封闭构造器时需要重新检查吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆