【设计模式】观察者模式
观察者模式
在许多设计中,经常涉及多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望跟踪那个特殊对象中的数据变化,也就是说当对象间存在一对多关系时,在这样的情况下就可以使用观察者模式。当一个对象被修改时,则会自动通知它的依赖对象。
观察者模式是关于多个对象想知道一个对象中数据变化情况的一种成熟的模式。观察者模式中有一个称作“主题”的对象和若干个称作“观察者”的对象,“主题”和“观察者”间是一种一对多的依赖关系,当“主题”的状态发生变化时,所有“观察者”都得到通知。
概念
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
关于
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
-
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
-
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
-
一个对象必须通知其他对象,而并不知道这些对象是谁。
-
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
实现
观察者模式的结构中包含四种角色:
主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及通知观察者更新数据的方法。
观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。
创建Observer类
抽象观察者,为所有具体观察者定义一个接口,在得到主题通知时更新自己。这个接口叫做更新接口,抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包括一个Update方法,这个方法叫做更新方法。
public interface Observer {
void Update();
}
创建Subject类
public abstract class Subject {
// 定义容器 用于存放观察者对象
private List<Observer> observers = new ArrayList<Observer>();
//增加观察者
public void attach(Observer observer){
observers.add(observer);
}
//移除观察者
public void detach(Observer observer){
observers.remove(observer);
}
//通知
public void notifyObserver(){
for (Observer o:observers) {
o.Update();
}
}
}
创建ConcreteSubject类
public class ConcreteSubject extends Subject{
// 具体的业务
public void doSomething(){
super.notifyObserver();
}
}
创建ConcreteObserver类
具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体类实现。
public class ConcreteObserver implements Observer{
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void Update() {
System.out.println(name + ":接受到信息,并进行处理");
}
}
测试
public class ObserverDemo {
public static void main(String[] args) {
// 创建一个主题(被观察者)
ConcreteSubject subject = new ConcreteSubject();
// 创建一个观察者
Observer observer1 = new ConcreteObserver("观察者1");
Observer observer2 = new ConcreteObserver("观察者2");
Observer observer3 = new ConcreteObserver("观察者3");
subject.attach(observer1);
subject.attach(observer2);
subject.attach(observer3);
subject.doSomething();
// 观察者1:接受到信息,并进行处理
// 观察者2:接受到信息,并进行处理
// 观察者3:接受到信息,并进行处理
subject.detach(observer1);
subject.doSomething();
// 观察者2:接受到信息,并进行处理
// 观察者3:接受到信息,并进行处理
}
}
观察者模式就是当一个行为发生时传递信息给另外一个用户接收做出相应的处理,两者之间没有直接的耦合关联。
优点:
1、具体主题和具体观察者是松耦合关系。由于主题接口仅仅依赖于观察者接口,因此具体主题只是知道它的观察者是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题接口,因此具体观察者只是知道它依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是哪个类。
2、观察者模式满足“开-闭原则”。主题接口仅仅依赖于观察者接口,这样,就可以让创建具体主题的类也仅仅是依赖于观察者接口,因此,如果增加新的实现观察者接口的类,不必修改创建具体主题的类的代码。。同样,创建具体观察者的类仅仅依赖于主题接口,如果增加新的实现主题接口的类,也不必修改创建具体观察者类的代码。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
微信关注

编程那点事儿