没有值的对象

时间:2011-01-20 09:34:24

标签: java object

我问this关于控制从阻塞队列中读取的线程的问题。虽然这不是我选择的解决方案,但有几个人建议在队列中添加一个特殊的“毒丸”或“哨兵”值,以便将其关闭:

public class MyThread extends Thread{
    private static final Foo STOP = new Foo();
    private BlockingQueue<Foo> blockingQueue = new LinkedBlockingQueue<Foo>();

    public void run(){
        try{
            Foo f = blockingQueue.take();
            while(f != STOP){
                doSomethingWith(f);
                f = blockingQueue.take();
            }
        }
        catch(InterruptedException e){

        }
    }

    public void addToQueue(Foo f) throws InterruptedException{
        blockingQueue.put(f);
    }

    public void stop() throws InterruptedException{
        blockingQueue.put(STOP);
    }
}

虽然我喜欢这种方法,但我决定不使用它,因为我不确定STOP字段使用什么值。在某些情况下很明显 - 例如,如果你知道你正在插入正整数,负数可以用作控制值 - 但Foo是一个相当复杂的类。它是不可变的,因此有一个带有几个参数的构造函数。添加无参数构造函数意味着将几个字段保留为未初始化或为null,这会导致方法在其他地方使用时中断 - Foo不仅仅与MyThread一起使用。类似地,将虚拟值放入主构造函数只会传递此问题,因为几个字段和构造函数参数本身就是重要对象。

我只是编程过于防守吗?我是否应该担心在类中添加无参数构造函数,即使没有setter使对象可用(只是假设其他程序员足够明智而不使用该构造函数)?如果Foo的设计没有无参数构造函数或者至少是非值,那么它是否会被破坏 - 在所有方法中放置if(someField == null){throw new RuntimeException();}检查会更好吗?

4 个答案:

答案 0 :(得分:2)

我真的没有看到这个设计的优势与简单的布尔变量相比,表明循环应该停止。

但是如果你真的想要使用这个设计,我会建议创建一个私有的无参数构造函数,然后创建一个静态的STOP Foo。像这样。

public class Foo {

  public static final Foo STOP = new Foo();
  ... fields

  private Foo(){}
  public Foo(...){
    ...
  }

  ...

}

public class MyThread extends Thread{
  private static final Foo STOP = new Foo();
  private BlockingQueue<Foo> blockingQueue = new LinkedBlockingQueue<Foo>();

  public void run(){
      try{
          Foo f = blockingQueue.take();
          while(f != STOP){
              doSomethingWith(f);
              f = blockingQueue.take();
          }
      }
      catch(InterruptedException e){

      }
  }

  public void addToQueue(Foo f) throws InterruptedException{
      blockingQueue.put(f);
  }

  public void stop() throws InterruptedException{
      blockingQueue.put(Foo.STOP);
  }
}

这样做的好处是你还没有暴露出无效的构造函数。

缺点是Foo类知道在某些情况下它被用作“毒丸”,这可能不是它的用途。另一个缺点是STOP对象可能不一致。您可以从中创建一个匿名子类,并使用UnsupportedOperationException或其他方法禁用这些方法。

答案 1 :(得分:1)

我认为你不使用空构造函数是正确的。如果Foo是一个如此复杂的类,那么使用完整的对象似乎不合逻辑。

如果可以添加null。这似乎是一个很好的方式。

另一种方法也可以是实现接口。 IBlockableQueueObject?这可以通过foo对象和STOP符号来实现。唯一的问题是,如果它不是STOP,你必须将界面强制转换回Foo

答案 2 :(得分:1)

另一种选择是将Foo包装在通用包装器中,例如:

public class Wrapped<T> {
    private final T value;

    public Wrapped(T value) {
        this.value = value;
    }

    public T get() { return value; }
}

然后您可以使用它将空值作为毒丸传递给BlockingQueue<Wrapped<Foo>>

答案 3 :(得分:0)

应该担心没有导致可用实例的无参数构造函数。

Foo的设计听起来很好 - 我通常会假设我不允许将null传入构造函数,除非文档特别允许我这样做。特别是有一个不可变的类。