设计模式 - 观察者模式

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

意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知并被自动更新。异步

通常实现

观察者模式使用四个类 Subject(通用抽象基类,不包含状态)、ConcreteSubject(带特定状态的Subject)、Observer (通用接口,不包含状态)和 ConcreteObserver (带特定的Observer )。ide

Subject基类:主要做用是抽象了被观察目标的通用操做,好比添加观察者、删除观察者、通知全部的观察者。测试

package com.dotleo.observer;

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

/**
 * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口
 *
 * @author LiuFei
 * @create 2017-12-03 19:53
 */
public class Subject {

    // 用来保存注册的观察者对象
    private List<Observer> observers = new ArrayList<Observer>();

    // attach detach notifyObservers
    // 绑定观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }

    // 删除观察者
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    // 通知全部观察者
    protected void notifyObServers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
}

有了Subject(目标)后,须要特殊定制一个含有状态的目标供观察者“观察”this

ConcreteSubject:持有特定的状态subjectState,并继承Subject基类,拥有绑定观察者、删除观察者、通知全部观察者的能力。code

package com.dotleo.observer;

/**
 * 具体的目标对象,负责把有关状态存入到相应的观察者对象中
 *
 * @author LiuFei
 * @create 2017-12-03 19:55
 */
public class ConcreteSubject extends Subject {

    // 目标对象的状态
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
        this.notifyObServers();
    }
}

观察者接口:提供了经过改变的目标去更新本身的状态server

package com.dotleo.observer;

/**
 * 这是一个观察者接口,定义一个更新的接口给那些在目标发生变化的时候被通知的对象
 *
 * @author LiuFei
 * @create 2017-12-03 19:58
 */
public interface Observer {
    
    void update(Subject subject);
}

观察者实例:实现了接口,拥有特定的状态,实现能够和目标同步的方法对象

package com.dotleo.observer;

/**
 * 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
 *
 * @author LiuFei
 * @create 2017-12-03 19:59
 */
public class ConcreteObserver implements Observer {

    // 观察者的状态
    private String observerState;

    /**
     * 获取目标类的状态同步到观察类的状态中
     * @param subject
     */
    @Override
    public void update(Subject subject) {
        observerState = ((ConcreteSubject) subject).getSubjectState();
    }
}

举个栗子

黄明在气象站工做,他能第一时间获得天气预报,为了给女友和老妈贴心的提示,他打算写一个程序(他会写代码),在天天本身改变天气状态后,他的女友和老妈能收到提示:继承

  • 女友收到:明每天气晴朗,蓝天白云,气温28度,是咱们的第一次约会,地点街心公园,不见不散!
  • 老妈收到:明每天气晴朗,蓝天白云,气温28度,是购物的好日子,明天去商城扫物!

因而他想到了观察者模式。接口

Subject基类:WeatherSubject

package com.dotleo.observer.weather;

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

/**
 * 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者的接口
 *
 * @author LiuFei
 * @create 2017-12-03 19:53
 */
public class WeatherSubject {

    // 用来保存注册的观察者对象
    private List<Observer> observers = new ArrayList<Observer>();

    // attach detach notifyObservers

    /**
     * 添加订阅天气的人
     * @param observer
     */
    public void attach(Observer observer) {
        observers.add(observer);
    }

    /**
     * 删除订阅天气的人
     * @param observer
     */
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    /**
     * 通知订阅天气的人
     */
    protected void notifyObServers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
}

带有特定状态(天气)的Subject:ConcreteWeatherSubject

package com.dotleo.observer.weather;

/**
 * 具体的目标对象,负责把有关状态存入到相应的观察者对象中
 *
 * @author LiuFei
 * @create 2017-12-03 19:55
 */
public class ConcreteWeatherSubject extends WeatherSubject {

    // 目标对象的状态
    private String weatherContent;

    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
        this.notifyObServers();
    }


}

Observer接口:Observer

package com.dotleo.observer.weather;

/**
 * 这是一个观察者接口,定义一个更新的接口给那些在目标发生变化的时候被通知的对象
 *
 * @author LiuFei
 * @create 2017-12-03 19:58
 */
public interface Observer {


    void update(WeatherSubject subject);
}

带有特定状态(天气),信息(观察者姓名,提醒内容):ConcreteObserver

package com.dotleo.observer.weather;

/**
 * 具体的观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
 *
 * @author LiuFei
 * @create 2017-12-03 19:59
 */
public class ConcreteObserver implements Observer {

    // 观察者姓名,谁收到这个消息,黄明的女友或者老妈
    private String observerName;
    // 天气状态,从目标处获取
    private String weatherContext;
    // 提醒内容,黄明的女友提醒约会,而他老妈则提醒购物
    private String remindThing;

    /**
     * 获取目标类的状态同步到观察类的状态中
     * @param subject
     */
    @Override
    public void update(WeatherSubject subject) {
        weatherContext = ((ConcreteWeatherSubject) subject).getWeatherContent();
        System.out.println(observerName + "收到了" + weatherContext + "," + remindThing);
    }

    public String getObserverName() {
        return observerName;
    }

