目录
前言
在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。
在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。
定义与特点
定义
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
优点
-
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
-
目标与观察者之间建立了一套触发机制。
缺点
1.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
结构与实现
结构
实现
package net.biancheng.c.observer;
import java.util.*;
public class ObserverPattern {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
//抽象目标
abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
//抽象观察者
interface Observer {
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
案例
天气预报案例。
1. 创建通知者接口Subject
//接口,让wetherData来实现
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
2. 创建观察者接口Observer
//观察者接口,由观察者来实现
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
3. 创建观察者接口具体实现类,并重写方法
public class QianduSite implements Observer {
//气压 温度 湿度
private float temperature;
private float pressure;
private float humidity;
//更新天气情况
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
public void display(){
System.out.println("*** 千度天气 ***");
System.out.println("*** 千度今日天气: "+ temperature+"℃"+" ***");
System.out.println("*** 千度今日气压: "+ pressure+"hPa"+" ***");
System.out.println("*** 千度今日湿度: "+ humidity+"%m "+" ***");
if (temperature <= 10){
System.out.println("天气转凉,请注意保暖哦!");
}
}
}
4. 创建通知者接口实现类,并重写方法。
public class WetherData implements Subject{
private float temperature;
private float pressure;
private float humidity;
private ArrayList<Observer> observers;
public WetherData() {
observers = new ArrayList<Observer>();
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public void dataChange(){
notifyObserver();
}
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
//注册一个观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
//移除一个观察者
@Override
public void removeObserver(Observer o) {
if (observers.contains(o)){
observers.remove(o);
}
}
//遍历所有观察者
@Override
public void notifyObserver() {
for (int i = 0; i <observers.size() ; i++) {
observers.get(i).update(getTemperature(),getPressure(),getHumidity());
}
}
}
5. 创建客户端进行测试。
public class Client {
public static void main(String[] args) {
WetherData wetherData = new WetherData();
//创建观察者
QianduSite qianduSite = new QianduSite();
//注册到wetherData
wetherData.registerObserver(qianduSite);
//测试
System.out.println("通知");
wetherData.setData(10,978,70);
}
}
打印输出。
如果想要增加一个观察者,只需操作第3步,并在客户端创建该观察者对象并注册即可。