设计模式——(2)观察者模式

第二章 观察者模式

1.1 引言

在开发过程中,当遇到如下场景:

  • 类似微信公众号的消息通知模型,只要公众号更新了消息,那么所有订阅了该公众号的用户都会收到消息推送。
  • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。

就上述场景而言,一方更新消息,其余用户获取消息

由于在公众号消息通知模型中,订阅消息的用户可能增加、减少,因此,需要一种设计模式来解决此场景下的问题。

观察者模式就是应对此场景的一种设计模式。这里更新消息的一方称为主题(公众号),所有接收消息的用户称为观察者(订阅公众号的用户)

1.2 自己手动实现主题-订阅模型

设计方案如类图1所示,Subject 是自定义主题接口,其中包含注册观察者的方法 registerObserver();移除观察者的方法removeObserver();消息更新后,通知所有观察者的方法notifyAllObservers()。

WeChatPublicAccount 类是微信公众号,实现了Subject 接口。维护了一个 observersList ,用于管理所有的观察者,除此,还包括文章的标题、作者和内容等属性。

Observer 接口,为所有的观察者提供了统一的接口,并具有update()方法,用于更新信息。

User1类、User2类和User3类,是订阅了微信公众号的观察者。

设计模式——(2)观察者模式_第1张图片
类图1.自己实现微信公众号的主题-订阅模型

Subject 接口:

package headfirst.designpatterns.observer.simple;

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyAllObservers();
}

WeChatPublicAccount 类:

package headfirst.designpatterns.observer.simple;

import java.util.ArrayList;

public class WeChatPublicAccount implements Subject {

    private ArrayList<Observer> observersList;

    private String title;
    private String author;
    private String content;

    public WeChatPublicAccount() {
        observersList = new ArrayList<>();
    }

    public ArrayList<Observer> getObserversList() {
        return observersList;
    }

    @Override
    public void registerObserver(Observer observer) {
        if (observer != null) observersList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int index = -1;
        if (observer != null)
            index = observersList.indexOf(observer);
        if (index != -1)
            observersList.remove(index);
    }

    // 数据由主题由 “推” 的方式通知观察者
    @Override
    public void notifyAllObservers() {
        for (Observer o : observersList) {
            if (o != null)
                o.update(title, author, content);
        }
    }

    public void dataChanged() {
        notifyAllObservers();
    }

    public void setDataChanged(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
        dataChanged();
    }

}

Observer 接口:

package headfirst.designpatterns.observer.simple;

public interface Observer {
    void update(String title,String author,String content);
}

User1 类:

package headfirst.designpatterns.observer.simple;

public class User1 implements Observer {

    private String title;
    private String author;
    private String content;

    private Subject weChatPublicAccount;

    public User1(Subject weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.registerObserver(this);
    }

    @Override
    public void update(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
    }

    @Override
    public String toString() {
        return "User1{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

}

User2 类:

package headfirst.designpatterns.observer.simple;

public class User2 implements Observer {
    private String title;
    private String author;
    private String content;

    private Subject weChatPublicAccount;

    public User2(Subject weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.registerObserver(this);
    }

    @Override
    public void update(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
    }

