2011-01-20 4 views
2

나는 블로킹 큐에서 읽는 스레드를 제어하는 ​​것에 대해 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로두면 메서드가 다른 곳에서 사용 된 경우 메서드가 중단됩니다. FooMyThread과 함께 사용되지 않습니다. 비슷하게, 더미 생성자를 주 생성자에 넣는 것은 몇몇 필드와 생성자 매개 변수 자체가 중요한 객체이기 때문에이 문제를 그냥 넘겨 줄 것입니다.

단순히 프로그래밍을 과도하게 방어합니까? 객체를 사용할 수있게 만드는 설정자가없는 경우에도 클래스에 인수가없는 생성자를 추가하는 것에 대해 걱정해야합니까? (다른 프로그래머가 해당 생성자를 사용하지 않을 정도로 충분히 유용하다고 가정 할 때)? 인수가없는 생성자 또는 적어도 값이 아닌 값을 가질 수없는 경우 Foo의 디자인이 손상 되었습니까? 모든 방법에서 if(someField == null){throw new RuntimeException();} 검사를 사용하는 것이 더 낫습니까?

답변

1

빈 생성자를 사용하지 않는 것이 맞다고 생각합니다. Foo가 그러한 복잡한 클래스 인 경우 완전한 객체를 사용하는 것이 논리적 인 것처럼 보이지 않습니다.

null을 추가 할 수 있습니다. 그게 좋은 방법 같아.

다른 방법으로 인터페이스를 구현할 수도 있습니다. IBlockableQueueObject? 이것은 foo 객체와 STOP 부호로 구현 될 수 있습니다. 인터페이스가 STOP이 아닌 경우 Foo으로 다시 전송해야한다는 것입니다.

0

은 사용 가능한 인스턴스가 발생하지 않는 인수가없는 생성자가 있어야합니다.

Foo의 디자인은 괜찮습니다. 일반적으로 문서에서 특별히 허용하지 않는 한 생성자에 null을 전달할 수 없다고 가정합니다. 특히 불변 클래스가있는 경우.

2

이 디자인의 장점과 루프가 멈추어야한다는 것을 나타내는 간단한 부울 변수가 실제로 무엇인지 모르겠습니다.

하지만 정말로이 디자인으로 가고 싶다면 private no-arg 생성자를 만들고 정적 정지 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

또 다른 옵션은 이와 같은 일반적인 래퍼에 푸를 포장하는 것입니다 : 당신이 다음 BlockingQueue<Wrapped<Foo>>에 독약으로 null 값을 전달하는 데 사용할 수있는

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

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

    public T get() { return value; } 
} 

.

관련 문제