【问题标题】:Variation of Java observer pattern or Publisher-Subscriber PatternJava 观察者模式或发布者-订阅者模式的变体
【发布时间】:2019-11-09 23:50:08
【问题描述】:

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

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

因为,在 web 中有一些实现,它们都将它们作为 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);
     }

【问题讨论】:

  • 就我个人而言,我会通过 (1) 做到这两点:publisher.addObserver(this);。拥有(1)原样我会说是不正确的,因为它基于公开发布者的内部数据结构的功能。例如,如果 Publisher 决定更改为另一个数据结构,或者想要做一些事情,比如记录新的观察者?
  • 是的。我明白你的意思了。谢谢。

标签: java design-patterns publish-subscribe observer-pattern


【解决方案1】:

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

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

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

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

这就是“现实生活”中实际实现的样子

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

类会这样交互

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.
}

完全不同类型的多个对象(SettingsHandler 和ContactsHandler)但共同的基本/接口类型已订阅事件源的相同事件。一切都很干净。使用事件源对象的对象实际订阅/监听。可以被观察的类,observable,只是通知所有的监听者。没有其他交互。

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

解决方案 2 实际上遵循描述如何实现事件的模式。这个实现已经被证明并且被很好地确立,因为它实现了引入事件的目标并且产生了干净和可读的代码。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-26
          • 1970-01-01
          • 1970-01-01
          • 2012-07-23
          • 2016-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多