编程基本知识--观察者模式

来源:互联网 发布:直销会员管理系统源码 编辑:程序博客网 时间:2024/06/05 08:41

     观察者模式是我们经常使用的模式之一,那什么是观察者模式呢?观察者模式(有时又被称为发布-订阅Subscribe>模式是23种模式中的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。


    我还是用比较通俗的方法来理解!比如:某人去邮局订了一本杂志,那么如果杂志有更新了,邮局就会通知他,因此这里面就会有两个重要的对象,一个就是某人(我们可以称为观察者),那另一个对象就是邮局(我们可以称为被观察对象或者叫主题),那么它的作用是什么?一般而言有两个作用:1.管理观察者,我们去订杂志的时候总会告诉邮局,邮局也一定会有记录的。2.通知观察者,当有新杂志到的时候,主题必须通知观察者有新的杂志到了。

   我们可以进一步把上面的概念抽象成对象:

    邮局:抽象成主题(Subject),它是管理者一般会有注册方法(regists),当然也有注销方法(unregists),还有一个通知观察者的方法(notify)。

    某人:抽象成观察者(Observer),它是被通知的人,那肯定有一个更新的方法(update)。


   我们一般都是面向接口编程,所以再一次把上面抽象成接口:

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

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

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

     下面我们来简单实现一下: 

  一. 定义观察者接口:

package java.observer;public interface Observer {    public void update(Subject subject,Object data);}
   二.主题的实现:这里我用arraylist集合来保存所有观察者对象。因为在项目中我们也是经常只有一个主题,所以这里我使用单例.

package java.observer;import java.util.ArrayList;import java.util.List;public class Subject {        private static Subject mInstance;    private List<Observer> observers = new ArrayList<Observer>();        private Subject() {            }        public static Subject getInstance() {        if(mInstance == null) {            mInstance = new Subject();        }                return mInstance;    }        public void regists(Observer observer) {        if(observer != null) {            observers.add(observer);        }    }        public void unregists(Observer observer) {        if(observer != null) {            observers.remove(observer);        }    }        public void notify(Object object) {        for(Observer observer : observers) {            observer.update(this, object);        }    }}
三.我们再来具体实现一个观察者:

package java.observer;public class ObserverImpl implements Observer {    @Override    public void update(Subject subject, Object data) {        System.out.println((String)data);    }}
这里只是简单的打印,实际项目中肯定不会这么简单。

我还是简单的运行起来:

package javaObserver.myObserver;public class ObserverClient {        public static void main(String[] args) {        Subject obSubject = Subject.getInstance();// 获取主题        // 实例化一个观察者        ObserverImpl observer = new ObserverImpl();                // 注册观察者        obSubject.regists(observer);        // 如果事件到来时,主题会主动更新        obSubject.notify("update all observer ");    }}

到这里我们简单的观察者模式,基本已经完成。这只是在单线程的环境下面运行的,如果要想在多线程的环境下面运行,我们必须对上面几个部分进行修改:

  第一.单例问题 

public static Subject getInstance() {        if(mInstance == null) {            mInstance = new Subject();        }                return mInstance;    }
这是不安全,我们必须改成线程安全的。可以改成下面:

private static class SubjectInner {        public static Subject inner = new Subject();    }            public static Subject getInstance() {        return SubjectInner.inner;    }
第二,给regists,unregists 和 notify方法加上synchronized 关健字。这样相当对每个方法都加了锁,而且锁住的对象本身。

 似乎这样已经是很好的,因为我们保证了每个方法都是同步,只有一个线程能操作,但这里就有一个效率上面的问题,如果观察者对象很多的时候,一个线程在遍历,而其他要注册或取消的进程就有可能进行长时间的等待。因此,我们一般只对regists,unregists加同步。


  但如果没有对notify加同步的时候却产生了另外一个问题:concurrentmodificationexception ,这个异常是由于遍历集合的时候,同时有人在修改集合里面的数据。

要解决这个问题,我们一般有两种方法:

 第一种:把上面的arraylist 改成用CopyOnWriteArrayList 集合,CopyOnWriteArrayList的原理很解决,每次写数据的时候都把原来的数据复制一份,所以也就不会有问题,但这样也会有一个问题,如果数据大的也会比较占空间,是一种用空间换时间的做法。


第二种:每次注册的时候,先去检测有没有在遍历,如果正在遍历则添加到临时集合里面,遍历完成之后再添加到原来的集合里面。本人比较偏向第二种方法。


  

    

0 0
原创粉丝点击