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. JavaScript学习记录总结(六)——js函数闭包特性

    <script type="text/javascript">    function arrdemo(){        var arr=["hello&q ...

  2. LAMT基于mod_jk方式的负载均衡集群

    一.系统环境 1.apache服务器 系统环境:CentOS release 6.5 (Final) ip地址:192.168.1.203 2.tomcat1服务器 系统环境:CentOS relea ...

  3. 远程升级openSSH

    1.确认安装了telnet服务,并且telnet服务能正常运行 查询是否安装了telnet服务:rpm -qa telnet 如果显示出 类似于telnet-server-0.17-31.EL4.5这 ...

  4. jQuery判断元素是否在可视区

    假设此元素为 #item,先说几个关键的属性: $('#item').offset().top#item 的绝对偏移量,指#item的实际尺寸(即不包括外边框margin)的上边界到页面顶端的距离.这 ...

  5. VIM进阶学习之几种模式和按键映射

    Map是Vim强大的一个重要原因,可以自定义各种快捷键,用起来自然得心应手. vim里最基本的map用法也就是 :map c a 这里把c映射成了a,在map生效的情况下,按下c就等同于按下了a 当然 ...

  6. (转) Written Memories: Understanding, Deriving and Extending the LSTM

    R2RT   Written Memories: Understanding, Deriving and Extending the LSTM Tue 26 July 2016 When I was ...

  7. 让PHP代码更危险----使用别的系统命令--如sql语句--exec(),system()方法甚至html的js语句

    如题,所以涉及到别的语言时,程序就可能更加不安全.

  8. Request.RequestContext.RouteData.Values获取不到参数值

    Request.RequestContext.RouteData.Values是获取路由配置中参数的值. 如过路由配置是: public static void RegisterRoutes(Rout ...

  9. playframework1.x的eclipse插件开源-playtools

    playtools介绍 playframework(1.2.x)是一款令人兴奋的java restful风格的web框架,使用它已经有两年多. 其中结合eclipse开发项目往往要不断进行cmd窗口切 ...

  10. 木匠ing[索引]

    古人有云,一个不会写代码的木匠不会是个好厨子. 为了响应这个号召,开始我的木工之路. 首先介绍一个网站,www.zuojiaju.com 木工爱好者 ,里面有大量的关于木匠的帖子,感谢一下. 以前只是 ...