设计模式系列总览
设计模式 | 飞机票 |
---|---|
三大工厂模式 | 登机入口 |
策略模式 | 登机入口 |
委派模式 | 登机入口 |
模板方法模式 | 登机入口 |
观察者模式 | 登机入口 |
单例模式 | 登机入口 |
原型模式 | 登机入口 |
代理模式 | 登机入口 |
装饰者模式 | 登机入口 |
适配器模式 | 登机入口 |
建造者模式 | 登机入口 |
责任链模式 | 登机入口 |
享元模式 | 登机入口 |
组合模式 | 登机入口 |
门面模式 | 登机入口 |
桥接模式 | 登机入口 |
中介者模式 | 登机入口 |
迭代器模式 | 登机入口 |
状态模式 | 登机入口 |
解释器模式 | 登机入口 |
备忘录模式 | 登机入口 |
命令模式 | 登机入口 |
访问者模式 | 登机入口 |
软件设计7大原则和设计模式总结 | 登机入口 |
什么是观察者模式
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通 知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式,主要用 于在关联行为之间建立一套触发机制的场景
观察者模式示例
假如我们需要从气象台获取天气信息用作展示,当天气信息发生改变之后,我们也需要同步发生改变。
1、建立一个Subject接口
package com.zwx.design.pattern.observe.headfirst;
public interface Subject {
void registerObserver(Observer o);//注册观察对象
void removeObserver(Observer o);//移除观察对象
void notifyObservers();//通知观察对象
}
2、建立一个WeatherData(被观察者)实现Subject接口(面向接口编程会更易于扩展,这里也可以不用Subject接口,直接建立WeatherData实现)
package com.zwx.design.pattern.observe.headfirst;
import java.util.ArrayList;
import java.util.List;
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;//温度
private float humidity;//湿度
private float pressure;//气压
public void setMessurements(float temperature,float humidity,float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers();//气温信息发生变化时,通知所有观察者
}
public WeatherData() {
this.observers = new ArrayList<>();
}
/**
* 注册观察者
* @param o
*/
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
/**
* 移除观察者
* @param o
*/
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i >= 0){
observers.remove(i);
}
}
/**
* 通知所有观察者
*/
@Override
public void notifyObservers() {
for (Observer observer : observers){
observer.update(temperature,humidity,pressure);
}
}
}
3、建立一个***观察者***接口
package com.zwx.design.pattern.observe.headfirst;
public interface Observer {
void update(float temperature,float humidity,float pressure);
}
这个接口只定义了一个update方法,用于更新气象数据
4、建立一个WeatherDisplay(观察者)类实现Observer接口
package com.zwx.design.pattern.observe.headfirst;
public class WeatherDisplay implements Observer {
private Subject subject;
private float temperature;//温度
private float humidity;//湿度
private float pressure;//气压
public WeatherDisplay(Subject subject) {//注册监听对象
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
}
public void display(){
System.out.println("当前最新的温度为:" + temperature + ",湿度为:" + humidity +
",气压为:" + pressure);
}
}
5、最后再写一个测试类测试:
package com.zwx.design.pattern.observe.headfirst;
public class WeatherDataTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();//气象数据即被观察者
WeatherDisplay weatherDisplay = new WeatherDisplay(weatherData);//天气展示即观察者
weatherData.setMessurements(37.2f,80f,32.5f);
weatherDisplay.display();//天气展示
}
}
最后输出结果为:
说明被观察者WeatherData气象发生变化时,观察者WeatherDisplay也及时获得了最新的数据变化,从而实现了一个简单的观察者模式!
上面的示例中存在一种问题,那就是数据是被push(推)过来的,也就是说不管观察者想不想要,只要气象发生变化了,被观察者就会把数据push(推)给观察者,那么能不能实现让观察者主动去pull(拉)呢?答案是肯定的,上面我们只要把WeatherData中每个数据都提供getter方法,然后再稍作改进就可以实现!
JDK中自带实现了观察者模式,并且实现了push(推)和pull(拉)两种类型,接下来就让我们用JDK自带的观察者模式来实现pull(拉)的场景。
JDK观察者模式使用
假设我们在空间中发了一条动态,需要把这条动态推送给好友
1、首先建立一个Zone类(被观察者)实现Observable类
package com.zwx.design.pattern.observe.trendsNotice;
import java.util.Observable;
public class Zone extends Observable {
public void publishTrends(Trends trends){
System.out.println(trends.getNickName() + "发表了一个动态【" + trends.getContent() + "】" );
setChanged();//仅作标识用
notifyObservers(trends);//通知所有观察者
}
}
注意这里的Observable是java.util包下面的,这个类里面定义了一个发表动态的方法。这里setChanged()仅仅只是把一个改变标识设置为true
之所以要这一个,是因为JDK中的notifyObservers方法中会判断这个标识是否为true,为true才会进行通知
2、定义一个动态类
package com.zwx.design.pattern.observe.trendsNotice;
public class Trends {
private String nickName;//发表动态的用户昵称
private String content;//发表的动态内容
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
3、定义一个Friends类(观察者),实现Observer接口
package com.zwx.design.pattern.observe.trendsNotice;
import java.util.Observable;
import java.util.Observer;
public class Friends implements Observer {
private String friendName;
public String getFriendName() {
return friendName;
}
public void setFriendName(String friendName) {
this.friendName = friendName;
}
@Override
public void update(Observable o, Object arg) {
Trends trends = new Trends();
if(null != arg && arg instanceof Trends){
trends = (Trends)arg;
}
System.out.println(this.getFriendName() + ",您好!您收到了来自" + trends.getNickName() +
"的一条动态【" + trends.getContent() + "】" + "快去点赞吧!");
}
}
注意这里的update方法中的第2个参数,如果上面Zone类中的方法publishTrends方法中调用的notifyObservers方法不传入参数trends,这里拿到的就是空,也就是相当于实现了push(推),传入了参数我们update中就能拿到数据对象,这时候我们就可以主动去pull(拉)数据,只选择自己想要的数据进行展示或者处理
4、最后写一个测试类测试
package com.zwx.design.pattern.observe.trendsNotice;
public class ObserverTest {
public static void main(String[] args) {
Zone zone = new Zone();
Friends friends = new Friends();
friends.setFriendName("张三丰");
Trends trends = new Trends();
trends.setNickName("张无忌");
trends.setContent("祝太师傅长命百岁!");
zone.addObserver(friends);
zone.publishTrends(trends);
}
}
输出结果为如下:
说明发表动态之后朋友收到了通知,且拿到了动态相关数据,实现了观察者模式pull(拉)数据
java.util.Observable的黑暗面
1、Observable是一个类而不是一个接口,所以就限制了它的使用和服用,如果某类同时想具有Observable类和另一个超类的行为,就会有问题,毕竟java不支持多继承。
2、Observable将关键的方法保护起来了,比如setChanged()方法,这意味着除非我们继承自Observable,否则无法创建Observable实例并组合到我们自己的对象中来,这个设计违反了设计原则:多用组合,少用继承。
观察者模式的有优点
1、观察者和被观察者之间建立了一个抽象的耦合。
2、观察者模式支持广播通信
观察者模式的有缺点
1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。 2、使用要得当,要避免循环调用。