观察者模式是一种比较常见的设计模式,在安卓源码中也应用得很多,如baseAdapter、相关触发器机制。
首先从一个情景入手,战国时期,李斯(观察者)作为秦国宰相希望监视(观察)韩非子(被观察者),当韩非子吃饭、玩乐、参与国家大事时李斯都能够知道。很容易想到一种实现,在韩非子的类中定义相关属性,例如boolean类型的isPlaying,表示韩非子是否在玩乐,当韩非子玩乐时改变属性值并提供getter、setter。实现代码可如下:
public class HanFeiZi {
private boolean isPlaying=false;
public boolean isPlaying() {
return isPlaying;
}
public void setPlaying(boolean isPlaying) {
this.isPlaying = isPlaying;
}
public void haveFun() {
isPlaying=true;
}
}
李斯
为了观察韩信的一举一动,需要创建一个间谍(spy)对象,而间谍对象就是一直盯着韩非子不停地获取韩非子的相关属性,如果发生了变化则通知李斯。代码实现上建立一个线程死循环检索韩非子相关属性,并关联李斯对象,发生变化则通知李斯。这样子可以实现效果,但是从资源上看死循环一直占有资源,影响程序运行效率,从设计模式上看,面向过程而非面向对象。
上述实现方式的思路是观察者去不断尝试被观察者是否发生改变,观察者模式理解的关键是被观察者发生改变时主动去通知观察者:“我发生改变了”。
根据此思想可以写出如下代码:
public void haveFun() {
liSi.update();
}
当韩非子玩乐时告诉相关联的李斯对象,此时也能实现效果。但此时又存在问题,如果监视韩非子的不止李斯一个人,还有李斯1,李斯2……此时需要关联很多的对象,并且李斯关心的不只是韩非子的玩乐,此时按照上述原则会导致代码耦合冗余,违背开闭原则。
此时,经过循序渐进的尝试,可以看一下观察者模式的类图。
包括四个对象,如下:
Subject又称Observable,被观察者接口,能够动态地添加、删除观察者,并且能够通知观察者被观察者的更新。
Observer,观察者接口,观察者接收到更新信息后调用update进行信息处理。
ConcreteSubject:实现被观察者接口,定义自身业务逻辑,并决定对哪些事件进行通知。
ConcreteObserver:实现观察者接口,观察者在接收到消息后的反应不同,有各自的逻辑处理。
重新设定一个场景,女孩子吃零食时触发男朋友和女孩子妈妈不同的反应。我们可以逐步实现这个场景。
首先定义观察者接口:
public interface Observer {
void update(Observable observable,String msg);
}
提供update函数对接收到的信息进行处理。
紧接着实现被观察者接口:
public interface Observable {
void addObserver(Observer observer);
void deleteObserver(Observer observer);
void notifyObservers(String msg);
}
提供对观察者的添加、删除和消息通知。
实现女孩类:
public class GirlFriend implements Observable,GirlAction{
private ArrayList<Observer> observerList=new ArrayList();
public void addObserver(Observer observer) {
observerList.add(observer);
}
public void deleteObserver(Observer observer) {
observerList.remove(observer);
}
public void notifyObservers(String msg) {
for(Observer observer:observerList)
observer.update(this, msg);
}
public void eat() {
System.out.println("女孩自己讲:我吃零食了");
notifyObservers("女孩讲:我吃零食了");
}
/**
* @param args
*/
public static void main(String[] args) {
}
}
女孩类中有一个私有成员observerList作为观察者集合用来管理观察者。基于面向接口编程的理念,实现女孩的行为接口:
public interface GirlAction {
void eat();
}
此时,创建男朋友观察者类:
public class BoyFriend implements Observer{
public void update(Observable observable, String msg) {
// TODO Auto-generated method stub
System.out.println("男朋友接收到信息:"+msg);
System.out.println("男朋友说:随便吃,我养你");
}
}
其中自定义接收到信息后的应对逻辑。接着创建妈妈观察者类:
public class Mother implements Observer {
public void update(Observable observable, String msg) {
// TODO Auto-generated method stub
System.out.println("妈妈接收到信息:"+msg);
System.out.println("妈妈说:吃太多,嫁不出去");
}
}
此时观察者、被观察者均已创建。在被观察者的main函数中注册观察者:
Observer boyFriend=new BoyFriend();
Observer mother=new Mother();
GirlFriend girlFriend=new GirlFriend();
girlFriend.addObserver(boyFriend);
girlFriend.addObserver(mother);
girlFriend.eat();
此时,所有代码完成。运行结果如下:
女孩自己讲:我吃零食了
男朋友接收到信息:女孩讲:我吃零食了
男朋友说:随便吃,我养你
妈妈接收到信息:女孩讲:我吃零食了
妈妈说:吃太多,嫁不出去
以上即为一个观察者模式的简单实现。
观察者模式的优点:
1.观察者和被观察者之间抽象耦合,不管是增加观察者还是被观察者都很容易扩展。
2.形成了触发机制,例如打猎导致一只母鹿死亡,母鹿死亡导致小鹿饿死,小鹿饿死引发秃鹰争抢……
观察者模式缺点:
1.开发效率:一个被观察者、多个观察者导致开发调试比较复杂
2.运行效率:消息的通知默认是顺序执行,上一个消息处理的崩溃会导致下一个消息无法传达,可以使用异步方式(线程)解决。
观察者模式使用场景:
1.存在关联行为的场景
2.事件存在多级触发的场景
3.跨系统的消息交换场景,如消息队列处理机制。需要注意的是安卓的handler处理机制仍是使用的while(true)方式。
观察者模式注意事项:
如果一个观察者同时具有被观察者的身份,很容易造成a触发b,b触发c陷入回调循环中。所以建议观察者模式中最多存在一个对象既是观察者又是被观察者,即一条消息最多被转发一次(传递两次)。例如:A->B->C,B作为观察者获得A传来的消息,作为被观察者传递消息给C。
观察者模式扩展:
1.为了更加符合单一职责原则,可被观察者抽象出一个父类实现观察者管理和消息通知。
2.java中提供了java.util.Observable接口,我们不需要自定义实现系统的即可。
观察者模式在实际项目中的扩展:
1.观察者被观察者之间的消息沟通
实际项目中,观察者获取消息时会接收两个参数,一个为Observable类型的被观察者对象,一个是被观察者生成封装好的Javabean对象,封装了相关信息。
2.观察者消息的处理方式
如果观察者对于消息的处理逻辑存在耗时操作会导致被观察者对象执行消息通知时间增长。此时可以使用两种技术改善,第一种使用多线程的异步框架,第二种使用缓存技术同步矿建预先准备好相关资源。
特记下,以备后日回顾。