java - 使用泛型无需强制转换观察者模式

标签 java generics design-patterns observer-pattern unchecked-cast

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

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() 中自己进行转换。您对如何进行此操作有什么建议吗?

<小时/>

编辑

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

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

最佳答案

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

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

<小时/>

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

此时,this 当然不能保证是 T 类型 - 它是Subject 类型。但“this”在具体的 Subject 类中将是 T 类型。因此,用 getThisSubject() 替换 this 并将其在层次结构中向下移动。在代码中:

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

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

关于java - 使用泛型无需强制转换观察者模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50782041/

相关文章:

java - DBUnit PostgresqlDataTypeFactory 不识别枚举列表

c++ - 有没有办法从模板参数和字符串中生成模板类中的类型

menu - "Context Sensitive"右键菜单的设计模式

c++ - 如何降低库中的复杂性而不增加其他地方的复杂性?

java - 使用 appium 将元素滚动到 View 中不适用于 iOS

java - 无法在 Windows 10 上执行 MySQL 转储,IDE 为 Netbeans 8.2

swift - 类型约束的可选扩展对其他类型可见

swift - 在 Swift 泛型类型中包含继承约束

c# - .NET 库的异常处理模式 - 处理、无人值守还是重新抛出?

java - Eclipse - 开放声明/实现/返回类型的更改顺序