没有值的对象

发布于 2024-10-12 15:31:18 字数 1351 浏览 0 评论 0 原文

我问这个关于控制从阻塞队列读取的线程的问题。尽管这不是我选择采用的解决方案,但有几个人建议将特殊的“毒丸”或“哨兵”值添加到队列中以将其关闭,如下所示:

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 一起使用。类似地,将虚拟值放入主构造函数只会传递这个问题,因为几个字段和构造函数参数本身就是重要的对象。

我是否只是过度防御性地编程?我是否应该担心向类中添加无参构造函数,即使没有设置器使对象可用(假设其他程序员足够明智,不会使用该构造函数)?如果 Foo 不能有一个无参构造函数或至少一个非值,它的设计是否会被破坏 - 最好将 if(someField == null){throw new RuntimeException();} 检查所有方法?

I was asking this question about controlling a thread that was reading from a blocking queue. Although it wasn't the solution I chose to go with, several people suggested that a special "poison pill" or "sentinel" value be added to the queue to shut it down like so:

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);
    }
}

While I like this approach, I decided not to use it because I wasn't sure what value to use for the STOP field. In some situations it's obvious - for instance, if you know you're inserting positive integers, negative numbers could be used as control values - but Foo is a fairly complex class. It's immutable and hence has a constructor that takes several arguments. To add a no-argument constructor would mean leaving several fields uninitialised or null, which would cause methods to break if they were used elsewhere - Foo is not just used with MyThread. Similarly, putting dummy values into the main constructor would just pass this problem on as several of the fields and constructor parameters are themselves significant objects.

Am I simply programming over-defensively? Should I worry about adding no-argument constructors to a class, even if there are no setters to make the object usable (just assume other programmers will be sensible enough to not use that constructor)? Is the design of Foo broken if it can't have a no-argument constructor or at least a non-value - would it be better to put if(someField == null){throw new RuntimeException();} checks in all methods?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一束光,穿透我孤独的魂 2024-10-19 15:31:18

我真的不明白这种设计与指示循环应该停止的简单布尔变量相比有什么优势。

但如果你真的想采用这种设计,我建议创建一个私有的无参数构造函数,并创建一个静态 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 或其他方法的方法。

I don't really see what the advantage of this design is versus a simple boolean variable to indicate the loop should stop.

But if you really want to go with this design, I would suggest making a private no-arg constructor, and making a static STOP Foo. Like this.

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);
  }
}

This has the advantage that you're still not exposing an invalid constructor.

The disadvantage is that the Foo class knows that in some cases it's used as a 'poison pill', which might not be what it's for. Another disadvantage is that The STOP object might be inconsistent. You could make an anonymous subclass from it do disable the methods with UnsupportedOperationException or something.

奢欲 2024-10-19 15:31:18

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

如果可以添加null。这似乎是一个不错的方法。

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

I think you're right about not using empty constructors. If Foo is such an complex class, it doesn't seem logical to use a complete object for that.

If adding a null is possible. That seems a nice way to go.

Another way could also be to implement an interface. IBlockableQueueObject? This could be implemented by the foo object and by the STOP sign. Only thing is that you have to cast the interface back to the Foo if it is not a STOP.

习ぎ惯性依靠 2024-10-19 15:31:18

另一种选择是将 Foo 包装在一个通用包装器中,如下所示:

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

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

    public T get() { return value; }
}

然后您可以使用该包装器将 null 值作为毒丸传递给 BlockingQueue>

another option would be to wrap Foo in a generic wrapper such as this:

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

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

    public T get() { return value; }
}

which you can then use to pass a null value as a poison pill to a BlockingQueue<Wrapped<Foo>>.

窝囊感情。 2024-10-19 15:31:18

应该担心无参构造函数不会产生可用的实例。

Foo 的设计听起来不错 - 我通常假设我不允许将 null 传递到构造函数中,除非文档明确允许我这样做。尤其是对于不可变的类。

You should worry about having no-argument constructors that don't result in usable instances.

The design of Foo sounds fine - I would generally assume that I'm not allowed to pass in null into a constructor unless the documentation specifically allows me to. Especially with an immutable class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文