Объект без значения - PullRequest
       4

Объект без значения

2 голосов
/ 20 января 2011

Я задавал этот вопрос об управлении потоком, который читал из очереди блокировки. Хотя это не было решением, которое я выбрал, несколько человек предложили добавить в очередь специальное значение «отравляющая таблетка» или «страж», чтобы отключить его следующим образом:

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 - довольно сложный класс. Он неизменен и, следовательно, имеет конструктор, который принимает несколько аргументов. Добавить конструктор без аргументов означало бы оставить несколько полей неинициализированными или пустыми, что привело бы к разрыву методов, если они использовались в другом месте - Foo не используется только с MyThread. Точно так же, размещение фиктивных значений в главном конструкторе просто решило бы эту проблему, поскольку некоторые поля и параметры конструктора сами по себе являются важными объектами.

Я просто программирую слишком оборонительно? Должен ли я беспокоиться о добавлении конструкторов без аргументов в класс, даже если нет установщиков, чтобы сделать объект пригодным для использования (просто предположим, что другие программисты будут достаточно разумны, чтобы не использовать этот конструктор)? Не нарушен ли дизайн Foo, если в нем не может быть конструктора без аргументов или, по крайней мере, без значения - было бы лучше поставить проверки if(someField == null){throw new RuntimeException();} во всех методах?

Ответы [ 4 ]

2 голосов
/ 20 января 2011

Я действительно не вижу, в чем преимущество этой схемы по сравнению с простой логической переменной, указывающей, что цикл должен остановиться.

Но если вы действительно хотите использовать этот дизайн, я бы предложил создать частный конструктор без аргументов и сделать статический 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 голос
/ 15 февраля 2011

другой вариант заключался бы в том, чтобы обернуть Foo в универсальную оболочку, такую ​​как эта:

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

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

    public T get() { return value; }
}

, которую затем можно использовать, чтобы передать нулевое значение в виде отравленной таблетки для BlockingQueue<Wrapped<Foo>>.

1 голос
/ 20 января 2011

Я думаю, вы правы, что не используете пустые конструкторы.Если Foo такой сложный класс, не представляется логичным использовать для этого полный объект.

Если добавление null возможно.Кажется, это хороший путь.

Другим способом может быть также реализация интерфейса.IBlockableQueueObject?Это может быть реализовано объектом foo и знаком STOP.Единственное, что вы должны привести интерфейс обратно к Foo, если это не STOP.

0 голосов
/ 20 января 2011

Вам следует беспокоиться о наличии конструкторов без аргументов, которые не приводят к пригодным для использования экземплярам.

Дизайн Foo звучит хорошо - я бы вообще предположил, что мне не разрешенопередать NULL в конструктор, если документация специально не позволяет мне.Особенно с неизменным классом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...