    @Override
    public String toString() {
        return "User2{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

User3:

package headfirst.designpatterns.observer.simple;

public class User3 implements Observer {
    private String title;
    private String author;
    private String content;

    private Subject weChatPublicAccount;

    public User3(Subject weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.registerObserver(this);
    }

    @Override
    public void update(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
    }

    @Override
    public String toString() {
        return "User3{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

Test:

package headfirst.designpatterns.observer.simple;

public class Test {

    public static void main(String[] args) {

        WeChatPublicAccount weChatPublicAccount = new WeChatPublicAccount();

        Observer user1 = new User1(weChatPublicAccount);
        Observer user2 = new User2(weChatPublicAccount);
        Observer user3 = new User3(weChatPublicAccount);

        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user3.toString());
		
        // 更新数据
        weChatPublicAccount.setDataChanged("午觉睡多久比较好","丁香医生","午觉睡10-20mins是极好的,一觉醒来元气满满");

        System.out.println("丁香医生公众号数据更新后...");
        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user3.toString());

    }
}

输出: 在微信公众号更新数据后,所有的观察者都将收到公众号推送的数据。

User1{title='null', author='null', content='null'}
User2{title='null', author='null', content='null'}
User3{title='null', author='null', content='null'}
丁香医生公众号数据更新后...
User1{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}
User2{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}
User3{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}

1.3 通过Observable接口(推)实现微信公众号的主题-订阅模型

利用 JDK 自带的 Observable 类和 Observer 接口,设计的方案如类图2所示。Observable 类和 1.2节中的 Subject 接口功能类似,其维护了一个 obs 列表,用于管理所有观察者。

WeChatPublicAccount 类继承了 Observable 类,其中在公众号的数据更新后,可将所有数据封装至一个 HashMap dataMap中,然后以dataMap 参数,调用 notifyObservers(Object arg) 方法可逐一通知所有观察者,实现数据推送。
设计模式——(2)观察者模式_第2张图片

类图2.通过Observable接口(推)实现微信公众号的主题-订阅模型

WeChatPublicAccount 类:

package headfirst.designpatterns.observer.observablepush;

import java.util.HashMap;
import java.util.Observable;

public class WeChatPublicAccount extends Observable {

    private String title;
    private String author;
    private String content;
    private HashMap<String, String> dataMap;

    public WeChatPublicAccount() {
        dataMap = new HashMap<>();
    }

    // 将数据封装至 map,然后以 “推” 的方式将数据通知所有观察者。
    public void dataChanged() {
        // 当状态变化满足一定条件时,调用  setChanged() 标志状态已改变,可通知观察者。例如,温度变化1°,才允许标志状态已改变,可通知观察者。
        setChanged();
        dataMap.put("title", title);
        dataMap.put("author", author);
        dataMap.put("content", content);
        notifyObservers(dataMap);
    }

    public void setDataChanged(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
        dataChanged();
    }
}

User1类:

package headfirst.designpatterns.observer.observablepush;

import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;

public class User1 implements Observer {

    private String title;
    private String author;
    private String content;

    private Observable weChatPublicAccount;

    public User1(Observable weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.addObserver(this);
    }


    @Override
    public String toString() {
        return "User1{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public void update(Observable o, Object arg) {
        HashMap<String,String> dataMap = (HashMap<String, String>) arg;
        title = dataMap.get("title");
        author = dataMap.get("author");
        content = dataMap.get("content");
    }
}

User2 类:

package headfirst.designpatterns.observer.observablepush;

import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;

public class User2 implements Observer {

    private String title;
    private String author;
    private String content;

    private Observable weChatPublicAccount;

    public User2(Observable weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.addObserver(this);
    }

    @Override
    public String toString() {
        return "User2{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public void update(Observable o, Object arg) {
        HashMap<String,String> dataMap = (HashMap<String, String>) arg;
        title = dataMap.get("title");
        author = dataMap.get("author");
        content = dataMap.get("content");
    }
}

User3类:

package headfirst.designpatterns.observer.observablepush;

import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;

public class User3 implements Observer {

    private String title;
    private String author;
    private String content;

    private Observable weChatPublicAccount;

    public User3(Observable weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.addObserver(this);
    }


    @Override
    public String toString() {
        return "User3{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public void update(Observable o, Object arg) {
        HashMap<String,String> dataMap = (HashMap<String, String>) arg;
        title = dataMap.get("title");
        author = dataMap.get("author");
        content = dataMap.get("content");
    }
}

Test类:

package headfirst.designpatterns.observer.observablepush;

import java.util.Observer;

public class Test {

    public static void main(String[] args) {

        WeChatPublicAccount weChatPublicAccount = new WeChatPublicAccount();

        Observer user1 = new User1(weChatPublicAccount);
        Observer user2 = new User2(weChatPublicAccount);
        Observer user3 = new User3(weChatPublicAccount);

        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user3.toString());

        weChatPublicAccount.setDataChanged("午觉睡多久比较好","丁香医生","午觉睡10-20mins是极好的,一觉醒来元气满满");
        System.out.println("丁香医生公众号数据更新后...");
        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user3.toString());

    }
}

输出:

User1{title='null', author='null', content='null'}
User2{title='null', author='null', content='null'}
User3{title='null', author='null', content='null'}
丁香医生公众号数据更新后...
User1{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}
User2{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}
User3{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}

1.4 通过Observable接口(拉)实现微信公众号的主题-订阅模型

1.3 节中实现了主题-订阅模型,主题主动推送数据,本节主要介绍观察者主动拉取数据的模型。利用 JDK 自带的 Observable 类和 Observer 接口,设计的方案如类图3所示。

设计模式——(2)观察者模式_第3张图片
类图3.通过Observable接口(拉)实现微信公众号的主题-订阅模型

与 1.3 节中不同的是,当主题中的数据更新后,并不是主动的将数据推送至所有观察者,而是通知所有观察者,并提供了获取数据的公共方法,以供所有观察者自己获取所需的数据,getTitle()、getAuthor()和getContent(),除此,微信公众号调用的notifyObservers()方法,并不传递数据参数,只负责通知。

WeChatPublicAccount 类:

package headfirst.designpatterns.observer.observablepull;

import java.util.Observable;

public class WeChatPublicAccount extends Observable {