    public void setObserverName(String observerName) {
        this.observerName = observerName;
    }

    public String getWeatherContext() {
        return weatherContext;
    }

    public void setWeatherContext(String weatherContext) {
        this.weatherContext = weatherContext;
    }

    public String getRemindThing() {
        return remindThing;
    }

    public void setRemindThing(String remindThing) {
        this.remindThing = remindThing;
    }
}

测试类:ObserverTest

package com.dotleo.observer.weather;

/**
 * @author LiuFei
 * @create 2017-12-03 20:33
 */
public class ObserverTest {

    public static void main(String[] args) {
        // 1. 建立一个目标
        ConcreteWeatherSubject weatherSubject = new ConcreteWeatherSubject();
        // 2. 建立观察者
        ConcreteObserver concreteObserver1 = new ConcreteObserver();
        concreteObserver1.setObserverName("黄明的女友");
        concreteObserver1.setRemindThing("是咱们的第一次约会,地点街心公园,不见不散!");

        ConcreteObserver concreteObserver2 = new ConcreteObserver();
        concreteObserver2.setObserverName("黄明的老妈");
        concreteObserver2.setRemindThing("是购物的好日子,明天去商城扫物!");
        // 3. 注册观察者
        weatherSubject.attach(concreteObserver1);
        weatherSubject.attach(concreteObserver2);
        // 4. 目标发布天气
        weatherSubject.setWeatherContent("明每天气晴朗,蓝天白云,气温28度");
    }
}

运行结果

输入图片说明

优缺点

优势:

  1. 观察者和被观察者是抽象耦合的。
  2. 创建一套触发机制。

缺点:

  1. 若是一个被观察者对象有不少的直接和间接的观察者的话,将全部的观察者都通知到会花费不少时间。
  2. 若是在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能致使系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

应用场景

  1. 对一个对象状态的更新,须要其余对象同步更新,并且其余对象的数量动态可变。
  2. 对象仅须要将本身的更新通知给其余对象而不须要知道其余对象的细节。

注意事项

  1. JAVA 中已经有了对观察者模式的支持类。
  2. 避免循环引用。
  3. 若是顺序执行,某一观察者错误会致使系统卡壳,通常采用异步方式。

java内置的观察者模式

在java.util包中包含有基本的Observer接口和Observable抽象类.功能上和Subject接口和Observer接口相似.不过在使用上,就方便多了,由于许多功能好比说注册,删除,通知观察者的那些功能已经内置好了。

Observer -- > Observer接口
Observable --> Subject基类

**ConcreteWeatherSubject **

package com.dotleo.observer.util;

import java.util.Observable;

public class ConcreteWeatherSubject extends Observable {

    private String weatherContent;

    public String getWeatherContent() {
        return weatherContent;
    }

    public void setWeatherContent(String weatherContent) {
        this.weatherContent = weatherContent;
        this.setChanged();
        this.notifyObservers();
    }
}

**ConcreteObserver **

package com.dotleo.observer.util;

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

/**
 * @author LiuFei
 * @create 2017-12-04 22:37
 */
public class ConcreteObserver implements Observer {

    // 观察者姓名,谁收到这个消息,黄明的女友或者老妈
    private String observerName;
    // 天气状态,从目标处获取
    private String weatherContext;
    // 提醒内容,黄明的女友提醒约会,而他老妈则提醒购物
    private String remindThing;

    public ConcreteObserver(String observerName, String weatherContext, String remindThing) {
        this.observerName = observerName;
        this.weatherContext = weatherContext;
        this.remindThing = remindThing;
    }

    @Override
    public void update(Observable o, Object arg) {
            weatherContext  = ((ConcreteWeatherSubject ) o).getWeatherContent();
            System.out.println(observerName + "收到了" + weatherContext + "," + remindThing);
    }
}

**ObserverTest **

package com.dotleo.observer.util;

import com.dotleo.observer.weather.ConcreteObserver;

/**
 * @author LiuFei
 * @create 2017-12-04 22:43
 */
public class ObserverTest {

    public static void main(String[] args) {

        // 1. 建立一个目标
        com.dotleo.observer.weather.ConcreteWeatherSubject weatherSubject = new com.dotleo.observer.weather.ConcreteWeatherSubject();
        // 2. 建立观察者
        com.dotleo.observer.weather.ConcreteObserver concreteObserver1 = new com.dotleo.observer.weather.ConcreteObserver();
        concreteObserver1.setObserverName("黄明的女友");
        concreteObserver1.setRemindThing("是咱们的第一次约会,地点街心公园,不见不散!");

        com.dotleo.observer.weather.ConcreteObserver concreteObserver2 = new ConcreteObserver();
        concreteObserver2.setObserverName("黄明的老妈");
        concreteObserver2.setRemindThing("是购物的好日子,明天去商城扫物!");
        // 3. 注册观察者
        weatherSubject.attach(concreteObserver1);
        weatherSubject.attach(concreteObserver2);
        // 4. 目标发布天气
        weatherSubject.setWeatherContent("明每天气晴朗,蓝天白云,气温28度");
    }
}
相关文章
相关标签/搜索