【设计模式 - 19】之观察者模式(Observer)

1、模式简介

观察者模式的介绍:

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

  发布者(被观察者) + 订阅者(观察者) = 观察者模式

观察者模式的优点:

  • 观察者和被观察者是抽象耦合的;
  • 观察者模式建立了一套触发机制(触发联动)。

观察者模式的缺点:

  • 如果一个被观察者有很多直接或简介的观察者的话,将所有的观察者都通知到会花费很多时间;
  • 如果在观察者和被观察者之间有循环依赖的话,被观察者会触发它们之间的的循环调用,可能会导致系统崩溃(因此,在观察者模式中应该避免观察者和被观察者之间的循环调用)。

观察者模式的适用场景:

  • 当需要完成“触发联动”的功能时,即当一个对象的状态发生变化时,会触发与之相关联的多个对象状态的改变;
  • 当观察者对象不需要知道主题对象(被观察者)的实现细节时。

观察者模式的UML图:

 

2、案例

  在这个案例中,我们模拟报社的职能:

  • 报社的业务就是出版报纸;
  • 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,就会一直收到新报纸;
  • 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来;
  • 只要报社还在运营,就会一直有人向他们订阅或取消订阅报纸。

2.1、手写观察者模式

  手写的观察者模式的代码如下:

  被观察者的抽象父类Subject代码如下:

public abstract class Subject {
    protected String name;
    protected Object thing;

    abstract void addObserver(Observer observer);

    abstract void removeObserver(Observer observer);

    abstract void notifyObservers();

    public void setThing(Object thing) {
        this.thing = thing;
        System.out.println(this.name + "发布了一条消息:" + thing.toString());
        notifyObservers();
    }
}

  被观察者的具体类:报社类NewsOffice中的代码:

import java.util.ArrayList;
import java.util.List;

public class NewsOffice extends Subject {
    private List<Observer> observers;

    public NewsOffice() {
        super.name = "报社";
        this.observers = new ArrayList<Observer>();
    }

    @Override
    public void addObserver(Observer observer) {
        this.observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int index = this.observers.indexOf(observer);
        if (index >= 0) {
            this.observers.remove(observer);
        }
    }

    @Override
    void notifyObservers() {
        if (this.observers.size() > 0) {
            for (Observer observer : this.observers) {
                observer.update(super.thing);
                observer.display();
            }
        }
    }
}

  观察者的抽象父类Observer中的代码:

public abstract class Observer {
    protected String name;
    protected Object thing;

    public void update(Object thing) {
        this.thing = thing;
        System.out.println(this.name + "收到了消息:" + thing.toString());
    }

    public abstract void display();
}

  具体的观察者Person1中的代码:

public class Person1 extends Observer {

    public Person1() {
        super.name = "Person1";
    }

    @Override
    public void display() {
        System.out.println(super.name + "一边听歌一边看报纸");
    }
}

  具体的观察者还有Person2和Company1,这里以Person1为例,有需要的朋友可以通过博客最后的GitHub地址去GitHub上下载。

  测试类Test中的代码:

public class Test {
    public static void main(String[] args) {
        // 创建主题(被观察者)
        Subject office = new NewsOffice();
        // 创建观察者
        Observer person1 = new Person1();
        Observer person2 = new Person2();
        Observer company1 = new Compony1();
        // 为观察者注册主题
        office.addObserver(person1);
        office.addObserver(person2);
        office.addObserver(company1);

        // 主题发送消息
        office.setThing("第一条新闻");
        System.out.println();
        office.setThing("哈哈哈哈哈哈哈哈哈哈");
    }
}

  运行结果如下图所示:

2.2、JAVA内置观察者模式

  JAVA内置的观察者模式的代码如下:

  被观察者报社类NewsOffice(继承了JAVA内置的被观察者Observable类)的代码如下:

public class NewsOffice extends Observable {

    public void setNews(String news) {
        System.out.println("报社发布了新闻:" + news);
        super.setChanged();
        super.notifyObservers(news);
    }
}

  自定义的观察者CustomObserver(实现了JAVA内置的观察者接口Observer和自定义的展示接口IDisplay)的代码如下:

// 自定义的展示接口IDisplay中的代码:
public interface IDisplay {
    void display();
}


// 自定义的观察者CustomObserver中的代码:
public abstract class CustomObserver implements Observer, IDisplay {
    protected String name;

    @Override
    public void update(Observable subject, Object thing) {
        System.out.println(this.name + "收到了消息:" + thing.toString());
    }
}

  具体观察者Person1中的代码:

public class Person1 extends CustomObserver {

    public Person1() {
        super.name = "Person1";
    }

    @Override
    public void display() {
        System.out.println(super.name + "一边听歌一边看报纸");
    }
}

  Person2和Company1中的代码同样在GitHub中。

  测试类Test中的代码:

public class Test {
    public static void main(String[] args) {
        // 创建主题(被观察者)
        NewsOffice office = new NewsOffice();
        // 创建三个观察者
        CustomObserver person1 = new Person1();
        CustomObserver person2 = new Person2();
        CustomObserver company1 = new Company1();
        // 为观察者注册主题
        office.addObserver(person1);
        office.addObserver(person2);
        office.addObserver(company1);

        // 被观察者发布消息
        office.setNews("哈哈哈哈哈哈");
        System.out.println();
        office.setNews("这不是一条垃圾新闻");
    }
}

  运行结果如下图所示:



  最后贴出观察者模式的GitHub代码地址:【GitHub - Observer】

posted on 2016-12-22 14:43  ITGungnir  阅读(335)  评论(0编辑  收藏  举报

导航