簡體   English   中英

設計模式:子類調用基類

[英]Design pattern: child class calling base class

我有“處理程序”能夠觸發“經紀人”(一個做某事的對象 - 這里不重要)。

處理程序正在偵聽不同類型的事件:

  • TimeEvent:每10秒,10分鍾(...)
  • FileSystemEvent:復制/移動/刪除文件后
  • DbEvent:將記錄添加到數據庫表時
  • MailEvent:當我在Office 365郵箱中收到電子郵件時

每個處理程序必須:

  • 啟動和停止方法(開始/停止捕獲事件)
  • 關聯代理的實例
  • 一種“觸發”代理的方法(Process方法+特定的參數集)。

每個處理程序應該

  • 在引發特定事件時觸發關聯的代理

我想從基本的Handler類中觸發代理,因此我集中了我的邏輯(觸發事件,捕獲異常,管理線程等)。 但是,基本處理程序不知道如何調用代理(何時調用此函數,要將哪些參數發送到Process方法)>>只有專門的子處理程序才知道如何執行此操作。

我找到的唯一方法是在基本處理程序中實現一個接受Action參數的Execute方法......我不喜歡這種方法,因為它不是真正的直接(孩子需要調用基類,否則沒有任何反應)。 我希望找到一個更好的設計來處理這個問題。 另外我可以告訴你我的開發人員會告訴我他們不懂如何使用系統。

abstract class Handler : IHandler
{
    public IBroker Broker { get; protected set; }

    public event ProcessedEventHandler Processed;
    protected void OnProcessed(ProcessExecutionResult result) => Processed?.Invoke(this, result);

    public static IHandler Create(IBroker broker)
    {
        if (broker is ITimerTriggeredBroker)
            return new TimeHandler((ITimerTriggeredBroker)broker);
        return null;
    }

    protected Handler(IBroker broker)
    {
        if (broker == null) throw new ArgumentNullException(nameof(broker));
        Broker = broker;
    }

    public abstract void Start();
    public abstract void Stop();

    protected void Execute(Action action)
    {
        var res = new ProcessExecutionResult();
        try
        {
            action?.Invoke();
            res.IsSuccess = true;
        }
        catch (Exception ex)
        {
            res.Exception = ex;
            res.IsSuccess = false;
        }
        finally
        {
            OnProcessed(res);
        }
    }
}

TimeHandler(處理與時間相關的事件)

class TimeHandler : Handler
{
    private readonly Timer _timer;
    private readonly DateTime _start;
    private readonly TimeSpan _frequency;

    public TimeHandler(ITimerTriggeredBroker broker)
        : base(broker)
    {
        _start = broker.Trigger.StartTime;
        _frequency = broker.Trigger.Frequency;
        _timer = new Timer(_ => Execute(broker.Process));
    }
 (...)
}

FileHandler(處理FileSystem相關事件)

class FileHandler : Handler
{
    private readonly FileSystemWatcher _watcher = new FileSystemWatcher();

    public FileHandler(IFileTriggeredBroker broker)
        : base(broker)
    {
        if (!Directory.Exists(broker.Trigger.DirectoryPath))
            throw new DirectoryNotFoundException("Unable to locate the supplied directory");

        _watcher.Filter = broker.Trigger.Filter;
        _watcher.Path = broker.Trigger.DirectoryPath;
        _watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName |
                                NotifyFilters.FileName;

        _watcher.Created += (s, a) =>
        {
            if (IsCopied(a.FullPath)) Execute(() => broker.Process(a.FullPath));
        };
    }

您要實現的目標有幾個方面:

  1. 架構應易於理解,並由程序員遵循。 它應該在他們編程時指導他們並保護他們不犯錯誤。

  2. 它應該是健壯的。 例如,您應該保證處理在監視文件夾中創建的每個文件。

在我的回答中,我將忽略健壯性方面。 請認真看待這個。 FileSystemWatcher無法保證提供所有創建的文件。 此外,建議您在單獨的線程中分離FileSystemWatcher事件的處理,或者為此使用.NET任務。

此外,我認為您應該考慮使用隊列,如Microsoft MQ,Azure Queue,RabbitMQ。 您可以直接執行此操作或使用MassTransit等系統。

下面我提出一個架構,它將使您的程序員更容易構建。

一般說明

將應用程序划分到文件夾或不同的程序集中,以便在框架和特定處理程序/代理之間進行明確分離。

解

對於每種類型的處理,我們創建一個特定的消息類,讓處理程序和代理實現一個特定於消息類型的通用接口。

我們將使用C#高級類型系統來確保它很難出錯,並且編譯器將幫助程序員使用正確的東西。 為此,我們使用基於消息類型類的通用接口和類。

主程序

在這里,我們將設置一個管理器,它將使用各自的消息注冊所有處理程序和代理。 這是一個獨立的例子,我建議你使用一個依賴注入系統,例如AutoFac來進一步優化它。

static void Main(string[] args)
{
    var manager = new Manager();
    manager.Register<FileHandlerMessage>(new FileHandler(), new FileBroker());
    manager.Register<TimeHandlerMessage>(new TimeHandler(), new TimeBroker());

    manager.Start();

    Console.ReadLine();

    manager.Stop();
}

經理

Manager類的作用是組織正確使用處理程序和代理。

class Manager
{
    private List<IGenericHandler> handlers = new List<IGenericHandler>();

    public void Register<M>(IHandler<M> handler, IBroker<M> broker) where M : Message
    {
        handlers.Add(handler);
    }

    public void Start()
    {
        foreach ( var handler in handlers )
        {
            handler.Start();
        }
    }
    public void Stop()
    {
        foreach (var handler in handlers)
        {
            handler.Stop();
        }
    }
}

消息

對於每種類型的代理,我們將定義一個特定的消息類,派生自一個公共基類:

abstract class Message
{
}

class FileHandlerMessage : Message
{
    public string FileName { get; set; }
}

處理程序

interface IGenericHandler
{
    void Start();
    void Stop();
}

interface IHandler<M> : IGenericHandler where M : Message
{
    void SetBroker(IBroker<M> broker);
}

class FileHandler : IHandler<FileHandlerMessage>
{
    private IBroker<FileHandlerMessage> broker;

    public FileHandler()
    {
    }

    public void SetBroker(IBroker<FileHandlerMessage> fileBroker)
    {
        this.broker = fileBroker;
    }

    public void Start()
    {
        // do something
        var message = new FileHandlerMessage();
        broker.Process(message);
    }

    public void Stop()
    {
        // do something
    }
}

class TimeHandler : IHandler<TimeHandlerMessage>
{
    private IBroker<TimeHandlerMessage> broker;

    public void SetBroker(IBroker<TimeHandlerMessage>  broker)
    {
        this.broker = broker;
    }
    public void Start()
    {
        // do something
        var message = new TimeHandlerMessage();
        broker.Process(message);
    }

    public void Stop()
    {
        // do something
        throw new NotImplementedException();
    }
}

經紀商

class FileBroker : IBroker<FileHandlerMessage>
{
    public void Process(FileHandlerMessage message)
    {
        throw new NotImplementedException();
    }
}

class TimeBroker : IBroker<TimeHandlerMessage>
{
    public void Process(TimeHandlerMessage message)
    {
        throw new NotImplementedException();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM