没有使用泛型的观察者模式

No cast for observer pattern using generics

我一直在寻求实现一种主题观察者模式,其中主题在通知时向观察者提供自己。

public class Subject<T extends Subject> {

    /** suporting stuff for subject */

    private List<Observer<T>> observers = new ArrayList<>();

    protected void doNotify() {
        for(Observer<T> observer : observers) {
            /** This is the line where it gets interesting */
            observer.update((T)this);
        }
    }
}

实际上,这项工作是可行的,但是,编译器在 observer.update((T)this); 行给出了 Unchecked cast 警告。

当读到一点关于这个的时候,编译器是正确的(惊喜)它甚至被认为是臭代码,因为你可以编写实际触发 ClassCastException 的代码。

现在我正在寻找一种既不臭又坚如磐石的解决方案。然而,观察者不需要寻找它正在观察的对象的想法是我非常喜欢的。另外,我真的不喜欢观察员需要自己在 update() 中进行演员表。您对此有何建议?


编辑

我的观察者被声明为这样的接口:

public interface Observer<T> {
    void update(T subject);
}

前言:我建议不要使用泛型——因为通过强制客户(观察者)了解主题的确切类型,(而不是拥有非泛型主题 class),你不能,例如。将同一个观察者订阅到多个主题(不同类型)。

无论如何,有一种方法可以保证类型安全。


在调用 observer.update((T)this) 中你想要两件事:你想将 this 传递给观察者;并且您还希望 this 的类型为 T.

在这一点上,this 当然不能保证是 T 类型——它是 Subject 类型。但是 "this" 在具体的 Subject class 中将是类型 T。因此,将 this 替换为 getThisSubject() 并将其在层次结构中向下移动。在代码中:

package stackOv;
import java.util.*;

abstract class Subject<T extends Subject<T>> {
  private List<Observer<T>> observers = new ArrayList<>();
  // returns a reference of this, of type T
  protected abstract T getThisSubject();
  protected void doNotify() {
    for(Observer<T> observer : observers) {
      observer.update(getThisSubject());
    }
  }
  public void addObserver(Observer<T> obs) {
    observers.add(obs);
  }
}

class SubjectA extends Subject<SubjectA> { 
  @Override
  protected SubjectA getThisSubject() {
    return this;
  }
}

interface Observer<T> {
  void update(T subject);
}

class UseSubject {
  public static void main(String[] args) {
    SubjectA sub = new SubjectA();

    Observer<SubjectA> obs = new Observer<SubjectA>() {
      @Override
      public void update(SubjectA subject) {
        //and here we have a reference to the right type
        System.out.println("subj=" + subject);
      }
    };

    sub.addObserver(obs);
    sub.doNotify();
  }
}

让我强调一下,我们正在将 Observer 类型与 Subject 类型耦合;他们真的是一对一的关系。避免这种情况的一种方法是声明 Subject 非泛型,并且

interface Observer {
  void update(Subject subject);
}

加上使用访问者或其他模式。 (但这个回复已经够长了)。