Java设计模式之观察者模式
一.设计模式分为三种类型,共23种:
创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式.
今天主要讲解的是:观察者模式
二.什么是观察者模式?
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
这个说法比较官方,那么通俗的讲下:
个人理解:其实就是一种“监听”机制,当一个对象(被观察者)做了修改,或者说是改变,它就会通知监听它的对象(也就是观察者),然后观察者可以通过这个通知结果,在做自己的业务逻辑。
我们看个图:
或者还有别的例子:
1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
3、现在许多购房者都密切观察者房价的变化,当房价变化时,所有购房者都能观察到,以上的购房者属于观察者,这便是观察者模式。
三.使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项:
- 1.Java 中已经有了对观察者模式的支持类。
- 2.避免循环引用。
- 3.如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
四.JAVA自带的观察者模式:
观察者模式在JAVA里官方已经给出了支持,也就是用官方给出的工具类就行。
如果我们不用JAVA自带支持类,也可以手写,不过区别不大,我个人认为用JDK自带的支持类效率会高,而自己手写设计的,会更符合自己的需求。
下面我们看看JDK自带观察者模式:
1.被观察者类:需要继承java.util.Observable类。
2.观察者:实现java.util.Observer接口。
3.addObserver()方法添加观察者。
4.只要是被观察者发生了改变,被观察者就需要调用setChanged()方法,其实就是相当于标记一下。
5.当调用setChanged()方法之后,在使用notifyObservers()方法去通知所有的观察者。
注意:
如果在调用notifyObservers()方法之前没有调用setChanged()方法,就不会有什么动作发生。
notifyObservers()方法中包含clearChanged()方法,将标志变量置回原值。
notifyObservers()方法采用的是从后向前的遍历方式,即最后加入的观察者最先被调用update()方法。
代码演示
下面我们用代码演示下:
(1)首先我们先定义个被观察者:天气
public class Weather extends Observable {
private String message;
//天气状态,0是晴天,1是雨天
private int state;
public void setState(int state) {
this.state = state;
if (this.state==1){ //下雨天
message="下雨了";
}else {
message="天晴了";
}
setChanged();
notifyObservers();
}
public int getState() {
return state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
(2)定义2个观察者:
猫类:
public class Cat implements Observer {
@Override
public void update(Observable o, Object arg) {
if(((Weather) o).getState()==1){
System.out.println(((Weather) o).getMessage()+"小猫要回猫窝了");
}else {
System.out.println(((Weather) o).getMessage()+"小猫出来玩了");
}
}
}
狗类:
public class Dog implements Observer {
@Override
public void update(Observable o, Object arg) {
if(((Weather) o).getState()==1){
System.out.println(((Weather) o).getMessage()+"小狗要回家看家了");
}else {
System.out.println(((Weather) o).getMessage()+"小狗出门玩了");
}
}
}
我们测试一下:
public class Test {
public static void main(String[] args) {
Weather weather =new Weather();
weather.addObserver(new Cat());//注册猫类监听者
weather.addObserver(new Dog());//注册狗类监听者
weather.setState(1);//被监听者状态发生了改变
}
}
结果:
下雨了小狗要回家看家了
下雨了小猫要回猫窝了
这里面我们看下 猫类和狗类里的update(Observable o, Object arg)方法,我们是用Observable o向下转型的方式获取到了想到的字段,那么另一个参数 Object arg怎么用呢?
我们修改下代码:
public class Dog implements Observer {
@Override
public void update(Observable o, Object arg) {
if(arg.equals(1)){
System.out.println(((Weather) o).getMessage()+"小狗要回家看家了");
}else {
System.out.println(((Weather) o).getMessage()+"小狗出门玩了");
}
}
}
上面只是修改了notifyObservers(this.state),我们把参数放进去了,之前是没有传参数的。
在修改下监听者代码:
public class Cat implements Observer {
@Override
public void update(Observable o, Object arg) {
if(arg.equals(1)){
System.out.println(((Weather) o).getMessage()+"小猫要回猫窝了");
}else {
System.out.println(((Weather) o).getMessage()+"小猫出来玩了");
}
}
}
狗类:
public class Dog implements Observer {
@Override
public void update(Observable o, Object arg) {
if(arg.equals(1)){
System.out.println(((Weather) o).getMessage()+"小狗要回家看家了");
}else {
System.out.println(((Weather) o).getMessage()+"小狗出门玩了");
}
}
}
我们看到只是修改的if条件,从之前的向下转型获得对象里的属性,变成了上面直接传过来的this.state的值。
其实已经很明显了,如果在notifyObservers(this.state)里不写参数,会默认的把整个对象都返回,而写了参数的话,在传对象的基础上,又单独的传了这个参数。(这个也叫观察者的拉模式和推模式,个人感觉不到明显的差别,只是说,站在返回的数据角度,拉模式就是在返回的数据中拉取自己想要的某一个数据,而推模式是整个的对象都给你)。
五.观察者模式的优缺点:
1.优点:
A.观察者和被观察者是抽象耦合的。
B.建立一套触发机制。
2.缺点:
A.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
B.如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
C.观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。