【设计模式】之观察者模式

蔚落 2022-05-15 15:56 233阅读 0赞

观察者模式

什么是观察者模式

  • 观察者模式属于行为模式的一种,定义了对象的通用交流方式。
  • 观察者模式定义了一对多的关系,一个对象改变了状态,则其它所有依赖它的对象都会收到通知。
  • 观察者模式有时候在网络模型中也叫做发布-订阅模式。
  • 原来的对象叫做观察者,观察者们注册的对象叫做主体。当主体状态变更的时候,所有的观察者都会收到通知。

观察者模式的特点

  • 观察者们注册到主体对象中去。
  • 主体维护一个观察者的列表,并且在其状态发生变更的时候会广播通知所有的观察者。
  • 当明确不需要被通知的时候,观察者可以注销。

观察者模式的使用场景

  • 这种模式广泛运用于用户接口框架中。
  • 在很多MVC框架模型中经常使用。
  • 考虑一个excel文档中的表格的应用场景。一个表格中的图标是根据其数据构建出来的,如果数据有任何变更,表格都会自动重绘。

观察者模式类图

观察者模式类图

观察者模式示例

在这个示例中,描述了新闻发布者。在典型的流程中,新闻阅读者订阅新闻。
一单一个新的新闻被出版商发布了,所有的观察者都会收到通知。
在这里出版商的角色就是一个主体,订阅者就是观察者。
一个出版商可以有一个或者多个订阅者。

上面的类图中表现了2个观察者 Subscriber1Subscriber2均注册到了publisher。
一旦Publisher状态发生变更,两个订阅者都会收到通知。

Observer.java

  1. package org.byron4j.cookbook.designpattern.observer;
  2. /** * 观察者接口类 */
  3. public interface Observer {
  4. public void update(String editon);
  5. }

Subscriber1.java

  1. package org.byron4j.cookbook.designpattern.observer;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Builder;
  4. import lombok.Data;
  5. /** * 观察者1 */
  6. @AllArgsConstructor
  7. @Data
  8. @Builder
  9. public class Subscriber1 implements Observer {
  10. @Override
  11. public void update(String editon) {
  12. System.out.println("Subscriber1收到新的版本通知。" + editon);
  13. }
  14. }

Subscriber2.java

  1. package org.byron4j.cookbook.designpattern.observer;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Builder;
  4. import lombok.Data;
  5. /** * 观察者2 */
  6. @AllArgsConstructor
  7. @Data
  8. @Builder
  9. public class Subscriber2 implements Observer {
  10. @Override
  11. public void update(String editon) {
  12. System.out.println("Subscriber2收到新的版本通知。" + editon);
  13. }
  14. }

Subject.java

  1. package org.byron4j.cookbook.designpattern.observer;
  2. /** * 主体 */
  3. public interface Subject {
  4. /** * 注册主体 * @param observer */
  5. public void registerObserver(Observer observer);
  6. /** * 移除订阅 * @param observer */
  7. public void removeObserver(Observer observer);
  8. /** * 移除所有的观察者 */
  9. public void notifyObservers();
  10. /** * 初始化主体相关信息 */
  11. public void initObservers();
  12. }

Publisher.java

  1. package org.byron4j.cookbook.designpattern.observer;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Builder;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. /** * 出版商--发布者 */
  9. @AllArgsConstructor
  10. @NoArgsConstructor
  11. @Data
  12. @Builder
  13. public class Publisher implements Subject{
  14. /** * 已注册的观察者列表 */
  15. private List<Observer> _observers = new ArrayList<>();
  16. @Override
  17. public void registerObserver(Observer observer) {
  18. System.out.println("注册观察者:" + observer);
  19. _observers.add(observer);
  20. }
  21. @Override
  22. public void removeObserver(Observer observer) {
  23. System.out.println("注销观察者:" + observer);
  24. _observers.remove(observer);
  25. }
  26. @Override
  27. public void notifyObservers() {
  28. for (Observer observer : _observers) {
  29. observer.update("广播消息通知给:" + observer);
  30. }
  31. }
  32. @Override
  33. public void initObservers() {
  34. if( null == _observers ){
  35. _observers = new ArrayList<>();
  36. }
  37. }
  38. }

ObserverTest.java

  1. package org.byron4j.cookbook.designpattern;
  2. import org.byron4j.cookbook.designpattern.observer.*;
  3. import org.junit.Test;
  4. public class ObserverTest {
  5. @Test
  6. public void test(){
  7. // 主体
  8. Subject publisher = Publisher.builder().build();
  9. publisher.initObservers();
  10. // 观察者注册
  11. Observer subscriber1 = Subscriber1.builder().build();
  12. publisher.registerObserver(subscriber1);
  13. Observer subscriber2= Subscriber2.builder().build();
  14. publisher.registerObserver(subscriber2);
  15. // 通知当前已注册的观察者
  16. publisher.notifyObservers();
  17. // 移除观察者
  18. publisher.removeObserver(subscriber2);
  19. // 通知当前已注册的观察者
  20. publisher.notifyObservers();
  21. }
  22. }

测试用例输出结果

  1. 注册观察者:Subscriber1()
  2. 注册观察者:Subscriber2()
  3. Subscriber1收到新的版本通知。广播消息通知给:Subscriber1()
  4. Subscriber2收到新的版本通知。广播消息通知给:Subscriber2()
  5. 注销观察者:Subscriber2()
  6. Subscriber1收到新的版本通知。广播消息通知给:Subscriber1()

