1. 概述

  有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

2. 解决的问题

  将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。

3. 模式中的角色

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

  3.2 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

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

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

这里选取Apache Commons IO的monitor来分析观察者模式。

Observer使用

  1. // File Monitoring
  2.  
  3. // Create a new observer for the folder and add a listener
  4. // that will handle the events in a specific directory and take action.
  5. File parentDir = FileUtils.getFile(PARENT_DIR);
  6.  
  7. FileAlterationObserver observer = new FileAlterationObserver(parentDir);
  8. observer.addListener(new FileAlterationListenerAdaptor() {
  9.  
  10. @Override
  11. public void onFileCreate(File file) {
  12. System.out.println("File created: " + file.getName());
  13. }
  14.  
  15. @Override
  16. public void onFileDelete(File file) {
  17. System.out.println("File deleted: " + file.getName());
  18. }
  19.  
  20. @Override
  21. public void onDirectoryCreate(File dir) {
  22. System.out.println("Directory created: " + dir.getName());
  23. }
  24.  
  25. @Override
  26. public void onDirectoryDelete(File dir) {
  27. System.out.println("Directory deleted: " + dir.getName());
  28. }
  29. });
  30.  
  31. // Add a monior that will check for events every x ms,
  32. // and attach all the different observers that we want.
  33. FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer);
  34. try {
  35. monitor.start();
  36.  
  37. // After we attached the monitor, we can create some files and directories
  38. // and see what happens!
  39. File newDir = new File(NEW_DIR);
  40. File newFile = new File(NEW_FILE);
  41.  
  42. newDir.mkdirs();
  43. newFile.createNewFile();
  44.  
  45. Thread.sleep(1000);
  46.  
  47. FileDeleteStrategy.NORMAL.delete(newDir);
  48. FileDeleteStrategy.NORMAL.delete(newFile);
  49.  
  50. Thread.sleep(1000);
  51.  
  52. monitor.stop();
  53. } catch (IOException e) {
  54. //..

  

文件变更Observer 变量声明

  1. public class FileAlterationObserver implements Serializable {
  2.  
  3. private static final long serialVersionUID = 1185122225658782848L;
  4. private final List<FileAlterationListener> listeners = new CopyOnWriteArrayList<FileAlterationListener>();
  5. private final FileEntry rootEntry;
  6. private final FileFilter fileFilter;
  7. private final Comparator<File> comparator;
  8. //...
  9. }

CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。其 add(E) 和remove(int index)都是对新的数组进行修改和新增。所以在多线程操作时不会出现java.util.ConcurrentModificationException错误。

参考:

http://my.oschina.net/jielucky/blog/167198

判断变更的逻辑 checkAndNotify方法:

  1. final File rootFile = rootEntry.getFile();
  2. if (rootFile.exists()) {
  3. checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile));
  4. } else if (rootEntry.isExists()) {
  5. checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
  6.   //FileUtils.EMPTY_FILE_ARRAY=File[0];
  7. } else {
  8. // Didn't exist and still doesn't
  9. }
  10.  
  11. private void checkAndNotify(final FileEntry parent, final FileEntry[] previous, final File[] files) {
  12. int c = 0;
  13. final FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_ENTRIES;
  14. for (final FileEntry entry : previous) {
  15. while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) {
  16. current[c] = createFileEntry(parent, files[c]);
  17. doCreate(current[c]);
  18. c++;
  19. }
  20. if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) {
  21. doMatch(entry, files[c]);
  22. checkAndNotify(entry, entry.getChildren(), listFiles(files[c]));
  23. current[c] = entry;
  24. c++;
  25. } else {
  26. checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
  27. doDelete(entry);
  28. }
  29. }
  30. for (; c < files.length; c++) {
  31. current[c] = createFileEntry(parent, files[c]);
  32. doCreate(current[c]);
  33. }
  34. parent.setChildren(current);
  35. }
  36. //example
  37. private void doMatch(final FileEntry entry, final File file) {
  38. if (entry.refresh(file)) {
  39. for (final FileAlterationListener listener : listeners) {
  40. if (entry.isDirectory()) {
  41. listener.onDirectoryChange(file);
  42. } else {
  43. listener.onFileChange(file);
  44. }
  45. }
  46. }
  47. }

观察类实现的ListFiles 方法:

  1. private File[] listFiles(final File file) {
  2. File[] children = null;
  3. if (file.isDirectory()) {
  4. children = fileFilter == null ? file.listFiles() : file.listFiles(fileFilter);
  5. }
  6. if (children == null) {
  7. children = FileUtils.EMPTY_FILE_ARRAY;
  8. }
  9. if (comparator != null && children.length > ) {
  10. Arrays.sort(children, comparator);
  11. }
  12. return children;
  13. }

Monitor 类:

  1. public synchronized void start() throws Exception {
  2. if (running) {
  3. throw new IllegalStateException("Monitor is already running");
  4. }
  5. for (final FileAlterationObserver observer : observers) {
  6. observer.initialize();
  7. }
  8. running = true;
  9. if (threadFactory != null) {
  10. thread = threadFactory.newThread(this);
  11. } else {
  12. thread = new Thread(this);
  13. }
  14. thread.start();
  15. }
  16. public synchronized void stop(final long stopInterval) throws Exception {
  17. if (running == false) {
  18. throw new IllegalStateException("Monitor is not running");
  19. }
  20. running = false;
  21. try {
  22. thread.join(stopInterval);
  23. } catch (final InterruptedException e) {
  24. Thread.currentThread().interrupt();
  25. }
  26. for (final FileAlterationObserver observer : observers) {
  27. observer.destroy();
  28. }
  29. }
  30. public void run() {
  31. while (running) {
  32. for (final FileAlterationObserver observer : observers) {
  33. observer.checkAndNotify();
  34. }
  35. if (!running) {
  36. break;
  37. }
  38. try {
  39. Thread.sleep(interval);
  40. } catch (final InterruptedException ignored) {
  41. }
  42. }
  43. }

  

