观察者模式

2020-04-20  本文已影响0人  LENN123

前言

观察者(Observer)模式是一种使用频率很高的设计模式,之前在介绍Javax.Servlet包下的Listener的时候,我们知道Listener能够在某一特定事件发生的时候(比如Servlet容器的创建和销毁)作出响应。为了完成这个功能,Listener就采用了观察者模式。

场景

假设我们现在有一个闹钟类Clock,当把它开启的时候它就会在一个特定的时间闹铃,如果类Son听到这个闹钟的铃声,就会起床去上学。为了模拟这个场景,我们可以简单的写下如下代码。

class Clock {
    private volatile boolean ring;
    public void turnOn() {
        System.out.println("闹钟响了");
        ring = true;
    }
    public boolean isRing(){
        return ring;
    }
}
class Son {
    public void goToSchool(){
        System.out.println("孩子起床上学");
    }
}
public class ObserverTest {
    public static Clock clock = new Clock();
    public static void main(String[] args) {
        Son son = new Son();

        new Thread(()->{
            try{
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clock.turnOn();
        }).start();

        while (!clock.isRing()) {
            // wait
        }
        son.goToSchool();
    }
}
闹钟响了
孩子起床上学

Process finished with exit code 0

这个实现方式很简单,Son一直轮询判断闹钟有没有响就可以了。缺点也很显然,沦陷的时候一直在空转,占用cpu时间,造成了资源浪费。那有什么办法可以不用空转去轮询状态呢?我们可以让闹钟闹铃的时候直接通知Son起来处理就好了,也就是让son监听clock转换为clock通知son

class Clock {
    private volatile boolean ring;
    private Son son = new Son();
    public void turnOn() {
        System.out.println("闹钟响了");
        ring = true;
        son.goToSchool();
    }
    public boolean isRing(){
        return ring;
    }
}

现在我们把son捆绑在clock上,当clockturn on方法一调用就立刻调用songoToSchool()方法。现在可以说我们把son注册进了clock,使其成为clock的观察者。观察者模式到这里就结束了吗? 显然没有,考虑到我们对于一个事件并不是只有一个观察者,假设在这个场景下,闹钟一响,不仅儿子要起床上学,父亲也要起床上班,母亲则需要出门买菜,也就是说对于闹钟闹铃这个事件,有3个观察者,则上述代码要改成如下。

class Father {
    public void goToWork(){
        System.out.println("爸爸起床上班");
    }
}
class Mother {
    public void goBuyFood(){
        System.out.println("妈妈出门买菜");
    }
}
class Son {
    public void goToSchool(){
        System.out.println("孩子起床上学");
    }
}
class Clock {
    private volatile boolean ring;
    private Son son = new Son();
    private Father father = new Father();
    private Mother mother = new Mother();
    public void turnOn() {
        System.out.println("闹钟响了");
        ring = true;
        son.goToSchool();
        father.goToWork();
        mother.goBuyFood();
    }
    public boolean isRing(){
        return ring;
    }
}

添加了FatherMother类之后,我们还要把它们写死进Clock代码里,此外还要明确每个观察者需要被执行的方法。所有的观察者和Clock这个被观察对象紧紧的耦合在一起了。代码的扩展性特别差。何如修改才能让其真正成为观察者模式呢?我们知道约定优于配置,既然我们不知道每一个观察者需要被调用的具体方法,那我们就约定一个方法叫做doSomething(),所有的观察者只要实现某个接口实现该方法就可以了,此外有了这个接口,我们可以将所有的观察者对象看成一类,在被观察对象类中提供一个注册接口就可以了。让我们按照这个思路来进行优化。

interface Observer {
    public void doSomething();
}

class Father implements Observer{
    public void goToWork(){
        System.out.println("爸爸起床上班");
    }

    @Override
    public void doSomething() {
        this.goToWork();
    }
}

class Mother implements Observer {
    public void goBuyFood(){
        System.out.println("妈妈出门买菜");
    }

    @Override
    public void doSomething() {
        goBuyFood();
    }
}
class Son implements Observer{
    public void goToSchool(){
        System.out.println("孩子起床上学");
    }

    @Override
    public void doSomething() {

    }
}
class Clock {
    private volatile boolean ring;
    private List<Observer> observers = new ArrayList<>();
    public void addObserver(Observer observer){
        observers.add(observer);
    }
    public void turnOn() {
        System.out.println("闹钟响了");
        ring = true;
        for (Observer observer : observers) {
            observer.doSomething();
        }
    }
    public boolean isRing(){
        return ring;
    }
}
public class ObserverTest {
    public static Clock clock = new Clock();
    public static void main(String[] args) {
        
       clock.addObserver(new Son());
       clock.addObserver(new Father());
       clock.addObserver(new Mother());
       
       clock.turnOn();

    }
}
闹钟响了
爸爸起床上班
妈妈出门买菜
上一篇下一篇

猜你喜欢

热点阅读