观察者模式
观察者模式又称为依赖,发布-订阅模式,其定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖的对象都得到通知并被自动更新。
Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.
使用场景:
1、当一个对象的数据更新时需要通知其他对象,但这个对象又不希望被通知的那些对象形成高耦合。就是说我不希望我的这个对象都和通知的那些对象之间有直接的引用关系。
2、当一个对象数据更新时需要通知其他对象也各自更新自己的数据,但这个对象不知道具体有多少个对象需要更新数据。
一、具体的主题向观察者推送数据
1、写一个主题(观察者所观察的东东)接口,里面定义的方法主要是看你自己的需求来定义,一般都会有添加、删除、通知观察者等方法。
/**
* 要观察的主题的接口
* @author YDS
*
*/
public interface Subject {
//添加观察者
public void addOberserver(Observer o);
//删除观察者
public void deleteOberserver(Observer o);
//通知观察者
public void notifyObserver();
}
2、创建一个观察者接口,里面定义的方法主要是对收到具体主题发送的消息后进行反应的操作,一般是更新数据。
/**
* 观察者接口
* @author YDS
*
*/
public interface Observer {
//接到电话
public void hearTelephone(String heardMess);
}
3、具体的主题,实现主题接口,实现接口中的方法,和一些具体主题里面需要的方法,例如判断某个要发送的消息是不是最新的方法等等~
/**
* 具体主题
* @author YDS
*
*/
public class SeekJobCenter implements Subject{
String mess;
boolean changed;
//存放观察者引用的数组线性表
ArrayList<Observer> personList;
SeekJobCenter() {
personList=new ArrayList<Observer>();
mess="";
changed=false;
}
public void addOberserver(Observer o) {
//把观察者的引用添加到数组线性表
if (!(personList.contains(o))) {
personList.add(o);
}
}
public void deleteOberserver(Observer o) {
//删除某个观察者
if (personList.contains(o)) {
personList.remove(o);
}
}
public void notifyObserver() {
//通知所有的观察者
if(changed){
for(Observer observer:personList){
observer.hearTelephone(mess);
}
changed=false;
}
}
public void giveNewMess(String str){
if(str.equals(mess)){
changed=false;
}else{
mess=str;
changed=true;
}
}
}
4、具体的观察者,实现观察者接口,定义了一个主题接口的对象,一般主题接口对象是通过构造函数传入的,同时在构造函数中,将我这个当前构造的对象添加到主题里面,也就是将我这个观察者添加到主题里面存放观察者的ArrayList里面。实现更新的方法,或者接口里面其他有特定操作的方法。
/**
* 大学生观察者
* @author YDS
*
*/
public class UniversityStudent implements Observer{
Subject subject;
File myFile;
UniversityStudent(Subject subject,String filename) {
// TODO Auto-generated constructor stub
this.subject=subject;
//使当前实例成为subject所引用的具体主题的观察者
subject.addOberserver(this);
myFile=new File(filename);
}
public void hearTelephone(String heardMess) {
// TODO Auto-generated method stub
try {
RandomAccessFile out=new RandomAccessFile(myFile, "rw");
out.seek(out.length());
byte[] b=heardMess.getBytes();
//更新文件内容
out.write(b);
out.writeBytes("\n\r");
System.out.println("我是一个大学生,");
System.out.println("我向文件"+myFile.getName()+"写入如下内容");
System.out.println(heardMess);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 另一个观察者:海归
*
* @author YDS
*
*/
public class HaiGui implements Observer {
Subject subject;
File myFile;
HaiGui(Subject subject, String fileName) {
this.subject = subject;
subject.addOberserver(this);
myFile = new File(fileName);
}
public void hearTelephone(String heardMess) {
boolean boo = heardMess.contains("程序员") || heardMess.contains("软件");
try {
if (boo) {
RandomAccessFile out = new RandomAccessFile(myFile, "rw");
out.seek(out.length());
byte[] b = heardMess.getBytes();
out.write(b);
//换行
out.writeBytes("\n\r");
System.out.println("我是一个海归,");
System.out.println("我向文件" + myFile.getName() + "写入如下内容:");
System.out.println(heardMess);
} else {
System.out.println("我是海归,这次信息中没有我需要的信息");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
测试类:
/**
* 测试类
* @author YDS
*
*/
public class Application {
/**
* @param args
*/
public static void main(String[] args) {
SeekJobCenter center=new SeekJobCenter();
UniversityStudent zhanglin=new UniversityStudent(center, "A.txt");
HaiGui wanghao=new HaiGui(center, "B.txt");
center.giveNewMess("辉腾公司需要10个java程序员");
center.notifyObserver();
center.giveNewMess("海景公司需要8个动画设计师");
center.notifyObserver();
center.giveNewMess("仁海公司需要9个电工");
center.notifyObserver();
center.giveNewMess("仁海公司需要9个电工");
center.notifyObserver();
}
}
这个小示例,中间分别有主题和观察者接口,具体的主题是求职中心,具体的观察者是大学生求职者,和海归求职者,当求职中心发布消息时,具体的主题求职中心首先回去判断这个消息是不是真的是新消息,如果是,再通知推送给观察者,具体的观察者不同,对接收到的消息做出的反应也不同。—示范来自《Java设计模式》 耿祥义、张跃平编著。
上面给的示例只是最简单的一种,一个主题对应多个观察者,当主题消息更新时通知观察者。观察者模式还有很多其他的实现,比如当观察者只对具体主题某一部分消息的改变有兴趣时,具体的观察者不同,感兴趣的消息内容也不同,比如女孩子一般都比较关心商场里面潮牌的上新,吃货一般会关注商场里面哪里开了个新店有什么好吃的,等等。。这个时候观察者就通过具体主题的引用和具体主题里面提供的方法去获取他们真正感兴趣的东西,其实思想都是一样的,只是在具体实现由略微的差别,我们在学习编程的时候,更应该注重的也是编程的这种思想的学习。
观察者模式的优点:
1、具体主题和具体观察者是低耦合的关系。因为主题接口仅仅依赖观察者接口,因此主题只知道,观察者是实现了观察者接口的,而不必知道是哪个具体的观察者类的对象。同理具体观察者也只需要知道具体主题是实现了主题接口的就行。
所以我们在写代码的过程中,就会方便很多,比如我们在具体的主题里面,只需要一个ArrayList就能保存所有的观察者(实现观察者接口的),而不需要保存很多不同观察者类的实例。而在具体的观察者里面也只需要有主题类的引用就可以了。根据传进来的参数类型不一样再做判断处理就好了
2、观察者模式满足“开闭原则”,如果增加新的具体的观察者(实现观察者的类),不必修改创建具体主题的类的代码。就是说你可以随意新建新的观察者,不用去管具体的主题类。