观察者模式的优点

  • 提供观察者和可观察者之间的松散耦合。主体仅仅需要知道观察者列表而不关心他们的具体实现。所有的观察者都是经主体通过单个消息广播广而告之的。
  • 在任何时候都可以添加或者移除观察者。当主体需要添加新的观察者时主体不需要做任何改动。

观察者模式的缺陷

  • 如果有时候出现问题的话,需要在观察者链进行debug是非常麻烦的。
  • 主体持有所有观察者的引用,如果不用的观察者没有及时从主体中注销,很可能会导致内存泄漏。这个问题通常称之为失效的监听器问题。

经验法则

  • 当不需要再监听主体时,需要明确地注销观察者。
  • 推荐主体使用弱引用维持观察者列表,以避免内存泄漏。

弱引用版本主体实现

  1. package org.byron4j.cookbook.designpattern.observer;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Builder;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. import java.lang.ref.WeakReference;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. /** * 出版商--发布者---弱引用观察者列表--防止内存泄漏 */
  10. @AllArgsConstructor
  11. @NoArgsConstructor
  12. @Data
  13. @Builder
  14. public class Publisher2WeakReference implements Subject{
  15. /** * 已注册的观察者列表 */
  16. private List<WeakReference<Observer>> _observers = new ArrayList<>();
  17. @Override
  18. public void registerObserver(Observer observer) {
  19. System.out.println("注册观察者:" + observer);
  20. _observers.add(new WeakReference<Observer>(observer));
  21. }
  22. @Override
  23. public void removeObserver(Observer observer) {
  24. System.out.println("注销观察者:" + observer);
  25. _observers.remove(observer);
  26. }
  27. @Override
  28. public void notifyObservers() {
  29. for (WeakReference<Observer> observer : _observers) {
  30. observer.get().update("广播消息通知给:" + observer.get());
  31. }
  32. }
  33. @Override
  34. public void initObservers() {
  35. if( null == _observers ){
  36. _observers = new ArrayList<>();
  37. }
  38. }
  39. }

观察者模式的另一个示例

  1. abstract class Observer {
  2. protected Subject subject;
  3. public abstract void update();
  4. }
  5. class Subject {
  6. private List<Observer> observers = new ArrayList<>();
  7. private int state;
  8. public void add(Observer o) {
  9. observers.add(o);
  10. }
  11. public int getState() {
  12. return state;
  13. }
  14. public void setState(int value) {
  15. this.state = value;
  16. execute();
  17. }
  18. private void execute() {
  19. for (Observer observer : observers) {
  20. observer.update();
  21. }
  22. }
  23. }
  24. class HexObserver extends Observer {
  25. public HexObserver(Subject subject) {
  26. this.subject = subject;
  27. this.subject.add(this);
  28. }
  29. public void update() {
  30. System.out.print(" " + Integer.toHexString(subject.getState()));
  31. }
  32. }
  33. class OctObserver extends Observer {
  34. public OctObserver(Subject subject) {
  35. this.subject = subject;
  36. this.subject.add( this );
  37. }
  38. public void update() {
  39. System.out.print(" " + Integer.toOctalString(subject.getState()));
  40. }
  41. }
  42. class BinObserver extends Observer {
  43. public BinObserver(Subject subject) {
  44. this.subject = subject;
  45. this.subject.add(this);
  46. }
  47. public void update() {
  48. System.out.print(" " + Integer.toBinaryString(subject.getState()));
  49. }
  50. }
  51. public class ObserverTest {
  52. public static void main( String[] args ) {
  53. Subject sub = new Subject();
  54. // Client configures the number and type of Observers
  55. new HexObserver(sub);
  56. new OctObserver(sub);
  57. new BinObserver(sub);
  58. Scanner scan = new Scanner(System.in);
  59. for (int i = 0; i < 5; i++) {
  60. System.out.print("\nEnter a number: ");
  61. sub.setState(scan.nextInt());
  62. }
  63. }
  64. }

发表评论

表情:
评论列表 (有 0 条评论,233人围观)

还没有评论,来说两句吧...

相关阅读

    相关 设计模式观察模式

    观察者模式 什么是观察者模式 观察者模式属于行为模式的一种,定义了对象的通用交流方式。 观察者模式定义了一对多的关系,一个对象改变了状态,则其它所有依赖

    相关 设计模式观察模式

    前言 使用场景: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟

    相关 设计模式观察模式

    当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。 即目标发生更

    相关 设计模式观察模式

    定义 当一个对象的状态发生变化时,能够自动通知其他关联对象,自动刷新对象状态。观察者模式提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步

    相关 设计模式观察模式

     今天放假,又有时间继续啃《java设计模式》这本书了。每次学会一种设计模式内心都会有一种小小的成就感,但是懂是懂了,不知道会不会用。主要是现在没有什么项目经验,设计模式学了也

    相关 设计模式观察模式

    [上一篇:设计模式之策略模式][Link 1] 故事要从气象站说起,气象站有个WeatherData对象,这个对象负责拿到所有的气象数据(温度、湿度、气压),而气象站同时也

    相关 设计模式观察模式

    1 观察者模式 当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都

    相关 设计模式——观察模式

    > 设计模式: > > 前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定。而是一套用来提高代码可复用性、可维护性、可读性、稳健性、以及安全性的解决方案