[设计模式] 观察者模式
手机用户请
横屏
获取最佳阅读体验,REFERENCES
中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。
平台 | 地址 |
---|---|
CSDN | https://blog.csdn.net/sinat_28690417 |
简书 | https://www.jianshu.com/u/3032cc862300 |
个人博客 | https://yiyuery.github.io/NoteBooks/ |
观察者模式定义了对象之间的一对多依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并更新。
对于观察模式的实现方式有很多种,一般是以包含Subject和Observer接口的类设计做法最为常见。
根据多个观察者对于同一主题信息的获取方式,可以分为:
- 被观察者(主题)主动推送,一变化,就将被观察对象推送给所有观察者。
- 观察者通过接口主动拉取主题中的部分信息,避免无关信息的干扰。
观察者模式的好处:
- 提供了一种对象设计,让主题和观察者之间松耦合
- 解决一对多的依赖问题
场景分析
加入我们现在有个手机工厂,每次生产出一批手机后,就需要通知对应的代理商过来拿货。
我们就可以利用观察者模式来实现这个场景需求。
- 手机工厂就是被观察对象,也就是主题。
- 多个代理商分别充当不同的观察者对象。需要监听手机工厂的 生产情况。
- 一旦手机工厂生产出手机,就会通知我们的代理商过来取货。
实战
类图
- 首先,我们需要定义观察者和被观察者接口,并定义通知能力接口。
public interface Observer<T> {
/**
* 观察者接收通知
*/
void handle(T message);
}
public interface Subject<T> extends INotify{
/**
* 注册观察者
* @param observer
*/
void register(Observer<T> observer);
/**
* 注销观察者
* @param observer
*/
void cancel(Observer<T> observer);
}
public interface INotify {
/**
* 通知
*/
void sendMessage();
}
- 然后,定义对应实例实现观察者和被观察者
手机工厂
public class PhoneFactory implements Subject<Message> {
private List<Observer<Message>> observers;
/**
* 消息体
*/
private Message message;
public PhoneFactory() {
this.observers = new ArrayList<>();
}
/**
* 注册观察者
*
* @param observer
*/
@Override
public void register(Observer<Message> observer) {
observers.add(observer);
}
/**
* 注销观察者
*
* @param observer
*/
@Override
public void cancel(Observer<Message> observer) {
observers.remove(observer);
}
/**
* 发送通知
*/
@Override
public void sendMessage() {
observers.forEach(p->{
p.handle(message);
});
}
/**
* 外部设置消息变更
* @param message
*/
public void setMessage(Message message) {
this.message = message;
sendMessage();
}
}
消息体
@Data
@Builder
@AllArgsConstructor
public class Message {
/**
* 消息体
*/
private String data;
}
代理商
//淘宝
public class TaobaoAgent implements Observer<Message> {
/**
* 观察者接收通知
*
* @param message
*/
@Override
public void handle(Message message) {
System.out.println("Taobao receive message: " message.getData());
}
}
//天猫
public class TianmaoAgent implements Observer<Message> {
/**
* 观察者接收通知
*
* @param message
*/
@Override
public void handle(Message message) {
System.out.println("Tianmao receive message: " message.getData());
}
}
- 最后,定义测试类,我们来看下效果
/**
* 自定义观察者模式
*/
@Test
public void testX1() {
//被观察者
PhoneFactory phoneFactory = new PhoneFactory();
//观察者
TianmaoAgent tianmaoAgent = new TianmaoAgent();
TaobaoAgent taobaoAgent = new TaobaoAgent();
//注册
phoneFactory.register(tianmaoAgent);
phoneFactory.register(taobaoAgent);
//发送通知
phoneFactory.setMessage(Message.builder()
.data("刚生产了100台Xiaomi手机")
.build());
//移除观察者
phoneFactory.cancel(tianmaoAgent);
//再次发送通知
System.out.println("-------------------");
phoneFactory.setMessage(Message.builder()
.data("刚又生产了100台iPhone")
.build());
}
打印
Tianmao receive message: 刚生产了100台Xiaomi手机
Taobao receive message: 刚生产了100台Xiaomi手机
-------------------
Taobao receive message: 刚又生产了100台iPhone
从输出可以看出,观察者和被观察者有以下几个特点:
- 观察者可以随时进行注册和注销行为
- 被观察者只会给注册过的观察者发送通知
- 不同代理商接收通知的顺序和自己注册的先后有关系
那么思考下这个模式有没有什么不好的地方?
- 每次被观察者发送消息,观察者都需要全部接收并处理。如果信息比较复杂或是观察者只关注其中一部分数据怎么处理?
- 这种属于被动等待被观察者推的行为,如果不是通知,而是其他一些实时数据的监控呢?如果观察者不想等待,想直接获取当前实时数据信息呢?
- JDK内置了观察者模式的实现,为什么还要自己定义呢?与自定义的有什么区别呢?
带着问题,我们来看下JDK内置的观察者模式怎么实现的?并看下如何实现主动拉取被观察者信息。
代码语言:javascript复制JDK内置观察者模式实现
public class PhoneFactory2 extends Observable {
/**
* 消息体
*/
private Message message;
public void setMessage(Message message) {
this.message = message;
setChanged();
notifyObservers(message);
}
/**
* 观察者按需主动拉取自己所需要信息
* @return
*/
public Message getMessage() {
return message;
}
}
public class TaobaoAgent2 implements Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
*/
@Override
public void update(Observable o, Object arg) {
System.out.println("Taobao2 receive message: " arg.toString());
}
}
public class TianmaoAgent2 implements Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
*/
@Override
public void update(Observable o, Object arg) {
System.out.println("Tianmao2 receive message: " arg.toString());
}
}
比较下变化:
- 首先代码量上减少了,不需要定义接口
- notifyObservers方法不用实现,直接使用父层方法
- 使用的是继承的方式实现观察者和被观察者
- 定义了getMessage方法,可以主动拉取信息
- 发送通知前,需要调用父层方法setChanged
定义下测试类:
代码语言:javascript复制/**
* 使用JDK原生观察者模式
*/
@Test
public void testX2() {
//被观察者
PhoneFactory2 phoneFactory = new PhoneFactory2();
//观察者
TianmaoAgent2 tianmaoAgent = new TianmaoAgent2();
TaobaoAgent2 taobaoAgent = new TaobaoAgent2();
//注册
phoneFactory.addObserver(tianmaoAgent);
phoneFactory.addObserver(taobaoAgent);
//发送通知
phoneFactory.setMessage(Message.builder()
.data("刚生产了100台Xiaomi手机")
.build());
//移除观察者
phoneFactory.deleteObserver(tianmaoAgent);
//再次发送通知
System.out.println("-------------------");
phoneFactory.setMessage(Message.builder()
.data("刚又生产了100台iPhone")
.build());
//主动拉取消息
System.out.println("pull message:" phoneFactory.getMessage());
}
看下测试类的输出:
代码语言:javascript复制Taobao2 receive message: Message(data=刚生产了100台Xiaomi手机)
Tianmao2 receive message: Message(data=刚生产了100台Xiaomi手机)
-------------------
Taobao2 receive message: Message(data=刚又生产了100台iPhone)
pull message:Message(data=刚又生产了100台iPhone)
分析下两次输出的区别:
- 接收消息的顺序反了
- 被观察者开放了自身被监控属性的Getter方法
其实乍看之下,两种方式实现结果貌似没什么区别,但是仔细观察JDK内置的Observable,不是一个接口,是个类。其内部的setChanged方法也是被 protected
修饰的
protected synchronized void setChanged() {
changed = true;
}
这意味着:你除非继承自Observable,否则无法创建Observable实例,并组合到你自己的对象中来,这个设计违反了 多用组合,少用继承
的设计原则。
相较于我们自己实现的观察者模式,可以结合泛型定义被通知的消息体,还可以结合我们的通知接口 INotify
等方式来扩展,开发自由度更加高。但是无论是哪种方式,关键是熟悉观察者模式后,善用即可,
REFERENCES
《Head First》读书笔记