观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖他的对象都会得到通知并自动更新.
在许多设计中,经常涉及到多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望能跟踪这个特殊对象中的数据变化.
例如:某些寻找工作的人都对求职中心的职业需求信息变化非常关心,很想跟踪求职中心的职业需求信息的变化.
面对这样的问题,不用担心,观察者模式是关于多个对象想知道一个对象中数据变化情况的的一种成熟模式.
观察者模式结构中包括四个角色:
主题:主题是一个接口,该接口规定了具体主题需要实现的方法,比如添加,删除观察者以及通知观察者更新数据的方法.
具体主题:具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据.具体主题需要使用一个集合,比如ArrayList,
存放观察者的引用,以便数据变化时通知具体观察者.
观察者:观察者是一个接口,该接口规定了具体观察者用来更新数据的方法.
具体观察者:具体观察者是实现观察者接口类的实例.具体观察者依赖于主题变量,以便观察者可以添加到主题的集合中,或从主题的集合中移除.
应用场景一:
有两个大学生在求职网找工作,同时注册了一个招聘网站,并希望这个网站有什么最新的招聘信息能及时通知他们.
1.推数据方式:是将具体主题变化后的数据全部交给具体观察者.
2.拉数据方式:不将变化后的数据交给具体观察者,而是提供了获取这些数据的方法,具体观察者在得到通知后,根据自己需要的数据拉上来.
应用场景二:
如上的应用场景一是采用推数据方式,下面对其扩展,比如说招聘网站提供了java和c的工作,而两位大学生一位对java感兴趣,一位对c感兴趣.
1.具体主题和具体观察者是松耦合关系.由于主题接口仅仅依赖于观察者接口,因此具体主题只知道他依赖的观察者是实现观察者接口的某个类的实例,
但不需要知道具体是哪个类,同样由于观察者仅仅依赖于主题接口,因此具体观察者只知道他依赖的主题是实现主题接口的某个类的实例,但不需要具体
知道是哪个类.
2.观察者模式满足"开闭原则".主题接口仅仅依赖观察者接口,因此,如果创建新的实现观察者的接口的类,不必修改具体主题类的代码.同样,观察者接口
仅仅依赖于主题接口,因此,如果增加新的实现主题接口的类,不必修改具体观察者的代码.
在许多设计中,经常涉及到多个对象都对一个特殊对象中的数据变化感兴趣,而且这多个对象都希望能跟踪这个特殊对象中的数据变化.
例如:某些寻找工作的人都对求职中心的职业需求信息变化非常关心,很想跟踪求职中心的职业需求信息的变化.
面对这样的问题,不用担心,观察者模式是关于多个对象想知道一个对象中数据变化情况的的一种成熟模式.
观察者模式结构中包括四个角色:
主题:主题是一个接口,该接口规定了具体主题需要实现的方法,比如添加,删除观察者以及通知观察者更新数据的方法.
具体主题:具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据.具体主题需要使用一个集合,比如ArrayList,
存放观察者的引用,以便数据变化时通知具体观察者.
观察者:观察者是一个接口,该接口规定了具体观察者用来更新数据的方法.
具体观察者:具体观察者是实现观察者接口类的实例.具体观察者依赖于主题变量,以便观察者可以添加到主题的集合中,或从主题的集合中移除.
应用场景一:
有两个大学生在求职网找工作,同时注册了一个招聘网站,并希望这个网站有什么最新的招聘信息能及时通知他们.
1.主题接口类,直接看代码:Subject.
<span style="font-size:12px;">package com.ilucky.pattern.observer.one;
/**
* @author IluckySi
* @date 20140612
*/
public interface Subject {
public abstract void addObserver(Observer observer);
public abstract void deleteObserver(Observer observer);
public abstract void notifyObservers();
}</span>
2.具体主题类,直接看代码:SubjectOne.
<span style="font-size:12px;">package com.ilucky.pattern.observer.one;
import java.util.ArrayList;
import java.util.List;
/**
* @author IluckySi
* @date 20140612
*/
public class SubjectOne implements Subject {
private String notify;
private boolean flag;
private List<Observer> observers;
public SubjectOne() {
notify = "";
flag = false;
observers = new ArrayList<Observer>();
}
public void addObserver(Observer observer) {
if(!observers.contains(observer)) {
observers.add(observer);
}
}
public void deleteObserver(Observer observer) {
if(observers.contains(observer)) {
observers.remove(observer);
}
}
public void notifyObservers() {
if(flag) {
for(int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
observer.getNotify(notify);
}
}
flag = false;
}
public void setNotify(String notify) {
if(this.notify.equals(notify)) {
flag = false;
} else {
System.out.println("SubjectOne网站发布(新)公告:" + notify);
this.notify = notify;
flag = true;
}
}
}</span>
3.观察者类,直接看代码:Observer.
<span style="font-size:12px;">package com.ilucky.pattern.observer.one;
/**
* @author IluckySi
* @date 20140612
*/
public interface Observer {
public void getNotify(String notify);
}</span>
4.具体观察者,直接看代码:ObserverOne,ObserverTwo.
<span style="font-size:12px;">package com.ilucky.pattern.observer.one;
/**
* @author IluckySi
* @date 20140612
*/
public class ObserverOne implements Observer {
public ObserverOne(Subject subject) {
subject.addObserver(this);
}
public void getNotify(String notify) {
System.out.println("ObserverOne收到网站布告:" + notify);
}
}</span>
<span style="font-size:12px;"><pre name="code" class="java">package com.ilucky.pattern.observer.one;
/**
* @author IluckySi
* @date 20140612
*/
public class ObserverTwo implements Observer {
public ObserverTwo(Subject subject) {
subject.addObserver(this);
}
public void getNotify(String notify) {
System.out.println("ObserverTwo收到网站布告:" + notify);
}
}
</span>
5.最后看测试类MainTest.
<span style="font-size:12px;">package com.ilucky.pattern.observer.one;
/**
* @author IluckySi
* @date 20140612
*/
public class MainTest {
public static void main(String[] args) {
//创建主题.
SubjectOne subjectOne = new SubjectOne();
//创建观察者.
new ObserverOne(subjectOne);
new ObserverTwo(subjectOne);
//主题更新信息, 并通知所有观察则.
subjectOne.setNotify("腾讯公司招聘10名c++工程师");
subjectOne.notifyObservers();
subjectOne.setNotify("百度公司招聘20名java工程师");
subjectOne.notifyObservers();
subjectOne.setNotify("百度公司招聘20名java工程师");
subjectOne.notifyObservers();
}
}
/**
输出结果:
SubjectOne网站发布(新)公告:腾讯公司招聘10名c++工程师
ObserverOne收到网站布告:腾讯公司招聘10名c++工程师
ObserverTwo收到网站布告:腾讯公司招聘10名c++工程师
SubjectOne网站发布(新)公告:百度公司招聘20名java工程师
ObserverOne收到网站布告:百度公司招聘20名java工程师
ObserverTwo收到网站布告:百度公司招聘20名java工程师
*/</span>
注意:具体主题在通知具体观察者更新数据时有两种极端方式.1.推数据方式:是将具体主题变化后的数据全部交给具体观察者.
2.拉数据方式:不将变化后的数据交给具体观察者,而是提供了获取这些数据的方法,具体观察者在得到通知后,根据自己需要的数据拉上来.
应用场景二:
如上的应用场景一是采用推数据方式,下面对其扩展,比如说招聘网站提供了java和c的工作,而两位大学生一位对java感兴趣,一位对c感兴趣.
1.主题接口类,直接看代码:Subject.
<span style="font-size:12px;">package com.ilucky.pattern.observer.two;
/**
* @author IluckySi
* @date 20140612
*/
public interface Subject {
public abstract void addObserver(Observer observer);
public abstract void deleteObserver(Observer observer);
public abstract void notifyObservers();
}</span>
2.具体主题类,直接看代码:SubjectOne.
<span style="font-size:12px;">package com.ilucky.pattern.observer.two;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author IluckySi
* @date 20140612
*/
public class SubjectOne implements Subject {
private Map<String, String> notify;
private List<Observer> observers;
public SubjectOne() {
notify = new HashMap<String, String>();
observers = new ArrayList<Observer>();
}
public void addObserver(Observer observer) {
if(!observers.contains(observer)) {
observers.add(observer);
}
}
public void deleteObserver(Observer observer) {
if(observers.contains(observer)) {
observers.remove(observer);
}
}
public void notifyObservers() {
for(int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
observer.getNotify();
}
}
public void setNotify(Map<String, String> notify) {
this.notify = notify;
}
public String getNotify(String key) {
return notify.get(key);
}
}</span>
3.观察者类,直接看代码:Observer.
<span style="font-size:12px;">package com.ilucky.pattern.observer.two;
/**
* @author IluckySi
* @date 20140612
*/
public interface Observer {
public void getNotify();
}</span>
4.具体观察者,直接看代码:ObserverOne,ObserverTwo.
<span style="font-size:12px;">package com.ilucky.pattern.observer.two;
/**
* @author IluckySi
* @date 20140612
*/
public class ObserverOne implements Observer {
public Subject subject;
public ObserverOne(Subject subject) {
this.subject = subject;
subject.addObserver(this);
}
public void getNotify() {
if(subject instanceof SubjectOne) {
System.out.println("ObserverOne收到网站布告:" + ((SubjectOne)subject).getNotify("c"));
}
}
}
</span>
<span style="font-size:12px;">package com.ilucky.pattern.observer.two;
/**
* @author IluckySi
* @date 20140612
*/
public class ObserverTwo implements Observer {
public Subject subject;
public ObserverTwo(Subject subject) {
this.subject = subject;
subject.addObserver(this);
}
public void getNotify() {
if(subject instanceof SubjectOne) {
System.out.println("ObserverTwo收到网站布告:" + ((SubjectOne)subject).getNotify("java"));
}
}
}</span>
5.最后看测试类MainTest.
<span style="font-size:12px;">package com.ilucky.pattern.observer.two;
import java.util.HashMap;
import java.util.Map;
/**
* @author IluckySi
* @date 20140612
*/
public class MainTest {
public static void main(String[] args) {
//创建主题.
SubjectOne subjectOne = new SubjectOne();
//创建观察者.
new ObserverOne(subjectOne);
new ObserverTwo(subjectOne);
//主题更新信息, 并通知所有观察者.
Map<String, String> map = new HashMap<String, String>();
map.put("java", "百度公司招聘20名java工程师");
map.put("c", "腾讯公司招聘10名c++工程师");
subjectOne.setNotify(map);
subjectOne.notifyObservers();
}
}
/**
输出结果:
ObserverOne收到网站布告:腾讯公司招聘10名c++工程师
ObserverTwo收到网站布告:百度公司招聘20名java工程师
*/</span>
观察者模式的优点:1.具体主题和具体观察者是松耦合关系.由于主题接口仅仅依赖于观察者接口,因此具体主题只知道他依赖的观察者是实现观察者接口的某个类的实例,
但不需要知道具体是哪个类,同样由于观察者仅仅依赖于主题接口,因此具体观察者只知道他依赖的主题是实现主题接口的某个类的实例,但不需要具体
知道是哪个类.
2.观察者模式满足"开闭原则".主题接口仅仅依赖观察者接口,因此,如果创建新的实现观察者的接口的类,不必修改具体主题类的代码.同样,观察者接口
仅仅依赖于主题接口,因此,如果增加新的实现主题接口的类,不必修改具体观察者的代码.