您的位置:首页 > 其它

设计模式笔记(六)——观察者模式

2018-04-09 00:00 162 查看
摘要: 前几天在做Spring的监听器时突然想到了观察者模式,这个也很重要。。。。

1. 是什么——定义

定义对象间的一种一对多的依赖关系,让多个观察者同时监听某一个主题现象,当一个对象的状态发生改变时,会通知所有观察者对象,所有依赖于它的对象都得到通知并被自动更新。

2. 为什么——特点

一个对象状态改变的同时,需要同时改变其他对象。

3. 什么时候用——适用性

l 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

l 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

l 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。

l 一个对象必须通知其他对象,而并不知道这些对象是谁。

l 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

4. UML图



5. 怎么用——使用方法

需求:

气象局发布天气预报,学生和上班族根据天气做出相应反应。

5.1 气象局

设计WeatherStation:

内部维护一个{“晴天”, “下雨”, “雾霾”, “冰雹”, “狂风”, “暴雪”}

气象局要根据观测进行天气更新

updateWeather();

气象局要不断工作,不断观测天气:

死循环中每隔1-2秒执行一次

updateWeather() {
while(true){
//更新天气
}
}


5.2 学生

学生要设计看天气预报的方法

notifyWeather();

注意这里可以给气象局的weather只设置get方法,这样别人只能看

让学生每次都去看气象局的天气,也就是在notify方法中传入天气

这里使用简单if,else-if,else结构:

l 晴天——开心上学

l 下雨——打着雨伞上学

l 雾霾——开心的吸着毒上学

l 冰雹——学校门口砸了个大坑,不去上学

l 狂风——学校被吹走了,不去上学

l 暴雪——学校门口被雪埋了,不去上学

public void notifyWeather(String weather) {
if ("晴天".equals(weather)) {
System.out.println(name + "开开心心的上学!");
}else if ("下雨".equals(weather)) {
System.out.println(name + "打伞上学!");
}else if ("雾霾".equals(weather)) {
System.out.println(name + "开心的吸着毒上学!");
}else if ("冰雹".equals(weather)) {
System.out.println("学校门口被砸了个坑,不去了!");
}else if ("下雪".equals(weather)) {
System.out.println("学校门口被雪埋了,不去上学!");
}else if ("狂风".equals(weather)) {
System.out.println("学校被吹走了,没学上了!");
}
}

之后,在Client中编写:

while(true){
student.notifyWeather(station.getWeather());
}

发现这种方法实在太差,这样学生啥也别干了,光盯着天气预报算了。

而且,如果只是这样,会在客户端留下一个非常严重的问题:

因为气象局一直在观测天气,所以work()方法后面的代码永远无法执行。

5.3 解决代码无法执行的bug

new Thread(new Runnable() {
@Override
public void run() {
while(true){
updateWeather();
int s = random.nextInt(1000)+1000;
try {
Thread.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();

运行之后发现会出现观察时间与气象局更新时间不匹配的bug。

问题:现在是人主动去关注天气,能不能让气象局播天气预报?

5.4 气象局维护学生

新建一个方法:

addStudent();

每次更新天气时,迭代学生列表,通知学生

public void updateWeather() throws Exception {
while (true) {
weather = weathers[random.nextInt(weathers.length)];
System.out.println("当前的天气是:" + weather);
for (Student student : students) {
student.notifyWeather(weather);
}
Thread.sleep(random.nextInt(1000) + 1000);
}
}

到此为止,已经构成了观察者模式

5.5 新增上班族

l 晴天——开心上班

l 下雨——打着雨伞上班

l 雾霾——带着消毒面具上班

l 冰雹——带着三级头盔上班

l 狂风——拖着大石头上班

l 暴雪——披着被子上班

public void notifyWeather(String weather) {
if ("晴天".equals(weather)) {
System.out.println(name + "开开心心的上班!");
}else if ("下雨".equals(weather)) {
System.out.println(name + "打伞上班!");
}else if ("雾霾".equals(weather)) {
System.out.println(name + "戴着防毒面具上班!");
}else if ("冰雹".equals(weather)) {
System.out.println(name + "戴着三级头去上班");
}else if ("下雪".equals(weather)) {
System.out.println(name + "披着被子去上班");
}else if ("狂风".equals(weather)) {
System.out.println(name + "拖着大石头上班!");
}
}


5.6 气象局维护上班族

试着在Client中创建Employee的对象。。。

发现在Client中不能addEmployee!

观察上班族和学生,他们都有一个共同的特点:

看天气预报!根据天气不同,作出不同的反应!

这属于行为——可以抽出一个接口(面向接口编程而不是实现类)

5.7 抽取被通知者

public interface Observable {
public void notifyWeather(String weather);
}

之后,在气象局中使用多态即可。

5.8 新增天气预报软件,抽取天气通知类型(观察者)

无需多言,与上面的思路完全一致。

6. 实际应用

其实,Java的GUI实现的组件事件监听,本质就是观察者模式。

----当某个按钮被点击时,要通知另一些组件作出反应(如点击全选,所有复选框被选中)

但发现:GUI的事件监听机制是使用匿名内部类!

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("我被点击了。。。");
}
});

这种设计思路是:只要被通知的对象符合一定的特征即可!

这不就是匿名内部类的使用思路吗???(突然滑稽)

7. Java中的观察者模式支持

翻开java.util包,可以发现一个类和一个接口:

Observer类,Observable接口





实现Observable接口的,都是被通知对象。

Observer本身就可以通知,当然可以继承Observer类,扩展功能。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式