我们使用org.apache.commons.io.monitor包下的类创建了一个处理器来监听一些特定的事件(在上面的例子中就是我们对文件或目录所做的所有操作事件),为了获得这些信息,我们需要做以下几步操作:

1、创建一个File对象,这个对象指向我们需要监听变化的目录。

2、创建一个FileAlterationObserver对象,这个对象会观察这些变化。

3、通过调用addListener()方法,为observer对象添加一个 FileAlterationListenerAdaptor对象。你可以通过很多种方式来创建一个适配器,在我们的例子中我们使用内部类的方式进行创建并且只实现其中的一部分方法(只需要实现我们例子中需要用的方法即可)。

4、创建一个FileAlterationMonitor 对象,将已经创建好的observer对象添加其中并且传入时间间隔参数(单位是毫秒)。

5、调用start()方法即可开启监视器,如果你想停止监视器,调用stop()方法即可。

Observer - IO (File Monitor)的更多相关文章

  1. java.io.NotSerializableException: test.io.file.Student

    java.io.NotSerializableException: test.io.file.Student    at java.io.ObjectOutputStream.writeObject0 ...

  2. IO:File类(java.io.File)

    public class File extends Object implements Serializable, Comparable<File> 构造方法: public File(S ...

  3. java.io.file

    package cn.edu.tongji.cims.wade.system;     import java.io.*;     public class FileOperate {     pub ...

  4. java获取指定路径下的指定文件/java.io.File.listFiles(FilenameFilter filter)

    java.io.File.listFiles(FilenameFilter filter) 返回抽象路径名数组,表示在目录中此抽象路径名表示,满足指定过滤器的文件和目录. 声明 以下是java.io. ...

  5. 【java IO File】统计项目代码总共多少行

    统计项目代码总共有多少行 思想: 1.首先将不需要迭代的文件夹,保存在集合中,不满足的就是需要迭代的文件夹 2.将需要进行统计行数的代码文件保存在集合中,满足的就是需要计算文件行数的文件 3.迭代方法 ...

  6. System.IO.File.Create 不会自动释放,一定要Dispose

    这样会导致W3P进程一直占用这个文件 System.IO.File.Create(HttpContext.Current.Server.MapPath(strName)) 最好加上Dispose Sy ...

  7. IIS目录下文件共享后System.IO.File.Exists返回false

    场景:在iis目录下,因为特殊需要共享一个文件夹,给到其他的技术人员访问,突然发现小小的操作,搞“大”了,使用 string path = Server.MapPath("~/file/te ...

  8. java.io.File类

    java.io.File类 1.凡是与输入.输出相关的类.接口等都定义在java.io包下 2.File是一个类.能够有构造器创建其对象.此对象相应着一个文件(.txt .avi .doc .ppt ...

  9. 详解C#中System.IO.File类和System.IO.FileInfo类的用法

    System.IO.File类和System.IO.FileInfo类主要提供有关文件的各种操作,在使用时需要引用System.IO命名空间.下面通过程序实例来介绍其主要属性和方法. (1) 文件打开 ...

随机推荐

  1. 一个方法中的ajax在success中renturn一个值,但是方法的返回值是undefind?

    https://segmentfault.com/q/1010000003762379 A页面 console.log(handleData("search_list", &quo ...

  2. POJ 3928 Ping pong(树状数组)

                                                                          Ping pong Time Limit: 1000MS   ...

  3. 共享内存+互斥量实现linux进程间通信 分类: Linux C/C++ 2015-03-26 17:14 67人阅读 评论(0) 收藏

    一.共享内存简介 共享内存是进程间通信中高效方便的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针,两个进程可以对一块共享 ...

  4. Anroid 异常:is not valid; is your activity running?

    本文转载于:http://blog.csdn.net/biangren/article/details/7514722 是由于有activity时依附于另一个activity的,当被依附的activi ...

  5. Microservice Orleans

    https://azure.microsoft.com/en-us/blog/containers-docker-windows-and-trends/ https://channel9.msdn.c ...

  6. UMDH

    1. 什么是UMDH UMDH=User Mode dump heap.把进程的堆dump下来.包括堆栈信息.UMDH是WinDbg附带的一个工具. 2. 下载安装 WinDbg.完事后在WinDbg ...

  7. C数据类型

    结构体 因为数组中各元素的类型和长度都必须一致,以便于编译系统处理.为了解决这个问题,C语言中给出了另一种构造数据类型——“结构(structure)”或叫“结构体”.它相当于其它高级语言中的记录.“ ...

  8. unity, 同步物体坐标一定要在LateUpdate中进行

    设a为主动物体,b为跟随物体. 如果a,b都在同一个Update里更新坐标,那么两者自然是同步的. 如果a在a.Update里更新位置,而b在b.Update里将自己的位置更新为与a相同,那就会有误差 ...

  9. OpenJudge计算概论-计算鞍点

    /*======================================================================== 计算鞍点 总时间限制: 1000ms 内存限制: ...

  10. jQuery的图像裁剪插件Jcrop

    1.最基本使用方法     html代码部分: <img src="demo_files/flowers.gif" id="demoImage"/> ...