当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
观察者模式也叫作发布-订阅模式,也就是事件监听机制。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。减少对象之间的耦合有利于系统的复用,但是同时需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。
观察者模式组成
抽象主题角色
抽象主题角色把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
具体主题角色
将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
抽象观察者角色
为所有的具体观察者提供一个接口,在得到主题通知时更新自己
具体观察者角色
存储与主题的状态相关的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态协调
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
实现
观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。
ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。
创建抽象类
package com.kgc.observerpattern;
public abstract class Observer {
protected Subject subject; //创建实体类属性,建立连接机制
public abstract void update(); //更新状态方法
}
创建 Subject 类
package com.kgc.observerpattern;
import java.util.ArrayList;
import java.util.List;
public class Subject {
//存放观察者的集合
private List<Observer> observers = new ArrayList<Observer>();
private int state; //状态
public int getState() {
return state;
}
//设置状态
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
//增加观察者
public void attch(Observer observer) {
observers.add(observer);
}
//通知所有观察者更新状态
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update(); //调用抽象类的更新方法
}
}
}
创建实体观察者类。
package com.kgc.observerpattern;
/**
* 二进制
* @author Lenovo
*
*/
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.attch(this);
}
@Override
public void update() {
System.out.println("Binary String:"
+ Integer.toBinaryString(subject.getState()));
}
}
package com.kgc.observerpattern;
/**
* 八进制
* @author Lenovo
*
*/
public class OctalObserver extends Observer {
public OctalObserver(Subject subject) {
this.subject = subject;
this.subject.attch(this);
}
@Override
public void update() {
System.out.println("Octal String:"
+ Integer.toOctalString(subject.getState()));
}
}
package com.kgc.observerpattern;
/**
* 十六进制
* @author Lenovo
*
*/
public class HexObserver extends Observer {
public HexObserver(Subject subject) {
this.subject = subject;
this.subject.attch(this);
}
@Override
public void update() {
System.out.println("Hex String:"
+ Integer.toHexString(subject.getState()));
}
}
使用 Subject 和实体观察者对象。
package com.kgc.observerpattern;
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new HexObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
执行结果:
观察者模式实例
抽象主题角色,有增加观察者、删除观察者、通知观察者的功能:
public abstract class Subject
{
/** 用来保存注册的观察者对象 */
private List<Observer> list = new ArrayList<Observer>();
/** 注册观察者对象 */
public void attch(Observer observer)
{
list.add(observer);
System.out.println("Attached an observer");
}
/** 删除观察者对象 */
public void detach(Observer observer)
{
list.remove(observer);
System.out.println("Detached an observer");
}
/** 通知所有注册的观察者对象 */
public void notifyObservers(String newState)
{
for (int i = 0; i < list.size(); i++)
{
list.get(i).update(newState);
}
}
}
- 具体主题角色
public class ConcreteSubject extends Subject
{
private String state;
public String getState()
{
return state;
}
public void change(String newState)
{
state = newState;
System.out.println("主题状态为:" + state);
// 状态发生改变时,通知各个观察者
this.notifyObservers(state);
}
}
- 观察者接口:
public interface Observer
{
void update(String state);
}
- 具体观察者实现了观察者接口:
public class ConcreteObserver implements Observer
{
/** 观察者的状态 */
private String observerState;
public void update(String state)
{
/** 更新观察者的状态 */
observerState = state;
System.out.println("状态为:" + observerState);
}
}
- 客户端调用代码
public static void main(String[] args)
{
/** 创建主题角色 */
ConcreteSubject subject = new ConcreteSubject();
/** 创建观察者对象 */
Observer observer = new ConcreteObserver();
/** 将观察者注册到主题对象上 */
subject.attch(observer);
/** 改变主题对象的状态 */
subject.change("new state");
}
观察者模式在Java中的应用
JDK是有直接支持观察者模式的,就是java.util.Observer这个接口:是观察者的接口,定义的观察者只需要实现这个接口就可以了。update()方法,被观察者对象的状态发生变化时,被观察者的notifyObservers()方法就会调用这个方法:
Observable是被观察者的父类,也就是主题对象。
方 法 | 作 用 |
addObserver(Observer o) | 如果观察者与集合中已有的观察者不同,则向对象的观察者集合中添加此观察者 |
clearChanged()、hasChanged()、setChanged() | 这三个方法算是一对,用来标记此观察者对象(主题对象)是否被改变的状态的 |
countObservers() | 返回观察者对象的数目 |
deleteObserver(Observer o) | 从对象的观察者集合中删除某个观察者 |
deleteObservers() | 清除观察者列表 |
notifyObservers()、notifyObservers(Object arg) | 如果本对象有变化则通知所有等级的观察者,调用update()方法 |
public class Watched extends Observable
{
private String data = "";
public String getData()
{
return data;
}
public void setData(String data)
{
if (!this.data.equals(data))
{
this.data = data;
setChanged();
}
notifyObservers();
}
}
创建一个主题:
public class Watcher implements Observer
{
String data;
public Watcher(Observable o)
{
o.addObserver(this);
}
public String getData()
{
return data;
}
public void update(Observable o, Object arg)
{
this.data = ((Watched)o).getData();
System.out.println("状态发生改变:" + ((Watched)o).getData());
}
}
写一个main函数调用一下:
public static void main(String[] args)
{
/** 创建被观察者对象 */
Watched watched = new Watched();
/** 创建观察者对象,并将被观察者对象登记 */
Watcher watcher = new Watcher(watched);
/** 给被观察者状态赋值 */
watched.setData("start");
watched.setData("run");
watched.setData("stop");
}
观察者模式的优点以及实际应用
- 去重复代码,使得代码更清晰、更易读、更易扩展
- 解耦,使得代码可维护性更好,修改代码的时候可以尽量少改地方
使用观察者模式可以很好地做到这两点。增加观察者,直接new出观察者并注册到主题对象之后就完事了,删除观察者,主题对象调用方法删除一下就好了,其余都不用管。
观察者模式主要应用场景有:
1、对一个对象状态的更新需要其他对象同步更新
2、对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节,如消息推送
观察者模式的其它综合学习参考:https://segmentfault.com/a/1190000012295887?utm_source=tag-newest