设计模式第三篇,观察者模式,大家多多指教
简介
观察者模式定义了对象之间的一组一对多的依赖,当一个对象改变时,其他被依赖的对象都会收到通知并且自动更新(引自《Head First设计模式》)。首先观察者模式有一个对象,我们称之为主题对象(Subject),有很多其他的对象我们称之为观察者(Observer),主题对象和观察者是一对多的关系,当主题对象发生改变时,观察者会收到来自于主题对象的通知,并根据通知做出相应的操作。在这里有一个需要注意的地方,那就是一个对象要成为观察者或者不想做观察者了,都需要告知主题对象,主题对象会更新一对多的这个依赖关系,由此可见主题对象应该维护的有一个观察者列表。
JDK内置的观察者模式
JAVA API内置的观察者模式分别是java.util.Observable类和java.util.Observable接口。要实现观察者模式只需要把主题对象继承java.util.Observable类,把观察者实现java.util.Observable接口。下面我们通过一个例子来展示观察者模式的工作过程,例子的场景是某个社区提问的过程:
第一步:新建一个问题类,定义两个成员变量分别是提问人和问题的内容
/* * 问题 */ public class Question { /** 提问人 */ private String name; /** 问题内容 */ private String question; public Question(String name, String question) { this.name = name; this.question = question; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getQuestion() { return question; } public void setQuestion(String question) { this.question = question; } }
第二步:新建主体对象,此处的主体对象应该是社区,社区类继承Observable类,提问方法是publishQuestion,此处会设置主体对象为已改变,并且通知所有的观察者
/* * 社区对象 */ public class Community extends Observable { private static Community community; private Community(){ if(community != null){ throw new RuntimeException("不要想反射攻击我"); } } public static Community getInstance(){ if(community == null){ community = new Community(); } return community; } public void publishQuestion(Question question){ System.out.println(question.getName()+"在社区提了提了一个问题,问题内容是:"+ question.getQuestion()); /** 设置主体对象的状态为已改变 */ setChanged(); /** 通知所有的观察者 */ notifyObservers(question); } }
第三步:新建观察者为社区的工作人员,负责解决问题,观察者要实现Observer接口
public class Worker implements Observer { @Override public void update(Observable o, Object arg) { Community community = (Community) o; Question question = (Question) arg; System.out.println("=================="); System.out.println("收到来自社区的一个提问,提问人是:"+ question.getName()); System.out.println("内容是:"+ question.getQuestion()); } }
第四步:注册观察者,把观察者和主题对象关联起来
public class ObserverTest { public static void main(String[] args) { Community community = Community.getInstance(); Question question = new Question("张三","几点上班?"); /** 观察者注册 */ community.addObserver(new Worker()); community.publishQuestion(question); } }
番外
上面的观察者模式的实现是由主题对象往观察者去“推消息“,即主题对象主动去通知观察者(Community去通知Worker) ,其实这种java API还支持观察者去主题对象去“拉消息”,这种做法和推各有特点,推的好处是由主题对象去控制推的时机和内容,能够一次性把所有的东西推给观察者,但是对于不同的观察者而言有种一视同仁的感觉,如果所有观察者需要的信息并不一致的话,就需要去推更多的内容,这其中必定包含一些冗余的消息。拉的好处是能够由观察者自主的去选择需要的内容和时机,这样观察者能够只拉自己想要的东西,而且由于时间又观察者定,主题对象和观察者之间的交互就不会有那么频繁,但是这样的话主题对象必须把自己“暴露”给观察者,而且收集到的信息可能不全,需要多次收集才能补全。(目前主流的做法是推)
总结
jdk自带的观察者模式实现了主题对象和观察者之前的松耦合,定义了对象之间的一对多的关系,主题对象不需要知道观察者的细节,也不需要知道到底谁成为了观察者。jdk自带的观察者模式是使用继承的方式去实现,一定程度上违背的OO设计的原则:多用组合,少用继承,而且继承带来的问题必然是扩展的麻烦。Observable类其实是比较简单的,它的源码主要就是两个成员变量和几个方法,如果有必要的话,可以实现自己的Observable,希望各位小伙伴都能掌握松耦合的设计思想。