设计模式之观察者模式

164 阅读5分钟

老铁们,大家好啊,今天风哥带大家聊聊 观察者模式(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

好了,本次分享就到这了。如果你觉得对你有帮助,麻烦动动手指三连啊,感谢,下次见。