美文网首页设计模式
浅谈设计模式2——观察模式

浅谈设计模式2——观察模式

作者: 吃根香蕉压压惊 | 来源:发表于2016-05-04 15:17 被阅读60次


    我想大家一定都去医院挂过号,大概流程是:挂号,到就诊室门口排队,等着医生叫号看病。今天说的观察者模式就是这样一个道理。很多的病人就是观察者,等待医生状态发生改变——看完病,叫号。然后所有的病人听到号,看看叫的是不是自己,走入诊室看病,或者继续等待。

    1、背景叙述
    依然感觉还是用书上的例子比较好,但是像“气象观测站”这种东西,除了看天气预报,我绝不会想到。为了不让问题更加复杂,我想还是直接切入主题,用比较形式化的描述方式来阐述,也能更快进入主题。

    2、观察者模式
    对于某个“对象A”,当它的状态发生变化时——某个属性发生变化,或者调用某个函数。则其他的对象也因为“对象A”的变化做出某些操作。“对象A”为被监听者,其他的对象为监听者。有没有想到Java中的事件?对,这个就是事件的原型。先结合订报纸的示例,用语言描述一下具体实现,然后上代码,最后给大家说下这样做有啥好处。

    1)订报纸需要以下步骤
    (1)去邮局,请求订报纸,邮局将我们的个人信息注册。——邮局就是“被监听者”,我们是“监听者”。监听者需要到被监听者那里注册个人信息。
    (2)当邮局到了新报纸,则就将报纸分发给我们。——邮局从没有报纸的状态,转换到有报纸的状态,状态发生改变,需要告诉我们状态发生改变,“报纸”就是“被监听者”发送给“监听者”信息
    (3)我们收到报纸后,有的人读报纸,了解新闻;有的人比较土豪,用来包家具;有的人更土豪,送给邻居。——“监听者”收到消息后,采取不同的动作。

    从以上分析举例来看,监听者模式(也就是观察者模式,监听者说的比较顺口)其实是一种通信模式。就是将信息进行广播。然后收到相同信息的不同实体,根据信息和自身特点,做出相应操作。
    需要注意的是,因为投递员只认识绿皮邮筒,所以所有的监听者要提供统一的监听接口来接受消息。监听接口其实就是个函数。
    但是,为了保证程序面向对象的良好设计,我们使用接口来对各个类进行修饰。

    2)程序部分

    import java.util.ArrayList;
    //测试类
    public class Test
    {
        public static void main(String args[])
        {
            PostOffice postOffice=new PostOffice();//被观察者
            Observer1 observer1=new Observer1();//观察者1号
            Observer2 observer2=new Observer2();//观察者2号
            
            postOffice.addObserver(observer1);//注册观察者1号
            postOffice.addObserver(observer2);//注册观察者2号
            
            postOffice.setState(true, true);//改变被观察者状态
        }
    }
    /*
    如上面的程序,监听者需要跑到被监听者那里去注册,只有注册了,被监听者才能发消息给监听者。
    */
    
    
    interface Subject //定义被监听者接口,但是有一个不好点,下面会仔细说。
    {
        public void addObserver(Observer o);
        public void deleteObserver(Observer o);
        public void notifyObserver(Object otherArg);
    }
    
    interface Observer//定义监听者接口,实际只是定义一个接受信息的接口 
    {
        public void  receive(Subject subject, Object otherArg );
    }
    
    //实现被被观察者接口
    class PostOffice implements Subject
    {
        private boolean newsPaper=false;
        private boolean gift=false;
        
        private ArrayList<Observer> arrayList;//存放观察者的列表
        
        public PostOffice()//构造函数
        {
            this.newsPaper=false;
            this.gift=false;
            this.arrayList=new ArrayList<Observer>(); //注意一定要初始化对象实体
        }
        
          
        public void stateChanged()//用户自定义
        {
            this.notifyObserver(null);
        }
        
        public void setState(boolean newspaper, boolean gift) //用户自定义
        {
            this.newsPaper=newspaper;
            this.gift=gift;
            
            this.stateChanged();
        }
        
        //以下为实现的Subject接口
        public void addObserver(Observer o)
        {
            this.arrayList.add(o);
        }
        public void deleteObserver(Observer o)
        {
            int index=this.arrayList.indexOf(o);
            if(index>0)
                this.arrayList.remove(index);
        }
        public void notifyObserver(Object otherArg) 
        {
            for (Observer observer : this.arrayList) 
                observer.receive(this, otherArg);
        }
    }
    
    class Observer1 implements Observer
    {
        @Override
        public void receive(Subject subject, Object otherArg) 
        {
            System.out.println("this is Observer1");        
        }
    }
    
    class Observer2 implements Observer
    {
    
        @Override
        public void receive(Subject subject, Object otherArg) {
            System.out.println("this is Observer2");
            
        }
    }
    

    3)程序分析部分
    希望上面的程序大家能看懂,下面主要针对程序需要说明几点注意事项。
    (1)Java类库中,被监听者Subject实际上是一个类,而此处是一个接口。那么到底哪个比较好呢?
    如果是接口,就必须维护一个ArrayList列表(接口中都是public成员,这样就暴露了列表成员),来存储申请注册的监听者。而如果是类,则没有这么多麻烦,直接调用addObserver(),添加监听者即可。也就是说,接口破坏了封装性。
    但是,如果是类,由于Java只支持单继承,因此这样做子类就不能继承其他类,也不好用。

    (2)Observer接口中,receive()函数传递的参数可以根据需要自己设定,但是需要考虑两个方面:
    a、参数不能太多,否则会增加通信量,严重影响程序的执行效率。
    b、参数不能太少,因为这个函数是所有类接收信息的统一接口,一旦未来功能要扩展,就需要修改很多的东西——凡是实现receive()方法的类全都需要修改。
    所以此处我把接口设计成接收两个参数,一个是被监听者,一个是其他类。如果需要被监听者的成员变量,可以采用getter 和 setter方法。如果需要获取其他信息,可以将这些信息全部打包在一个容器类里,通过otherArg来传递。

    (3)对于PostOffice类,有三个函数:setState()函数来改变当前对象的状态,stateChanged()函数由用户自定义,来执行当对象状态改变时,需要作出的动作。最后一个notifyObserver()才是真正通知监听者,通知他们。为什么要这么麻烦,直接将notifyObserver()放到setState()中不是更好?
    这样做将:改变对象状态,改变对象状态后应该做的操作,通知监听者,三个操作进行了分离。将操作的粒度变小,从而让修改更加容易。

    同时,还要注意,对于邮局这个模型,报纸来了通知一下就好。但是对于温度检测等模型,变化速度太快,而响应速度又不太高(每秒变化一度,但是变化十度才做出反应)这样的情况,设置上面的三个函数就很有效。

    顺便说下,上面的函数依然有文章可做。可以在Subject接口中再增加一个setchanged()函数。下面只是说下增加到PostOffice中的代码。增加了setChanged(),好处是可以根据当前状态,来决定到底通不通知监听者。

           private boolean changed=false;
           public void stateChanged()//用户自定义
        {
            this.setChanged(false);//注意这行,通知完后,就表明没有改变了
            this.notifyObserver(null);
        }
        
        public void setState(boolean newspaper, boolean gift) //用户自定义
        {
            this.newsPaper=newspaper;
            this.gift=gift;
            
            this.setChanged(true);//注意这行,发生了改变
            
            this.stateChanged();
        }
           public void  setChanged(boolean changed)
        {
            this.changed=changed;
        }
    

    至此,观察模式结束。码了这么多字,好累。

    相关文章

      网友评论

        本文标题:浅谈设计模式2——观察模式

        本文链接:https://www.haomeiwen.com/subject/kxyrrttx.html