原文: https://javatutorial.net/java-observer-design-pattern-example

观察者模式是一种软件设计模式,其中一个称为主题的对象维护着一个依赖于它的所有其他对象(主题)的列表。 这些依赖者称为观察者。 如果主题(所有受抚养者(观察者)的维护者)的状态发生变化,则会自动通知他们。 它通常通过方法调用来完成。

何时使用观察者模式

  • 通常在对象之间存在一对多关系时使用。
  • 如果一个对象(另一个相关)修改/更改了其“行为”,则需要更新该对象。
  • 当主题的观察者数量未知时。

怎么运行的

通常,会有一个Subject.java文件,其中包含执行所有观察者的添加,删除和更新的所有方法,或者仅初始化它们,并在不同的.java文件中执行所有方法的功能。 按照约定,还有一个Observer.java文件,该文件包含一个每次主题更改时都会调用的update()方法。 之后,您必须创建实现Subject类或Observer类的.java文件。 请注意,这取决于您的类是应实现Subject还是Observer类文件。

Java 观察者设计模式示例 - 图1

观察者设计模式

现实生活中的例子

想想您最喜欢的会员服务。 也许是体育馆,或者是 Netflix,或者完全不同。 在本示例中,我们将使用 Netflix,但基本上任何其他成员资格服务都可以使用。 如果 Netflix 更改了其每月会员资格的价格,而您要为此付费,则它应通知您。 实际上,它应该通知所有注册/订阅人员服务。

Java 观察者设计模式示例

Subject.java

  1. public interface Subject {
  2. public double getCurrentPrice();
  3. public void setNewPrice(double newPrice);
  4. public void addNewSubscriber(Observer newSuObserver);
  5. public void removeSubscriber(Observer unsubscriber);
  6. public void notifyAllSubscribers();
  7. }

Subject.java中,我们将初始化将在SubscriptionGrabber.java文件中使用的方法,我将在稍后向您介绍。 这些方法处理添加,删除以及主题状态更改时触发的消息。

Observer.java

  1. public interface Observer {
  2. public void update(double price);
  3. }

这是我们的Observer类。 它具有将在类SubscriptionObserver.java文件中创建的update(double price)方法。 请注意:每次订阅价格更改时,都会调用此方法。

SubscriptionGrabber.java

  1. import java.util.*;
  2. public class SubscriptionGrabber implements Subject {
  3. private List<Observer> subscribers = new ArrayList<Observer>();
  4. private double price;
  5. public SubscriptionGrabber() {
  6. subscribers = new ArrayList<Observer>();
  7. }
  8. public double getCurrentPrice() {
  9. return price;
  10. }
  11. public void setNewPrice(double newPrice) {
  12. this.price = newPrice;
  13. notifyAllSubscribers();
  14. }
  15. public void addNewSubscriber(Observer newSubscriber) {
  16. subscribers.add(newSubscriber);
  17. }
  18. public void removeSubscriber(Observer unsubscriber) {
  19. int indexOfUnsubscriber = subscribers.indexOf(unsubscriber);
  20. subscribers.remove(indexOfUnsubscriber);
  21. }
  22. public void notifyAllSubscribers() {
  23. for (Observer subscriber : subscribers){
  24. subscriber.update(price);
  25. }
  26. }
  27. }

.java文件使用Subject接口。 请注意,在setNewPrice(double newPrice)方法中,我们在设置新价格后会通知用户。 重要的是,我们必须在价格变化后执行此操作,否则以后,如果其中有更多代码块,则可能会出现问题。 我们的addSubscriber(Observer newSubscriber)removeSubscriber(Observer subscriber)只是在观察者列表中添加人或从观察者列表中删除人。 我们需要在我们的模式中具有删除功能,因为我们的目的是仅通知订阅者,或者仅通知使用我们服务的人员。 我们无需通知不再使用我们服务的人员。 最后,此.java文件中的final方法仅对每个订户调用update(double price)方法(我们仍未创建)。

SubscriptionObserver.java

  1. public class SubscriptionObserver implements Observer {
  2. private double price;
  3. private Subject subscriberGrabber;
  4. private int subscriptionID;
  5. public SubscriptionObserver(Subject subscriptionGrabber) {
  6. this.subscriberGrabber = subscriptionGrabber;
  7. this.subscriptionID+=1;
  8. subscriberGrabber.addNewSubscriber(this);
  9. System.out.println("New Observer: " + this.subscriptionID);
  10. }
  11. public void update(double price) {
  12. System.out.println("Price changed from $" + this.price + " to $" + price);
  13. this.price = price;
  14. }
  15. }

这是实际上(最终)具有更新功能的.java文件。 请注意我是如何在其中放入打印语句的。 这非常重要(在update方法中包含打印语句),因为您需要直观地显示用户所发生的事情。 在构造函数中,该参数始终是一个空列表(您将在下一个.java文件中看到),并且subscriptionGrabber将是我们的新列表,其中将包含订阅者。 在第 9 行,您可以看到我们正在添加此新订户。

ObserverPattern.java

  1. public class ObserverPattern {
  2. public static void main(String[] args) {
  3. SubscriptionGrabber subscriptionGrabber = new SubscriptionGrabber();
  4. SubscriptionObserver subscriptionObserver = new SubscriptionObserver(subscriptionGrabber);
  5. SubscriptionObserver subscriptionObserver2 = new SubscriptionObserver(subscriptionGrabber);
  6. SubscriptionObserver subscriptionObserver3 = new SubscriptionObserver(subscriptionGrabber);
  7. subscriptionGrabber.setNewPrice(5);
  8. }
  9. }

在这里我们创建抓取程序(列表),然后将该列表作为参数传递给订阅观察者变量作为构造函数参数。 然后,我们将订阅的新价格设置为 5 美元。 预期结果将是通知所有用户价格确实已更改为 5 美元。 试试吧!

Java 观察者设计模式示例 - 图2

Java 观察者模式结果 1

它按预期工作。 现在,我们尝试再创建 2 个订阅,并查看消息是否会弹出 3 次(因为我们将有 3 个订阅)。

Java 观察者设计模式示例 - 图3

Java 观察者模式结果 2

它确实打印了三遍。