移动架构学习笔记八:化主动为被动的观察者模式

 

目录

观察者模式

角色

1、抽象主题(Subject):

2、具体主题(Concrete Subject):

3、抽象观察者(Observer):

4、具体观察者(Concrete Observer):

案例

总结


  • 观察者模式

观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。

  • 角色

1、抽象主题(Subject):

它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

2、具体主题(Concrete Subject):

将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

3、抽象观察者(Observer):

为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

4、具体观察者(Concrete Observer):

实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

  • 案例

观察者模式是我们工作中最常用到的模式,也是最好从字面意思去理解的一个模式。我们生活中都会遇到这样一个场景,当我们有了小孩之后,会有一件麻烦事,小孩子一天中有大部分时间都在睡觉,而当他睡醒之后就得吃奶。那作为父亲,我们需要随时随地观察小孩有没有睡醒,醒来后立马冲奶粉喂奶。就这么一件事,我们用代码怎么解决呢?

刚参加工作的菜鸟程序员会这么写:

public class Main {
    public static void main(String[] args) {
        boolean cry = false;
        while (!cry){
            System.out.println("wait for child cry.......");
        }
        System.out.println("Child is crying.......");
    }
}

 哈哈,一个while循环,死死的盯住小孩,等小孩一醒立马跳出循环进行下一步操作。这应该是一段辣眼睛的代码,其他不说,这是典型的面向过程编程,不是面向对象。

等菜鸟程序员工作半年之后,终于懂了面向对象编程是怎么一回事,于是写出来如下代码:

public class Child {
    private boolean cry;

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Child is crying.......");
        cry = true;
    }
}

 

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        while (!child.isCry()){
            System.out.println("wait for child cry.......");
        }
        System.out.println("Child is crying.......");
    }
}

 一段换汤不换药的代码,姑且认为他已经面向对象了吧。可是作为小孩的爸爸,有没有必要24小时盯着小孩,什么工作都不做,就等着喂奶呢?显然是没有必要的!

上面的观察是主动出击,做为观察者,被观察者走到哪里我跟到哪里,二十四小时形影不离。这种方式的观察显然浪费大量的资源。这时候我们需要化主动为被动,我们在被观察者身上安装一个监视器,有动静我们再行动也不迟。

于是菜鸟程序员在工作了一年之后,明白了这个道理,于是改进了之前的代码:

public class Dad {
    public void food(){
        System.out.println("小孩醒了,爸爸喂奶奶。。。。");
    }
}
public class Child {
    private boolean cry;
    private Dad dad;

    public Child() {
        dad = new Dad();
    }

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Child is crying.......");
        cry = true;
        dad.food();
    }
}
public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.wakeUp();
    }
}

 化主动为被动,小孩醒后的哭声来通知爸爸喂奶,其余时间爸爸可以做其他工作。菜鸟虽然进步了,但还是菜鸟,这段代码显然耦合度太高,不利于扩展。试想一下,这时观察者不只一个怎么办,家了爸爸、妈妈和狗狗都想观察小孩,代码是不是会变成这样:

public class Child {
    private boolean cry;
    private Dad dad;
    private Mum mum;
    private Dog dog;

    public Child() {
        dad = new Dad();
        mum = new Mum();
        dog = new Dog();
    }

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Child is crying.......");
        cry = true;
        dad.food();
        mum.hug();
        dog.wang();
    }
}

 工作超过三年的程序员,应该一眼就能看出上面代码的问题:现在观察者是三个,要是变成一百个怎么办?如果要方便扩展,代码应该这么写呢?三年以上经验的程序员会这么写:

public interface Observer {
    void handleWakeEvent();
}
public class Dad implements Observer {
    @Override
    public void handleWakeEvent() {
        System.out.println("小孩醒了,爸爸喂奶奶。。。。");
    }
}
public class Mum implements Observer {
    @Override
    public void handleWakeEvent() {
        System.out.println("小孩醒了,妈妈抱抱。。。。");
    }
}
public class Dog implements Observer {
    @Override
    public void handleWakeEvent() {
        System.out.println("小孩醒了,小狗汪汪。。。");
    }
}

爸爸、妈妈和狗狗本质上拥有同样一种行为:观察宝宝睡觉。于是这样一种行为可以抽象成一个接口,爸爸、妈妈和狗狗再实现这个接口,于是他们成为了一个拥有观察行为的观察者。

对于小孩来说,我只通知拥有观察行为的观察者,于是可以这样写:

public class Child {
    private boolean cry;
    private List<Observer> observers = new ArrayList<>();

    public Child() {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Child is crying.......");
        cry = true;
        for (Observer observer : observers) {
            observer.handleWakeEvent();
        }
    }
}

 感受到接口的好处了吗?这才是观察者模式,不管你三个还是一百个,统统给你通知到位,方便扩展。

当然有时候被观察者在通知观察者的时候,是需要携带一些信息的,也就是传参数。这时候我们要新增一个用于传递参数的事件类,把事件源(小孩)和时间发生时间传递给观察者:

public class WakeUpEvent {
    private long time;
    private Child child;

    public WakeUpEvent(long time, Child child) {
        this.time = time;
        this.child = child;
    }

    public long getTime() {
        return time;
    }

    public Child getChild() {
        return child;
    }
}

 同时改变观察者和被观察者的写法:

public interface Observer {
    void handleWakeEvent(WakeUpEvent event);
}

 

public class Child {
    private boolean cry;
    private List<Observer> observers = new ArrayList<>();

    public Child() {
        observers.add(new Dad());
        observers.add(new Mum());
        observers.add(new Dog());
    }

    public boolean isCry() {
        return cry;
    }

    public void wakeUp() {
        System.out.println("Child is crying.......");
        cry = true;
        WakeUpEvent event = new WakeUpEvent(System.currentTimeMillis(), this);
        for (Observer observer : observers) {
            observer.handleWakeEvent(event);
        }
    }
}

 这样就实现了被观察者和观察者之间传递参数了,这时,程序场上的老鸟们肯定会发现一个问题,被观察者与事件之间产生了耦合,WakeUpEvent里面关联了Child。这也减低了程序的扩展性,假如观察者更换了观察对象,WakeUpEvent岂不是就不能用了吗?这时老鸟们站出来润色润色,新增一个接口:

public interface Subject {
}

 

public class WakeUpEvent {
    private long time;
    private Subject source;

    public WakeUpEvent(long time, Subject source) {
        this.time = time;
        this.source = source;
    }

    public long getTime() {
        return time;
    }

    public Subject getSource() {
        return source;
    }
}

 

public class Child implements Subject {
    private boolean cry;
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void wakeUp() {
        System.out.println("Child is crying.......");
        cry = true;
        WakeUpEvent event = new WakeUpEvent(System.currentTimeMillis(), this);
        for (Observer observer : observers) {
            observer.handleWakeEvent(event);
        }
    }
}

 看出老鸟们的高明之处了吗?看似增加了一个无意义的接口,但却在谈笑间化解了WakeUpEvent和Child之间的耦合。这时WakeUpEvent不光可以用于观察小孩睡觉,也可用于观察爷爷奶奶睡觉。

到此为止,观察者模式终于理解清楚了,也完成了从菜鸟到老鸟的进化。程序场上的菜鸟们终于明白老鸟的十年工作经营可不是白混的。

  • 总结

观察者模式是工作中最最常用的模式,Android中的触摸事件、按键事件、点击事件等等,无一不是通过观察者模式来实现的。所以充分理解观察者模式有利于我们从程序场菜鸟到老鸟的转变。

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值