一.模式定义
Observer Pattern: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。
二.模式要素
Subject: 目标
ConcreteSubject: 具体目标
Observer: 观察者
ConcreteObserver: 具体观察者
三.举例说明
举一个母鸡下蛋的例子吧,假设有一只母鸡,它下的蛋需要三个观察者来分,分配规则如下:
当鸡不下蛋时,三个观察者都分不到鸡蛋。
鸡下1个蛋则只有观察者A得到1个蛋。
鸡下2个蛋时,则观察者A观察者B各得到一个蛋。
鸡下3个蛋时,则三个观察者各得一个蛋。
鸡下4个蛋时,则观察者A得到2个蛋,其余两个观察者各得到一个蛋,以此类推。
上面的例子实际上是鸡下蛋后通知三个观察者,观察者根据下蛋的情况更新自己应该得到几个鸡蛋。
那怎样用代码实现上面的例子呢?
先看下结构图,其实很简单。
IHen为母鸡的接口;Hen为具体的母鸡;IObserver为观察者的抽象类;ObserverA、ObserverB、ObserverC分别为三个具体观察者。
四.模式实例
IHen.java
package observerPattern;
import java.util.List;
public interface IHen {
/**
* 母鸡下蛋
*
* @param x
* @return
*/
void layEggs(int x);
/**
* 通知观察者
*
* @param observers
* @param x
*/
void notify(List observers, int x);
}
Hen.java
package observerPattern;
import java.util.List;
public class Hen implements IHen {
List observers;
Hen(List observers) {
this.observers = observers;
}
@Override
public void layEggs(int x) {
System.out.println("母鸡下了" + x + "个蛋");
notify(observers, x);
}
@Override
public void notify(List observers, int x) {
for (IObserver observer : observers) {
observer.receiveNotify(x);
}
}
}
IObserver.java
package observerPattern;
abstract class IObserver {
int totalEggs = 0;
/**
* 接到通知
*
* @param x
*/
void receiveNotify(int x) {
System.out.println(this.getClass().getSimpleName() + "接到通知");
action(x);
}
/**
* 执行动作
*/
abstract void action(int x);
}
ObserverA.java
package observerPattern;
class ObserverA extends IObserver {
@Override
void action(int x) {
if (0 == x) {
totalEggs += 0;
}
int a = x / 3;
totalEggs += a;
int b = x % 3;
if (1 == b || 2 == b) {
totalEggs += 1;
}
System.out.println("我是" + ObserverA.class.getSimpleName() + ",我总共有" + totalEggs + "个鸡蛋");
}
}
ObserverB.java
package observerPattern;
class ObserverB extends IObserver {
/**
* 接到通知后的动作
*
* @param x
*/
@Override
void action(int x) {
if (0 == x) {
totalEggs += 0;
}
int a = x / 3;
totalEggs += a;
int b = x % 3;
if (2 == b) {
totalEggs += 1;
}
System.out.println("我是" + ObserverB.class.getSimpleName() + ",我总共有" + totalEggs + "个鸡蛋");
}
}
ObserverC.java
package observerPattern;
class ObserverC extends IObserver {
/**
* 接到通知后的动作
*
* @param x
*/
@Override
void action(int x) {
if (0 == x) {
totalEggs += 0;
}
int a = x / 3;
totalEggs += a;
System.out.println("我是" + ObserverC.class.getSimpleName() + ",我总共有" + totalEggs + "个鸡蛋");
}
}
Main.java
package observerPattern;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
IObserver oa = new ObserverA();
IObserver ob = new ObserverB();
IObserver oc = new ObserverC();
List observerList = new ArrayList<>();
observerList.add(oa);
observerList.add(ob);
observerList.add(oc);
IHen hen = new Hen(observerList);
hen.layEggs(7);
}
}
运行结果:
五.总结
1.观察者模式的优点
(1) 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
(2)观察者模式在观察目标和观察者之间建立一个抽象的耦合。
(3)观察者模式支持广播通信。
(4)观察者模式符合“开闭原则”的要求。
2.观察者模式的缺点
(1)如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
(2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
(3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
3.观察者模式的适用场景
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
(2)一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
(3)一个对象必须通知其他对象,而并不知道这些对象是谁。
(4)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。