这里介绍第三种设计模式的第二个类型中的第二种:观察者模式,定义:在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。主要解决一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。类似天气订阅,每当订阅中心的天气改变时,订阅者都会收到改变的通知。有点类似于发布订阅的模式。我们一起来学习吧!
1、UML
观察者模式的角色主要有一下集中
角色 | 作用 |
---|---|
抽象观察者 | 为所有的具体观察者定义一个接口,在得到主题通知时更新自己。 |
具体观察者 | 实现抽象观察者角色所需要的更新接口 |
抽象被观察者 | 也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。 |
具体被观察者角色 | 在改变时,所有登记过的观察者发出通知。 |
为什么说是两个类自检呢,由UML图可知,观察者持有被观察者的引用,被观察者也持有观察者的引用。
2、抽象观察者Observer
/**
* 观察者
* @author suibibk.com
*
*/
public interface Observer {
public void update();
//添加主题(被观察者)
public void addSubject(Subject subject);
//移除主题(被观察者)
public void removeSubject(Subject subject);
}
3、具体观察者ObserverA和ObserverB
/**
* 具体观察者
* @author suibibk.com
*/
public class ObserverA implements Observer {
@Override
public void update() {
System.out.println("ObserverA观察的主题有变化,需要做调整");
}
@Override
public void removeSubject(Subject subject) {
subject.removeObserver(this);
}
@Override
public void addSubject(Subject subject) {
subject.addObserver(this);
}
}
/**
* 具体观察者
* @author suibibk.com
*/
public class ObserverB implements Observer {
@Override
public void update() {
System.out.println("ObserverB观察的主题有变化,需要做调整");
}
@Override
public void removeSubject(Subject subject) {
subject.removeObserver(this);
}
@Override
public void addSubject(Subject subject) {
subject.addObserver(this);
}
}
我这里的实现跟网上得失不同的,我是没有直接持有观察者的引用,而是直接传观察者的对象来,原因在于我觉得观察者和被观察者应该是若耦合的状态,不应该是放在观察者的构造方法里来初始化观察者,这样的话耦合的太紧密。后面我我考虑那么可以用set方法放入,也是可以持有引用的,但是我又想,还是不行,也为我不一定是观察SubjectA,可能后面还有超级多Subject,所以最终也没有用,其实可以尝试用个List,但是还是算了,毕竟只是举个例子而已。
4、被观察者Subject
public interface Subject {
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
//Object中有,这里就不用notifyAll啦
public void notifyAll1();
public void change();
}
5、具体被观察者
public class SubjectA implements Subject{
private List<Observer> observers = new ArrayList<Observer>();
@Override
public void addObserver(Observer observer) {
//如果已经加入就不再加入
for (Observer o : observers) {
if(o.equals(observer)) {
System.out.println("已经观察了");
return;
}
}
System.out.println("添加观察成功");
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observers.contains(observer)){
System.out.println("移除观察成功");
observers.remove(observer);
}
}
@Override
public void notifyAll1() {
for (Observer observer : observers) {
observer.update();
}
}
@Override
public void change() {
System.out.println("主题A有变化,将变化通知给观察者");
notifyAll1();
}
}
6、测试类Test
public class Test {
public static void main(String[] args) {
//建立一个主题
Subject subject = new SubjectA();
Observer a = new ObserverA();
Observer b = new ObserverB();
//观察主题A
a.addSubject(subject);
//观察主题A
b.addSubject(subject);
//主题A有变化
subject.change();
a.addSubject(subject);
}
}
结果如下
添加观察成功
添加观察成功
主题A有变化,将变化通知给观察者
ObserverA观察的主题有变化,需要做调整
ObserverB观察的主题有变化,需要做调整
已经观察了
7、优缺点
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
8、使用场景:
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
9、注意事项
JAVA 中已经有了对观察者模式的支持类。
避免循环引用。
如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
总结
其实就是发布订阅的模式,不过这里还是需要提一句,我的观察者没有直接持有主题的引用,是因为我考虑耦合性以及多个是、主题的模式。如果大家的实现的时候不考虑耦合性就直接持有引用,在构造方法上初始化主题,也可以用set方法初始化耦合性就更低,但是还要考虑多主题的模式的话可能就需要用集合保存或者直接方法传入主题了。纯属个人见解,若有误望指正。