设计模式-观察者(一)

设计模式-观察者(一)

大家好,我们又见面了,还记得上次所写的 <<设计模式-策略模式>>么?,我们今天来学习另一个模式之前,先复习一下上次的”策略模式”是怎么定义的吧:

定义:

策略模式定义:算法族,分别封装起来,让他们之间可以互相替换,次模式让算法的变化独立于使用算法的客户.

“策略模式”中我们还学到了两个设计原则:

  • 1 :找出应用中可能需要变化之处,把他们独立起来,不要和那些不需要变化的部分的代码混合在一起.
  • 2 :针对借口编程,而不是针对实现编程.

思考……………

经过对”策略模式”的思考,是不是有点印象了? 下面我们继续向下一个模式进发把……..

这次我们先推出这个模式,名字为”观察者模式”, 它的定义: 观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新.

不懂?

当然,如果能现在就能明白其真谛的话,你可以关浏览器了,以后都不需要再来GC博客了……(开玩笑 ^_^)


我们一步一步来,先了解一下什么是观察者模式.

*不知道你订没订阅报纸(如果你从小到大没订阅过报纸或者书刊杂志,我只能说你错过了一个生活体验..) 好吧,瞎扯了,我们先介绍一下订阅报刊的过程:
假如你家附近有一所报社,而你又是<<意林>>(高中生应该都读过的)的爱好者,你当然会迫不及待的去订阅一年属于自己的<<意林>>.当你去报社订阅了<<意林>>之后,每当报社收到新出版的<<意林>>,它就会将你的那份送到你的家中供你阅读.*

好吧.整个流程应该弄明白了吧,是在不明白可以亲自去订阅一年的<<意林>>体验一把哈.

到这里,可能你会想:订阅报刊与”观察者模式”有什么关系啊~

我只能回答你:其实订阅报刊整个流程就是"观察者模式"的整个流程,也就是说"订阅报刊"用了"观察者模式".
当然,如果你已经想到了,那么只能说你的智商真的是太高了(@_@)

报社就相当于”观察者模式”里的”发布者(publish)/主题(Subject)”,而你就相当于”观察者模式”里的”订阅者(Subscribe)/观察者(Observer)”

“观察者模式”介绍完毕~~~


骗你的,代码还没写,怎么可能介绍完毕呢.况且”观察者模式”有好多种,今天只介绍其一….

现在我们开始写代码吧…用代码来实现上面的”订阅报刊”的功能…

//定义报社类
public class NewspaperOffice  {

    //定义主题后期会更新的数据
    private int updatedata;

    //通知所有的<<意林>>订阅者
    @Override
    public void notifyObservers() {
           //订阅者1订阅了意林
            Subscriber1 s1 = new Subscriber1();
            //让订阅者1更新数据,也就是给订阅者1送去<<意林>>
            s1.update(this.updatedata);

            //订阅者2订阅了意林
            Subscriber1 s2 = new Subscriber2();
            //让订阅者2更新数据,也就是给订阅者2送去<<意林>>
            s2.update(this.updatedata);
   }

    //当主题里的数据进行更新的时候,通知所有的观察者
    public void setUpdatedata(int updatedata) {
            this.updatedata = updatedata;
            notifyObservers();
    }

}


//定义订阅者1类
public class Subscriber1 {

    //定义订阅者自己的信息
    private String name;

    //定义订阅者需要在报社那里接到的数据
    private int updatedata;

    //构造器里还装入了该订阅者的名字
    public Subscriber1(String name){
                this.name = name;
            }

    public void display() {
                System.out.println("我是订阅者,我的名字叫: "+name+" , 这是从报社接到的数据: "+ updatedata);
            }

    public void update(int updatedata) {
                this.updatedata = updatedata;
                display();
            }
}

//定义订阅者2类
public class Subscriber2 {

    //定义订阅者自己的信息
    private String name;
    private int age;

    //定义订阅者需要在报社那里接到的数据
    private int updatedata;

