老铁们,大家好啊,今天风哥带大家聊聊 观察者模式(Observer)。
顾名思义,就是有个观察者,观察到某个人发生了某些事情,就采取相应的动作。
观察者模式,大多用于对响应事件的处理,可以理解为一种消息发送机制。
我们考虑这样一件事情:
假设你发现你儿子干坏事了,那你一定要揍他吧。
如果这个场景要设计类去实现,你会怎么做?
最容易想到的实现方式 ,就是定义个 Son 类,然后去用循环去观察 Son 的状态。
版本一
Son.java
class Son{
private boolean doWrongThing;
public boolean isDoWrongThing(){
return this.doWrongThing;
}
public void doWrongThing(){
this.doWrongThing = true;
}
}
ObserverClient.java
public class ObserverClient {
public static void main(String[] args) {
Son s = new Son();
while(s.isDoWrongThing()){//观察
//开揍
}
}
}
这种方式,明显不太好。死循环也挺占 CPU 不是么。
版本二
我们看第二种写法,在 Son 类中聚合一个 被观察者 Dad 类,当发生事件时,调用相应的方法。
Son.java
class Son{
private Dad dad = new Dad();
private boolean doWrongThing;
public boolean isDoWrongThing(){
return this.doWrongThing;
}
public void doWrongThing(){
this.doWrongThing = true;
dad.beat();
}
}
Dad.java
class Dad{
public void beat(){
//看你想怎么揍了
}
}
这种方式,有个问题。
耦合度有点高,而且不太适合扩展(当需要多个观察者时)。对,看到扩展二字,你一定想到了接口,这不就是设计模式的本质嘛。
版本三
为了解决观察者和被观察者耦合度大的问题,我们引入了观察者接口,每个观察者都需要实现这个接口。
Son.java
这里,我们暂时用静态语句块增加所有的观察者。
class Son{
private static List<Observer> observers = new ArrayList();
static{
observers.add(new Dad());
observers.add(new Mum());
}
private boolean doWrongThing;
public boolean isDoWrongThing(){
return this.doWrongThing;
}
public void doWrongThing(){
this.doWrongThing = true;
for(Observer observer:observers){
observer.doSth();
}
}
}
Observer.java
interface Observer{
void doSth();
}
Dad.java
class Dad implements Observer{
public void doSth(){
System.out.println("揍");
}
}
Mum.java
class Mum implements Observer{
public void doSth(){
System.out.println("哄");
}
}
这里,有个问题,你儿子干坏事,你是每次都揍吗?不全是吧,还是得根据具体情况来吧。所以,应该向观察者传递一个事件对象,来描述被观察者的状态信息。
在版本四里,我们增加了一个 Event 事件对象。
版本四
相比于版本三,版本四增加 Event 对象,用来标识事件的具体信息。
在本示例中,我们用了两个成员变量:
- name ,事件内容。你儿子可能做了坏事,也可能做了好事,用这个值方便观察者采取不同的动作。
- Son , 事件的来源。你可能不光观察你儿子,还有你家的猫,可以根据事件来源采取不同的动作。
通过这样构造事件对象,观察者就和被观察者解耦了,它可以去观察更多事情了。
Event.java
class Event{
private String name;
private Son source;
public Event(String name,Son source){
this.name = name;
this.source = source;
}
public Son getSouce(){
return this.source;
}
public String getName(){
return this.name;
}
}
Son.java
根据被观察者不同的动作 doWrongThing 以及 doGoodThing,分别传入不同的事件对象。
同时,在被观察者中,增加 addListener 方法,传入监听者对象。
class Son{
private static List<Observer> observers = new ArrayList();
public void doWrongThing(){
Event e = new Event("doWrongSth",this);
for(Observer observer:observers){
observer.doSth(e);
}
}
public void doGoodThing(){
Event e = new Event("doGoodThing",this);
for(Observer observer:observers){
observer.doSth(e);
}
}
public void addListener(Observer o){
this.observers.add(o);
}
}
Observer.java
此时,在观察者方法上,传入事件对象。
interface Observer{
void doSth(Event e);
}
Dad.java
被观察者根据不同的事件类型,以及不同的事件源,采取不同的动作。
class Dad implements Observer{
public void doSth(Event e){
if("doWrongSth".equals(e.getName())
&&e.getSouce() instanceof Son){
System.out.println("揍");
}else{
//...
}
}
}
Mum.java
class Mum implements Observer{
public void doSth(Event e){
System.out.println("哄");
}
}
ObserverClient.java
创建对象,并增加观察者对象。
public class ObserverClient {
public static void main(String[] args) {
Son s = new Son();
s.addListener(new Dad());
s.addListener(new Mum());
s.doWrongThing();
}
}
运行一下,输出结果:
揍
哄
观察者模式的三要素
现在,我们知道了,观察者模式应该包含如下三要素:
- 被观察者(事件源)
- 事件
- 观察者
事件源对象里,增加监听器。
事件触发时,调用监听器的方法。
最后再看下观察者模式的类图。
观察者模式应用场景
看到上面的 addListener 方法,想必很多朋友会觉得似曾相识。
是的,观察者模式在 Java AWT 中应用非常广泛。
给某个按钮增加一个监听器,当按钮被按下时,触发某些操作。
在 AWT 中,同样有观察者模式的三要素:
- 事件源 Button
- 事件对象 ActionEvent
- 观察者 ActionListener
好了,本次分享就到这了。如果你觉得对你有帮助,麻烦动动手指三连啊,感谢,下次见。