Java 设计模式 -- 观察者模式

学校里有个十分可爱的女孩子,很多男孩都纷纷加入了她的粉丝行列,大家争先恐后的去搜集她的信息。这个时候女孩的室友,同学都开始向她抱怨,她也深感愧疚,感觉麻烦了周围的朋友。于是,她决定公开自己的状态,前提是不再去麻烦她身边的人。开始,她公开自己每天吃饭和学习的状态,只要她的粉丝在她这里进行注册,就可以不费吹灰之力的收到自己的动态信息,这对于粉丝而言简直是个天大的好消息...这个时候一个程序员路过,看到了这个现象,灵光一闪,这是观察者模式。首先,将女孩抽象成一个类,她的行为我们也可以写成方法,于是有了下面这两个类。

public interface Girl {
    public void addFans(Fans fans);

    public void removeFnas(Fans fans);

    public void notifyFans();
}

public class LovelyGirl implements Girl {
    private String eatState = "Inexact";
    private String studyState = "Inexact";
    private ArrayList<Fans> fanses;

    public LovelyGirl() {
        fanses = new ArrayList<>();
    }

    @Override
    public void addFans(Fans fans) {
        fanses.add(fans);
    }

    @Override
    public void removeFnas(Fans fans) {
        int i = fanses.indexOf(fans);
        if (i >= 0) {
            fanses.remove(i);
        }
    }

    @Override
    public void notifyFans() {
        for (int i = 0; i < fanses.size(); i++) {
            Fans fans = fanses.get(i);
            fans.update(eatState, studyState);
        }
    }

    public void setEatState(String eatState) {
        this.eatState = eatState;
        notifyFans();
    }

    public void setStudyState(String studyState) {
        this.studyState = studyState;
        notifyFans();
    }
}

这里,将女孩写成了一个接口。毕竟,人总会见异思迁的嘛,万一学校里来了个很漂亮的女孩子呢?那恐怕就要多出一个 BeautyGirl 类了。讲完了女孩,我们也得讲讲那些粉丝吧,粉丝有很多,他们的目的也只有一个 ——获取自己心仪女孩的最新信息。那好吧,为了让你们和女神保持一致,也为你们写一个接口吧。

public interface Fans {
    public void update(String eatState, String studyState);
}

有了接口之后,众多的粉丝们就可以实现这个接口,去获得最新信息了。比如说粉丝甲和粉丝乙——

public class FansOne implements Fans {

    @Override
    public void update(String eatState, String studyState) {
        System.out.println("I am fans one : " + eatState + ", " + studyState);
    }
}
public class FansTwo implements Fans {

    @Override
    public void update(String eatState, String studyState) {
        System.out.println("I am fans two : " + eatState + ", " + studyState);
    }
}

好了,所有准备工作已经完成了,不过这个时候已经中午了,女孩说了,我要开始吃饭了,比如像下面这样

public class Client {
    public static void main(String[] args) {
        LovelyGirl girl = new LovelyGirl();

        FansOne fansOne = new FansOne();
        girl.addFans(fansOne);

        FansTwo fansTwo = new FansTwo();
        girl.addFans(fansTwo);

        girl.setEatState("I am eating");
    }
}

打印结果如下

I am fans one : I am eating, Inexact
I am fans two : I am eating, Inexact

其实,上面这么一个例子就是我们常说的观察者模式。观察者模式定义了对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖者都会收到通知并且自动更新。生活中很多地方用到这种模式,比如说天气预报订阅系统,报纸订阅系统等。

上面这个例子还有可以修改之处,因为我们只写了推送功能,如果粉丝们想主动获取信息呢?如果有的粉丝对你的学习状态不感兴趣并且表示再也不想收到关于学习的信息推送了。那么该怎么进行改进呢?正好借这个机会,我们来看看 Java 为我们内置的观察者模式。Java 为我们提供了 Observer 接口和 Observable 类,下面我们来介绍一下如何使用。

  • 如何把对象变成观察者 ?
    实现观察者接口,然后调用 Observable 对象的 addObserver() 方法。不在想当观察者对象的时候,调用 deleteObserver() 方法就可以了。

  • 可观察者如何送出通知 ?
    首先,需要继承 java.util.Observable 类产生可观察者类,然后需要两个步骤。

    1. 先调用 setChanged() 方法,标记状态已经改变的事实。
    2. 然后调用两种 notifyObservers() 方法中的一个 : notifyObservers() 或者notifyObservers(Object arg)
  • 观察者如何接受通知?
    观察者实现了更新的方法,但是方法的签名不太一样 : update(Observable o, Object arg)。 这个时候就可以实现两种不同的效果。如果我们将 Object 属性不指定,也就是只传递 Observable 对象的值,这个时候就是默认不推送给观察者任何信息,不过观察者可以通过被观察者提供的 get 方法主动获取消息(这里也暗示了一个信息,就是我们的观察者内部需要获得被观察者的引用)。如果这里我们指定了 Object 的对象,那么被观察者会将信息推送给观察者。

注意哦,以上我们提到的 update() 方法中的 Object 参数和被观察者中notifyObservers(Object o) 中的 Object 是对应的。

利用以上介绍的信息,去改写以上的代码。

public class LovelyGirl extends Observable {
    private String eatState = "Inexact";
    private String studyState = "Inexact";

    public void setEatState(String eatState) {
        this.eatState = eatState;

    }

    public void setStudyState(String studyState) {
        this.studyState = studyState;
    }

    public String getEatState() {
        return eatState;
    }

    public String getStudyState() {
        return studyState;
    }

    public void stateChanged() {
        setChanged();
        notifyObservers();
    }
}

可见,在这里我们使用了无参数的 notifyObservers() 方法,这也就意味着,我们采用了不主动推送的方式。还有这里的 setChanged(),是 Observable 类中实现的一个方法,这个方法的作用是标记状态已经改变的事实,只有调用了这个函数,被观察者才会将最新的状态通知观察者。

public class FansOne implements Observer {

    private Observable observable;

    public FansOne(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof LovelyGirl) {
            LovelyGirl girl = (LovelyGirl) o;
            System.out.println("eatState : " + girl.getEatState() +
                    " studyState : " + girl.getStudyState());
        }
    }
}

可见,这里我们就将 addObserver() 方法写到了这里,因为我们既然在观察者中引用了被观察者对象,也就没有必要将添加的方法放入主函数了。这个时候再看看主函数的代码以及输出结果

public class Client {
    public static void main(String[] args) {
        LovelyGirl girl = new LovelyGirl();
        FansOne fansOne = new FansOne(girl);

        girl.setEatState("I am eating...");
    }
}


eatState : I am eating... studyState : Inexact

可见,利用了Java 内置的观察者模式之后,我们自己动手写的代码量就就减少了许多,而且逻辑也更加清晰了。不过我感觉 Java 内部将 Observable 设置为一个类还是有缺点的,因为Java 并不允许多重继承,这就限制了 Observable 的复用能力~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容