Java 观察者模式或发布者-订阅者模式的变体

Variation of Java observer pattern or Publisher-Subscriber Pattern

以下 2 个变体的实际观察者模式是什么。

  1. 在第一个中,观察者负责 订阅。
  2. 但在第二种情况下,Publisher 将 将特定观察者添加到订阅者列表的责任。

因为,网络中有一些实现,将它们都作为 java 中的观察者模式。

    1.
    // Observer is taking the responsibility of subscribing 
    // Observer.java
    @Override
    public void subscribe(MessagePublisher publisher) {
        publisher.getObservers().add(this);
    }

2.

    // Publisher is taking the observer to subscribe
    // Publisher.java
     @Override
     public void addObserver(Observer observer) {
       observers.add(observer);
     }

我怀疑他们都是。他们都必须实现 Observer 接口。

第二个例子是实现所谓的观察者模式。 (观察者)想要得到关于观察对象 X(Observable/event 源)中发生的某些事件的通知,对吧?然后你会告诉 Observable 通知你。或者换句话说,你开始监听观察者的通知——这就是为什么观察者也被称为监听者。
Observable 并不知道它所有的订阅者。它仅通过 IObserver 接口了解监听器,该接口定义了实际的通知回调。 Observable 只是将观察者添加到容器或集合中。

第一个例子没有意义。 observable 调用观察者的方法让他订阅?那个设计流程一定很奇怪。

假设您有一个 FileWriter class 公开事件 FileWriter.Completed。您有另一个 class FileHandler 来管理所有文件。 FileHandler 调用 FileWriter.write(filePath, data).

每次写入操作后,FileHandler 必须向用户显示一条消息。所以 FileWriterFileHandler 使用,但不知道谁在调用 write 方法。 FileWriter.write(string, string) 的众多消费者之一 FileHandler 知道 FileWriter implements an observable interface called IWriteToFileCompletedEventand defines the methodFileWriter.subscribeToWriteToFileCompleted(IObserver eventListener)`。

这就是 'real life'

中的实际实现方式
// The interface for the publisher or observable of a special event
interface IWriteToFileCompletedEvent {
  void subscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer);
  void unsubscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer);
}

// The interface for the observer of a special event
interface IWriteToFileCompletedListener {
  void notify(string filePathOfCompletedFile);
}

// The observable that exposes a special event
class FileWriter implements IWriteToFileCompletedEvent {
  private List<IWriteToFileCompletedListener> writeToFileCompletedListeners = new ArrayList<>();

  public void subscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer) {
    this.writeToFileCompletedListeners.add(observer);
  }

  public void unsubscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer) {
    this.writeToFileCompletedListeners.remove(observer);
  }

  public void writeToFile(string filePath, string data) {
    // Write data to file
    // Once done notify all listeners of the write completed event
    for (IWriteToFileCompletedListener observer : this.writeToFileCompletedListeners) {
      observer.notify(filePath);
    }
  } 
}

// The observer of a special event
class ContactsHandler implements IWriteToFileCompletedListener {
  private FileWriter fileWriter = new FileWriter();

  public void saveUserContactToFile(string filePath, string userInput) {
    this.fileWriter.subscribeToWriteToFileCompleted(this);
    this.fileWriter.writeToFile(filePath, userInput);
  }

  // Implementation of interface IWriteToFileCompletedListener 
  public void notify(string filePath) {
    this.fileWriter.unsubscribeToWriteToFileCompleted(this);
    this.messageDialog.show("The new contact was successfully saved to " + filePath);
  }
}

// Another observer of a special event
class SettingsHandler implements IWriteToFileCompletedListener {
  private FileWriter fileWriter = new FileWriter();

  public void saveUserSettingsToFile(string filePath, string userSettings) {
    this.fileWriter.subscribeToWriteToFileCompleted(this);
    this.fileWriter.writeToFile(filePath, userSettings);
  }

  // Implementation of interface IWriteToFileCompletedListener 
  public void notify(string filePath) {
    this.fileWriter.unsubscribeToWriteToFileCompleted(this);
    this.messageDialog.show("The new settings were successfully saved to " + filePath);
  }
}

classes 会这样互动

public void main(strng[] args {
  SettingsHandler settingsHandler = new SettingsHandler();
  ContactsHandler contactsHandler = new ContactsHandler();

  // Imaging this method receives user input fromo the UI:
  string newContact = textBox.gettext();
  this.contactsHandler.saveUserContactToFile("C:\Contacts.txt", newContact);

  // While waiting for the message to show the user adjusted some settings and clicked 'save'
  string changedSettings = getChangedSettings();
  this.settingsHandler.saveUserSettingsToFile("C:\UserSettings.txt", changedSettings);

  // After a while the user sees the messages send from the event listeners.
}

多个完全不同类型(SettingsHandlerContactsHandler)但共同base/interface类型的对象订阅了事件源的同一个事件。一切都很干净。实际使用事件源对象的对象subscribes/listens。可观察的class,observable,只是通知所有的监听者。没有其他互动。

总结一下:您的解决方案 1 一点也不方便(尽管您可以让它工作)并且会产生难看的代码。

解决方案 2 实际上遵循描述如何实现事件的模式。此实现已被证明并得到很好的确立,因为它实现了引入事件的目标并生成了清晰易读的代码。

观察者是应该自己注册还是让另一个实体来处理这个责任是应用观察者模式时的一个实现决定,而不是模式本身的结构方面。因此,遇到这两种变体也就不足为奇了。

在 GoF 书籍和其他地方记录的模式结构中,Subject 有一个 Attach(和 Detach)方法作为其接口的一部分。这意味着任何引用主题的实体都可以调用 Attach 并执行此注册。

两者都是观察者模式的有效实现,但是除非您明确需要公开观察者列表,否则第二种方法更可取。