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

     观察者模式是我们经常使用的模式之一,那什么是观察者模式呢?观察者模式(有时又被称为发布-订阅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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值