Java设计模式之观察者模式

观测者模式常用于实现订阅功能的场景,比如说微博的订阅,消息订阅等。我们订阅了它们,它们在有更新的时候就会给我们推送它们的更新信息,这样我们就不用每次都要去刷新它的状态了。观察者模式它定义了一种一对多的依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。假如一个对象的状态更新,需要其他对象也同步更新的时候,而且这些其他对象可以动态改变,这个时候我们的观测者模式就派上用场类。
我们看看类图(图片来自《Head First 设计模式》)
这里写图片描述
下面以我们日常订报纸为例,写个demo来更加细化地了解观察者模式是怎么回事。

观察者模式的简单实现

报纸的订阅很简单,报社出版报纸,每天更新报纸的内容,我们订阅了报纸,就会每天有人送报纸过来(当然你也可以自己去拿,这涉及到消息的推送方式,是你给我,还是我自己拿,这一会再讨论),从报社订报纸的人有人多,取消订阅的人也很多,因此订阅者是可以动态变化的。折射到我们观察者模式,出版报纸的报社就称为”Subject”主题,订阅报纸的人则称为”Observer”观察者。 根据上面的类图,我们开始实现这一订阅报纸的流程。
1.先建立接口

package Observer;

/**
 * Created by gray on 2017/8/13.
 */
public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObserver();
}
package Observer;

/**
 * Created by gray on 2017/8/13.
 */
public interface Observer {
    public void update(String context);
}

2.实现接口
对于报社我们要实现的是Subject接口

package Observer;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by gray on 2017/8/13.
 */
public class Newspaper implements Subject {
    private List<Observer> list;
    private String context;
    public Newspaper() {
        list = new ArrayList<>();
    }

    @Override
    public void registerObserver(Observer o) {
        list.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        int i = list.indexOf(o);
        if (i >= 0) {
            list.remove(i);
        }
    }

    @Override
    public void notifyObserver() {
        if (list!=null && list.size()>0){
            for (Observer o : list){
                o.update(this.context);
            }
        }
    }

    public void updateContext(String context){
        this.context = context;
        notifyObserver(); // 有更新就通知
    }
}

对于订阅者我们实现Observer接口

package Observer;

/**
 * Created by gray on 2017/8/13.
 */
public class Subscriber implements Observer {
    private String context;
    private Subject subject;
    public Subscriber(Subject subject){
        this.subject = subject;
        subject.registerObserver(this);
    }
    @Override
    public void update(String context) {
        this.context = context;
        System.out.println("这里是订阅者1,今天收到的报纸内容是:"+context);
    }
}

好了,现在来做个简单的测试吧

package Observer;

/**
 * Created by gray on 2017/8/13.
 */
public class Test {
    public static void main(String args[]){
        Newspaper n = new Newspaper();
        Subscriber s = new Subscriber(n);
        String str1 = "今天是8月1号,发生了xxxx事情...";
        n.updateContext(str1);
        String str2 = "今天是8月2号,发生了XXXX事情...";
        n.updateContext(str2);
        String str3 = "今天是8月3号,发生了XXXXXXX事情...";
        n.removeObserver(s); //取消订阅
        n.updateContext(str3);
        String str4 = "今天是8月4号,发生了xxxxxx事情...";
        n.registerObserver(s); //订阅
        n.updateContext(str4);
    }
}

上面的测试程序中,3号的报纸我取消类订阅,4号的报纸我又重新订阅,执行结果如图所示:
这里写图片描述
这样,简单的观察者模式就这样实现了。
这样,上面所展示的观察者模式是主动给我们推数据,那我们要自己拉数据该怎么做?我们看看Java API内置的观测者模式是怎么运作的。我们对以上例子做个简单的修改。
1.实现SUbject接口改成继承java.util.Observable类

package Observer;

import java.util.Observable;

/**
 * Created by gray on 2017/8/13.
 */
public class Newspaper2 extends Observable {
    private String context;
    public Newspaper2(){}
    public void updateContext(String context){
        this.context = context;
        setChanged(); // 通知前调用指示状态发生了改变
        notifyObservers();
    }

    public String getContext() {
        return context;
    }
}

订阅者实现java.util.Observer接口

package Observer;

import java.util.Observable;
import java.util.Observer;

/**
 * Created by gray on 2017/8/13.
 */
public class Subscriber2 implements Observer {
    private Observable observable;
    private String context;
    public Subscriber2(Observable observable){
        this.observable = observable;
        this.observable.addObserver(this);
    }
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof Newspaper2) {
            this.context = ((Newspaper2) o).getContext(); // 这里采用的拉的方式
            System.out.println("这里是订阅者1,今天收到的报纸内容是:"+ this.context);
        }
    }

    public void removeSubscriber(){
        this.observable.deleteObserver(this);
    }

    public void subscriber(){
        this.observable.addObserver(this);
    }
}

接下来我们做第二个测试,测试代码修改如下:

Newspaper2 n = new Newspaper2();
Subscriber2 s = new Subscriber2(n);
String str1 = "今天是8月1号,发生了xxxx事情...";
n.updateContext(str1);
String str2 = "今天是8月2号,发生了XXXX事情...";
n.updateContext(str2);
String str3 = "今天是8月3号,发生了XXXXXXX事情...";
s.removeSubscriber(); //取消订阅
n.updateContext(str3);
String str4 = "今天是8月4号,发生了xxxxxx事情...";
s.subscriber(); //订阅
n.updateContext(str4);

测试结果与第一种一样
这里写图片描述

总结一下:java内置的API所实现的观察者模式有一个坏处首先Observable它是个类,继承总没有实现接口复用性好。反正萝卜青菜各有所爱,无论是哪种实现方式,也无论是推的方式还是拉的方式,取决于你的应用场景。个人觉得如果你要的数据很复杂,那就采用推的方式,如果简单那就推的拉的都可以。
以上就是我对《Head First 设计模式》中关于观察者模式的一些理解及参考书上的例子自己写的一些例子。记录一下方便今后复习。详细代码见github

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值