观察者模式
在软件系统中经常会有这样的需求:如果一个对象的状态发生改变, 某些与它相关的对象也要随之做出相应的变化。 比如,我们要设计一个右键菜单的功能,只要在软件的有效区域内点击鼠标右键, 就会弹出一个菜单;再比如,我们要设计一个自动部署的功能,就像eclipse开发时, 只要修改了文件,eclipse就会自动将修改的文件部署到服务器中。 这两个功能有一个相似的地方,那就是一个对象要时刻监听着另一个对象, 只要它的状态一发生改变,自己随之要做出相应的行动。 其实,能够实现这一点的方案很多,但是,无疑使用观察者模式是一个主流的选择。
观察者模式的结构
在最基础的观察者模式中,包括以下四个角色:
- 被观察者:从类图中可以看到,类中有一个用来存放观察者对象的Vector容器(之所以使用Vector而不使用List,是因为多线程操作时,Vector在是安全的,而List则是不安全的),这个Vector容器是被观察者类的核心,另外还有三个方法:attach方法是向这个容器中添加观察者对象;detach方法是从容器中移除观察者对象;notify方法是依次调用观察者对象的对应方法。这个角色可以是接口,也可以是抽象类或者具体的类,因为很多情况下会与其他的模式混用,所以使用抽象类的情况比较多。
- 观察者:观察者角色一般是一个接口,它只有一个update方法,在被观察者状态发生变化时,这个方法就会被触发调用。
- 具体的被观察者:使用这个角色是为了便于扩展,可以在此角色中定义具体的业务逻辑。
- 具体的观察者:观察者接口的具体实现,在这个角色中,将定义被观察者对象状态发生变化时所要处理的逻辑。
优点
观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。
观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。
代码展示
示例一
package designpatterns.observer.jike;
public interface Observer {
void update();
}
package designpatterns.observer.jike;
public class ConcreteObserver1 implements Observer {
public void update() {
System.out.println("观察者1收到信息,并进行处理。");
}
}
package designpatterns.observer.jike;
public class ConcreteObserver2 implements Observer {
public void update() {
System.out.println("观察者2收到信息,并进行处理。");
}
}
package designpatterns.observer.jike;
import java.util.Vector;
public abstract class Subject {
private Vector<Observer> obs = new Vector();
public void addObserver(Observer obs){
this.obs.add(obs);
}
public void delObserver(Observer obs){
this.obs.remove(obs);
}
protected void notifyObserver(){
for(Observer o : obs){
o.update();
}
}
public abstract void doSomething();
}
package designpatterns.observer.jike;
public class ConcreteSubject extends Subject {
public void doSomething(){
System.out.println("被观察者事件反生");
this.notifyObserver();
}
}
package designpatterns.observer.jike;
public class Client {
public static void main(String[] args) {
Subject sub = new ConcreteSubject();
sub.addObserver(new ConcreteObserver1()); //添加观察者1
sub.addObserver(new ConcreteObserver2()); //添加观察者2
sub.doSomething();
}
}
示例二
package designpatterns.observer.bky;
public interface Observer {
void update(String message, String name);
}
package designpatterns.observer.bky;
public interface HuaiRen {
//添加便衣观察者
void addObserver(Observer observer);
//移除便衣观察者
void removeObserver(Observer observer);
//通知观察者
void notice(String message);
}
package designpatterns.observer.bky;
import java.util.ArrayList;
import java.util.List;
public class XianFan implements HuaiRen {
//别称
private String name = "大熊";
//定义观察者集合
private List<Observer> observerList = new ArrayList<Observer>();
//增加观察者
@Override
public void addObserver(Observer observer) {
if (!observerList.contains(observer)) {
observerList.add(observer);
}
}
//移除观察者
@Override
public void removeObserver(Observer observer) {
if (observerList.contains(observer)) {
observerList.remove(observer);
}
}
//通知观察者
@Override
public void notice(String message) {
for (Observer observer : observerList) {
observer.update(message, name);
}
}
}
package designpatterns.observer.bky;
public class BianYi implements Observer {
//定义姓名
private String bName = "张昊天";
@Override
public void update(String message, String name) {
System.out.println(bName + ":" + name + "那里有新情况:" + message);
}
}
package designpatterns.observer.bky;
public class Clienter {
public static void main(String[] args) {
//定义两个嫌犯
HuaiRen xf1 = new XianFan();
// Huairen xf2 = new XianFan2();
//定义三个观察便衣警察
Observer o1 = new BianYi();
// Observer o2 = new Bianyi2();
// Observer o3 = new Bianyi3();
//为嫌犯增加观察便衣
xf1.addObserver(o1);
// xf1.addObserver(o2);
// xf2.addObserver(o1);
// xf2.addObserver(o3);
//定义嫌犯1的情况
String message1 = "又卖了一批货";
String message2 = "老大要下来视察了";
xf1.notice(message1);
// xf2.notice(message2);
}
}
总结
java语言中,有一个接口Observer,以及它的实现类Observable,对观察者角色常进行了实现。我们可以在jdk的api文档具体查看这两个类的使用方法。
做过VC++、javascript DOM或者AWT开发的朋友都对它们的事件处理感到神奇, 了解了观察者模式,就对事件处理机制的原理有了一定的了解了。 如果要设计一个事件触发处理机制的功能 ,使用观察者模式是一个不错的选择,AWT中的事件处理DEM (委派事件模型Delegation Event Model)就是使用观察者模式实现的。
观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监听者,类似监听器的存在,一旦被观察/监听的目标发生的情况,就会被监听者发现,这么想来目标发生情况到观察者知道情况,其实是由目标将情况发送到观察者的。
观察者模式多用于实现订阅功能的场景,例如微博的订阅,当我们订阅了某个人的微博账号,当这个人发布了新的消息,就会通知我们。
关键点:
- 针对观察者与被观察者分别定义接口,有利于分别进行扩展。
- 重点就在被观察者的实现中:
- 定义观察者集合,并定义针对集合的添加、删除操作,用于增加、删除订阅者(观察者)
- 定义通知方法,用于将新情况通知给观察者用户(订阅者用户)
- 观察者中需要有个接收被观察者通知的方法。
观察者模式定义的是一对多的依赖关系,一个被观察者可以拥有多个观察者,并且通过接口对观察者与被观察者进行逻辑解耦,降低二者的直接耦合。
如此这般,想了一番之后,突然发现这种模式与桥接模式有点类似的感觉。
桥接模式也是拥有双方,同样是使用接口(抽象类)的方式进行解耦,使双方能够无限扩展而互不影响,其实二者还是有者明显的区别:
- 主要就是使用场景不同,桥接模式主要用于实现抽象与实现的解耦,主要目的也正是如此,为了双方的自由扩展而进行解耦,这是一种多对多的场景。观察者模式侧重于另一方面的解耦,侧重于监听方面,侧重于一对多的情况,侧重于一方发生情况,多方能获得这个情况的场景。
- 另一方面就是编码方面的不同,在观察者模式中存在许多独有的内容,如观察者集合的操作,通知的发送与接收,而在桥接模式中只是简单的接口引用。