實現 throws over don ogg align 自己的 穩定 ack

1、概述

  觀察者模式是軟件設計模式的一種,它描述了如何建立對象與對象之間的依賴關系,如何構造滿足這種需求的系統。這一模式中的關鍵對象是觀察目標和觀察者,一個目標可以有任意數目的與之相依賴的觀察者,一旦目標的狀態發生改變,所有的觀察者都將得到通知。作為對這個通知的響應,每個觀察者都將即時更新自己的狀態,以與目標狀態同步,這種交互也稱為發布-訂閱(publish-subscribe)。目標是通知的發布者,它發出通知時並不需要知道誰是它的觀察者,可以有任意數目的觀察者訂閱它並接收通知。

  該模式包含四種角色:

  • 抽象被觀察者角色:也就是一個抽象主題,它把所有對觀察者對象的引用保存在一個集合中,每個主題都可以有任意數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者角色。一般用一個抽象類和接口來實現。
  • 抽象觀察者角色:為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
  • 具體被觀察者角色:也就是一個具體的主題,在集體主題的內部狀態改變時,所有登記過的觀察者發出通知。
  • 具體觀察者角色:實現抽象觀察者角色所需要的更新接口,一邊使本身的狀態與制圖的狀態相協調。

技術分享圖片

觀察者模式結構圖

2、實現

抽象觀察者:

package directoryobserver;

import org.apache.commons.io.*;
import org.apache.commons.io.filefilter.*;
import org.apache.commons.io.monitor.*;
import org.apache.log4j.*;

import java.io.*;

public class DirectoryObserver
{
    private final IOFileFilter doneFileFilter;
    private FileAlterationObserver observer;
    private Logger log = Logger.getLogger(DirectoryObserver.class);
    private File directory;
    static boolean isStarting = false;

    public DirectoryObserver(File directory)
    {
        this.directory = directory;
        doneFileFilter = FileFilterUtils.suffixFileFilter(".done");
        observer = new FileAlterationObserver(directory, doneFileFilter);
    }
    
    public void addListener(NewFileListener listener)
    {
        observer.addListener(new DoneFileAlterationListener(listener));
    }
    
    public void start() throws Exception
    {
        FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer);

        isStarting = true;
        monitor.start();
        log.info("Watching for new files...");

        for (FileAlterationListener listener : observer.getListeners())
        {
            for (File doneFile : FileUtils.listFiles(directory, doneFileFilter, FileFilterUtils.directoryFileFilter()))
            {
                listener.onFileCreate(doneFile);
            }
        }
        isStarting = false;
    }
}

具體觀察者:

package directoryobserver;

import org.apache.commons.codec.digest.*;
import org.apache.commons.io.monitor.*;
import org.apache.log4j.*;

import java.io.*;

final class DoneFileAlterationListener implements FileAlterationListener
{
    private final NewFileListener newFileListener;
    private Logger log = Logger.getLogger(DoneFileAlterationListener.class);
    
    public DoneFileAlterationListener(NewFileListener newFileListener)
    {
        this.newFileListener = newFileListener;
    }
    
    @Override
    public void onStop(FileAlterationObserver observer)
    {
    }

    @Override
    public void onStart(FileAlterationObserver observer)
    {
    }

    @Override
    public void onFileDelete(File file)
    {
    }

    @Override
    public void onFileCreate(File doneFile)
    {
        try
        {
            log.info("Received done file " + doneFile.getName());

            if(doneFile.exists())
            {
                String[] tokens = doneFile.getName().split("\\.");
                if (tokens.length != 3)
                {
                    newFileListener.onError(doneFile, new WrongDoneFileName());
                    return;
                }

                String name = tokens[0];
                String md5 = tokens[1];

                File newFile = new File(doneFile.getParent() + File.separator + name);

                String calculatedMd5 = DigestUtils.md5Hex(new FileInputStream(newFile));

                if (calculatedMd5.equals(md5))
                {
                    newFileListener.onNewFile(newFile);
                }
                else
                {
                    newFileListener.onChecksumMismatch(newFile, doneFile);
                }
            }
            else if(!DirectoryObserver.isStarting)
            {
                newFileListener.onError(doneFile, new DoneFileException());
            }
        }
        catch (Exception e)
        {
            newFileListener.onError(doneFile, e);
        }
        finally
        {
            doneFile.delete();
        }
    }

    @Override
    public void onFileChange(File file)
    {
    }

    @Override
    public void onDirectoryDelete(File directory)
    {
    }

    @Override
    public void onDirectoryCreate(File directory)
    {
    }

    @Override
    public void onDirectoryChange(File directory)
    {
    }
}

  觀察者觀察路徑中的文件,當文件發生改變時,觀察者可以得到通知,並進行相應的操作。

3、效果

  • 可以實現表示層和數據邏輯層的分離,並定義了穩定的消息更新傳遞機制,抽象了更新接口,使得可以有各種各樣不同的表示層作為具體觀察者角色。
  • 在觀察目標和觀察者之間建立一個抽象的耦合。
  • 支持廣播通信。
  • 符合“開閉原則”的要求。

文件路徑監控項目中觀察者模式的分析