    private String title;
    private String author;
    private String content;

    public WeChatPublicAccount() {

    }

    // 通知所有观察者,可以通过 “拉” 的方式获取自己所需的数据。
    public void dataChanged() {
        setChanged();
        notifyObservers();
    }

    public void setDataChanged(String title, String author, String content) {
        this.title = title;
        this.author = author;
        this.content = content;
        dataChanged();
    }

    public String getTitle() {
        return title;
    }

    public String getAuthor() {
        return author;
    }

    public String getContent() {
        return content;
    }
}

User1类:

package headfirst.designpatterns.observer.observablepull;

import java.util.Observable;
import java.util.Observer;

public class User1 implements Observer {

    private String title;
    private String author;
    private String content;

    private Observable weChatPublicAccount;

    public User1(Observable weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.addObserver(this);
    }


    @Override
    public String toString() {
        return "User1{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public void update(Observable o, Object arg) {

        WeChatPublicAccount weChatPublicAccount = (WeChatPublicAccount) o;
        title = weChatPublicAccount.getTitle();
        author = weChatPublicAccount.getAuthor();
        content = weChatPublicAccount.getContent();
    }
}

User2 类:

package headfirst.designpatterns.observer.observablepull;

import java.util.Observable;
import java.util.Observer;

public class User2 implements Observer {

    private String title;
    private String author;
    private String content;

    private Observable weChatPublicAccount;

    public User2(Observable weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.addObserver(this);
    }


    @Override
    public String toString() {
        return "User2{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public void update(Observable o, Object arg) {

        WeChatPublicAccount weChatPublicAccount = (WeChatPublicAccount) o;
        title = weChatPublicAccount.getTitle();
        author = weChatPublicAccount.getAuthor();
        content = weChatPublicAccount.getContent();
    }
}

User3类:

package headfirst.designpatterns.observer.observablepull;

import java.util.Observable;
import java.util.Observer;

public class User3 implements Observer {

    private String title;
    private String author;
    private String content;

    private Observable weChatPublicAccount;

    public User3(Observable weChatPublicAccount) {
        this.weChatPublicAccount = weChatPublicAccount;
        weChatPublicAccount.addObserver(this);
    }


    @Override
    public String toString() {
        return "User3{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }

    @Override
    public void update(Observable o, Object arg) {

        WeChatPublicAccount weChatPublicAccount = (WeChatPublicAccount) o;
        title = weChatPublicAccount.getTitle();
        author = weChatPublicAccount.getAuthor();
        content = weChatPublicAccount.getContent();
    }
}

Test类:

package headfirst.designpatterns.observer.observablepull;

import java.util.Observer;

public class Test {

    public static void main(String[] args) {

        WeChatPublicAccount weChatPublicAccount = new WeChatPublicAccount();

        Observer user1 = new User1(weChatPublicAccount);
        Observer user2 = new User2(weChatPublicAccount);
        Observer user3 = new User3(weChatPublicAccount);

        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user3.toString());

        weChatPublicAccount.setDataChanged("午觉睡多久比较好","丁香医生","午觉睡10-20mins是极好的,一觉醒来元气满满");

        System.out.println("丁香医生公众号数据更新后...");
        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user3.toString());

    }
}

User1{title='null', author='null', content='null'}
User2{title='null', author='null', content='null'}
User3{title='null', author='null', content='null'}
丁香医生公众号数据更新后...
User1{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}
User2{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}
User3{title='午觉睡多久比较好', author='丁香医生', content='午觉睡10-20mins是极好的,一觉醒来元气满满'}

1.5 总结

观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新,一个主题,多个观察者

观察者提供了一种对象设计,让主题和观察者之间松耦合,主题只知道观察者实现了Observer 接口,而具体的观察者也仅依赖Subject 接口或Observable 超类实现注册。

要点:

  • 主题用一个共同的接口来更新观察者;
  • 主题与观察者间的松耦合设计,主题不知道观察者的细节,只知道观察者实现了观察者接口;
  • 可使用两种方式更新数据。

设计模式——(2)观察者模式_第4张图片
OO原则:

  • 为交互对象之间的松耦合设计而努力。

GitHub 代码地址:https://github.com/SJMP1573/DesignPatterns

参考

[1] Freeman E. Head First 设计模式[M] 中国电力出版社.
[2] 菜鸟教程.

你可能感兴趣的:(设计模式,java,设计模式,观察者模式)