    //构造器里还装入了该订阅者的名字
    public Subscriber1(String name, int age){
                this.name = name;
            }

    //显示出信息
    public void display() {
                System.out.println("我是订阅者,我的名字叫: "+name+",  我的年龄是: "+age+" , 这是从报社接到的数据: "+ updatedata);
            }

    //更新订阅者手中的信息(也就是更新手中的<<意林>>书的个数)
    public void update(int updatedata) {
                this.updatedata = updatedata;
                display();
            }

}

看了上面的代码,你有没有一种想骂我的感觉,上面的代码太不灵活了,当然这不可能是”观察者模式”哈,我只是写了一个java初学者会设计的模式.

上面代码很不灵活,的确.如果有一天,又有一个”订阅者3”去报刊订阅<<意林>>,NewspaperOffice这个类就需要重新写代码,在notifyObservers()方法里添加”订阅者3”的实例…当有一天”订阅者2”不想订阅了,NewspaperOffice类又要重新重构代码.麻烦至极啊…

很显然啊,上面的代码太别没有遵循之间我们所说的两个设计原则,所有是一组极差,及其不具备灵活性的代码…

下面,我们用”观察者模式”编写上面的代码…


首先,我们需要面向接口编程,把NewspaperOfficeSubscriber1,Subscriber2提取一下.

/*
这里提供了一个Subject(主题)接口,接口里定义了:注册观察者方法,删除观察者方法,向已经注册的观察者发送消息的方法
*/

public interface Subject {
    //用来注册观察者
    public void registerObserver(Observer o);
    //用来删除观察者
    public void removeObserver(Observer o);
    //通知所有已经注册的观察者
    public void notifyObservers();
}

/*
定义Observer(观察者)接口,所有观察者都具备更新内部数据的功能
*/
public interface Observer {
        //更新观察者内部的数据
        public void update(int updatedata);
}

/*
定义Subscriber(订阅者)接口,所有订阅者都可以展出自己的内部信息(也就是所有订阅者都可以向别人展出手中意林数目
*/
public interface Subscriber {
        //每一个订阅者都可以显示自己的信息
        public void display();
}


/*
下面,我们的报社出厂了
*/
import java.util.ArrayList;
import java.util.List;

//报社对象实线了主题借口
public class NewspaperOffice implements Subject {

        //定义主题后期会更新的数据
        private int updatedata;

        //建立一个list来装注册进来的观察者
        private List <Observer>observers;


        //构造函数,构造list
        public NewspaperOffice(){
            observers = new ArrayList<Observer>();
        }
        //添加注册观察者
        @Override
        public void registerObserver(Observer o) {
            observers.add(o);
        }

        //移除观察者
        @Override
        public void removeObserver(Observer o) {
            int i = observers.indexOf(o);
            if(i >= 0){
                observers.remove(i);
            }
        }

        //通知所有的观察者
        @Override
        public void notifyObservers() {
            for(Observer observer: observers){
                observer.update(this.updatedata);
            }
        }

        //当主题里的数据进行更新的时候,通知所有的观察者
        public void setUpdatedata(int updatedata) {
            this.updatedata = updatedata;
            notifyObservers();
        }

}





/*
定义订阅者1 实现了观察者接口,订阅者的接口

*/
public class Subscriber1 implements Observer, Subscriber {

    //定义订阅者自己的信息
    private String name;

    //定义订阅者需要在报社那里接到的数据
    private int updatedata;

    //定义观察者引用
    private Subject subject;

    //构造器里接受到该观察者需要注册的到哪个主题。并且注册到该主题上
    //构造器里还装入了该订阅者的名字
    public Subscriber1(Subject subject, String name){
        this.name = name;
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("我是订阅者,我的名字叫: "+name+" , 这是从报社接到的数据: "+ updatedata);
    }

    @Override
    public void update(int updatedata) {
        this.updatedata = updatedata;
        display();
    }

}


