1. 概述

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

2. 解决的问题

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

3. 模式中的角色

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

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

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

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

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

Observer使用

       // File Monitoring

        // Create a new observer for the folder and add a listener
// that will handle the events in a specific directory and take action.
File parentDir = FileUtils.getFile(PARENT_DIR); FileAlterationObserver observer = new FileAlterationObserver(parentDir);
observer.addListener(new FileAlterationListenerAdaptor() { @Override
public void onFileCreate(File file) {
System.out.println("File created: " + file.getName());
} @Override
public void onFileDelete(File file) {
System.out.println("File deleted: " + file.getName());
} @Override
public void onDirectoryCreate(File dir) {
System.out.println("Directory created: " + dir.getName());
} @Override
public void onDirectoryDelete(File dir) {
System.out.println("Directory deleted: " + dir.getName());
}
}); // Add a monior that will check for events every x ms,
// and attach all the different observers that we want.
FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer);
try {
monitor.start(); // After we attached the monitor, we can create some files and directories
// and see what happens!
File newDir = new File(NEW_DIR);
File newFile = new File(NEW_FILE); newDir.mkdirs();
newFile.createNewFile(); Thread.sleep(1000); FileDeleteStrategy.NORMAL.delete(newDir);
FileDeleteStrategy.NORMAL.delete(newFile); Thread.sleep(1000); monitor.stop();
} catch (IOException e) {
//..

  

文件变更Observer 变量声明

public class FileAlterationObserver implements Serializable {

    private static final long serialVersionUID = 1185122225658782848L;
private final List<FileAlterationListener> listeners = new CopyOnWriteArrayList<FileAlterationListener>();
private final FileEntry rootEntry;
private final FileFilter fileFilter;
private final Comparator<File> comparator;
//...
}

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

参考:

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

判断变更的逻辑 checkAndNotify方法:

final File rootFile = rootEntry.getFile();
if (rootFile.exists()) {
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile));
} else if (rootEntry.isExists()) {
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
  //FileUtils.EMPTY_FILE_ARRAY=File[0];
} else {
// Didn't exist and still doesn't
} private void checkAndNotify(final FileEntry parent, final FileEntry[] previous, final File[] files) {
int c = 0;
final FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_ENTRIES;
for (final FileEntry entry : previous) {
while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) {
current[c] = createFileEntry(parent, files[c]);
doCreate(current[c]);
c++;
}
if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) {
doMatch(entry, files[c]);
checkAndNotify(entry, entry.getChildren(), listFiles(files[c]));
current[c] = entry;
c++;
} else {
checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
doDelete(entry);
}
}
for (; c < files.length; c++) {
current[c] = createFileEntry(parent, files[c]);
doCreate(current[c]);
}
parent.setChildren(current);
}
//example
private void doMatch(final FileEntry entry, final File file) {
if (entry.refresh(file)) {
for (final FileAlterationListener listener : listeners) {
if (entry.isDirectory()) {
listener.onDirectoryChange(file);
} else {
listener.onFileChange(file);
}
}
}
}

观察类实现的ListFiles 方法:

 private File[] listFiles(final File file) {
File[] children = null;
if (file.isDirectory()) {
children = fileFilter == null ? file.listFiles() : file.listFiles(fileFilter);
}
if (children == null) {
children = FileUtils.EMPTY_FILE_ARRAY;
}
if (comparator != null && children.length > ) {
Arrays.sort(children, comparator);
}
return children;
}

Monitor 类:

public synchronized void start() throws Exception {
if (running) {
throw new IllegalStateException("Monitor is already running");
}
for (final FileAlterationObserver observer : observers) {
observer.initialize();
}
running = true;
if (threadFactory != null) {
thread = threadFactory.newThread(this);
} else {
thread = new Thread(this);
}
thread.start();
}
public synchronized void stop(final long stopInterval) throws Exception {
if (running == false) {
throw new IllegalStateException("Monitor is not running");
}
running = false;
try {
thread.join(stopInterval);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
for (final FileAlterationObserver observer : observers) {
observer.destroy();
}
}
public void run() {
while (running) {
for (final FileAlterationObserver observer : observers) {
observer.checkAndNotify();
}
if (!running) {
break;
}
try {
Thread.sleep(interval);
} catch (final InterruptedException ignored) {
}
}
}

  

我们使用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. DIV的圆角表现和TAB切换

    内容大体是从网上找过来的,感觉那位哥们说的对,css还是比较靠谱的,当然有些高玩搞出来的东西本地 没有运行起来. 把自己的应用贴出来同大家分享 <html> <head> &l ...

  2. leetcode 101 Symmetric Tree ----- java

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  3. URAL 1205 By the Underground or by Foot?(SPFA)

    By the Underground or by Foot? Time limit: 1.0 secondMemory limit: 64 MB Imagine yourself in a big c ...

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

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

  5. Qt事件和信号的区别 .

    仔细来看,事件与信号其实并无多大差别,从我们对其需求上来说,都只要能注册事件或信号响应函数,在事件或信号产生时能够被通知到即可.但有一项区别在于,事件处理函数的返回值是有意义的,我们要根据这个返回值来 ...

  6. JSBinding + SharpKit / 初体验:下载代码及运行Demo

    QQ群:189738580 以下是群主维护的JSB版本: git地址:https://github.com/qcwgithub/qjsbunitynew.git插件源码地址(不包含SpiderMonk ...

  7. sql server 移动表到文件组

    存储过程如下: USE [SCC_Test_FileGroupAndIndex] GO /****** Object:  StoredProcedure [dbo].[sp_MoveTable]    ...

  8. js中冒泡事件和捕获事件

    js中冒泡事件和捕获事件: 冒泡事件:冒泡事件是从里向外,即是从被绑定元素开始一直向外到达页面的所有祖先元素都会被触发,这 一过程被称为事件冒泡.这个事件从原始元素开始一直冒泡到DOM树的最上层 捕获 ...

  9. Android中使用speex将PCM录音格式转Wav格式

    Android中使用speex将PCM录音格式转Wav格式 2013-09-17 17:24:00|  分类: android |  标签:android  speex  wav  |举报|字号 订阅 ...

  10. fetch用法

    fetch(REQUEST_URL) .then((response) => response.json()) .then((responseData)=> { console.log(r ...