/*
定义订阅者2,基本内容与订阅者1相似,但是请注意,订阅者1与订阅者2是完全不同的两个类型,因为订阅者2里需要填写年龄信息.
如果两个类一模一样,那写成一个类不就好了么?
为什么要这样设计两个类呢? 因为我们后期的订阅者是各种各样的,订阅者怎么变化都无所谓,这充分说明了我们类与类之间的耦合程度非常低.
*/
public class Subscriber2 implements Observer, Subscriber {

        //定义订阅者自己的信息
        private String name;
        private int age;

        //定义订阅者需要在报社那里接到的数据
        private int updatedata;

        //定义观察者引用
        private Subject subject;

        //构造器里接受到该观察者需要注册的到哪个主题。并且注册到该主题上
        //构造器里还装入了该订阅者的名字
        public Subscriber2(Subject subject, String name, int age){
            this.age = age;
            this.name = name;
            this.subject = subject;
            subject.registerObserver(this);
        }

        @Override
        public void display() {
            System.out.println("我是订阅者,我的名字叫: "+name+",  我的年龄是: "+age+" , 这是从报社接到的数据: "+ updatedata);
        }

        @Override
        public void update(int updatedata) {
            this.updatedata = updatedata;
            display();
        }

}

基本结构都已经设计完了,现在我来写个测试类进行测试

public class Main {
        public static void main(String[] args) {
            //创建报社
            NewspaperOffice nsp1 = new NewspaperOffice();

            //用Subscriber1创建订阅者1,并且将他注册到报社
            Subscriber1 GC = new Subscriber1(nsp1, "GC");
            //用Subscriber2创建订阅者2,也注册到报社
            Subscriber2 YP = new Subscriber2(nsp1, "YP", 33);

            System.out.println("===========下面开始发布第1版报纸,此时GC与YP都在报社里========");
            //现在报社开始发布第一版报纸
            nsp1.setUpdatedata(1);
            System.out.println("===========下面开始发布第2版报纸,此时GC与YP都在报社里========");
            //报社发布第二版报纸
            nsp1.setUpdatedata(2);

            //现在GC不订阅报纸了,报社删除GC
            nsp1.removeObserver(GC);
            System.out.println("===========下面开始发布第3版报纸,此时只有YP都在报社里========");
            //删除GC之后,报社又发布了第三版报纸
            nsp1.setUpdatedata(3);

          /*
             * 运行结果如下:
             * ===========下面开始发布第1版报纸,此时GC与YP都在报社里========
             * 我是订阅者,我的名字叫: GC , 这是从报社接到的数据: 1
             * 我是订阅者,我的名字叫: YP,  我的年龄是: 33 , 这是从报社接到的数据: 1
             * ===========下面开始发布第2版报纸,此时GC与YP都在报社里========
             * 我是订阅者,我的名字叫: GC , 这是从报社接到的数据: 2
             * 我是订阅者,我的名字叫: YP,  我的年龄是: 33 , 这是从报社接到的数据: 2
             * ===========下面开始发布第3版报纸,此时只有YP都在报社里========
             * 我是订阅者,我的名字叫: YP,  我的年龄是: 33 , 这是从报社接到的数据: 3
             */

        }
}

这是“观察者”模式的类图,里面只有Subject类与Observer类之间的关系,请仔细思考。
观察者模式


好吧,我承认,”观察者模式”不是那么容易理解,如果你看了我上面所写的内容之后就的能理解”观察者模式”,我真的再次赞叹一下:你的智商真的很高很高….

经过对将近两个小时的奋斗,终于把<<设计模式-观察者模式>>写完了,如果有不会的,没看懂的请Email或者留言给我……………

请转发OR复制的同学,标注出处,尊重作者劳动成果,谢谢亲


谢谢观看,结束本节!

注:本文借鉴了《HeadFirst-设计模式》一书,作者总结的内容不到书内容精彩十分之一,还望大家多读原著。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章