设计模式:子类调用基类

时间:2016-08-01 04:30:13

标签: c# events design-patterns

我有“处理程序”能够触发“经纪人”(一个做某事的对象 - 这里不重要)。

处理程序正在侦听不同类型的事件:

  • TimeEvent:每10秒,10分钟(...)
  • FileSystemEvent:文件复制/移动/删除后
  • DbEvent:将记录添加到数据库表
  • MailEvent:当我在Office 365邮箱中收到电子邮件时

每个处理程序必须具有:

  • 启动和停止方法(开始/停止捕获事件)
  • 关联经纪人的实例
  • 一种“触发”代理的方法(处理方法+特定的参数集)。

每个处理程序都应该

  • 在引发特定事件时触发关联的代理
  

我想从基本的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 个答案:

答案 0 :(得分:0)

您要实现的目标有几个方面:

  1. 架构应易于理解,并由程序员遵循。它应该在他们编程时引导他们并保护他们不会犯错误。

  2. 应该很健壮。例如,您应该保证处理在监视文件夹中创建的每个文件。

  3. 在我的回答中,我将忽略健壮性方面。请认真看待这个。 FileSystemWatcher无法保证提供所有创建的文件。此外,建议您在单独的线程中分离FileSystemWatcher事件的处理,或者为此使用.NET任务。

    此外,我认为您应该考虑使用队列,如Microsoft MQ,Azure Queue,RabbitMQ。您可以直接执行此操作,也可以使用MassTransit等系统。

    下面我提出一个架构,它将使程序员更容易构建。

    一般说明

    将应用程序划分到文件夹或不同的程序集中,以便在框架和特定处理程序/代理之间明确区分。

    Solution

    对于每种类型的处理,我们创建一个特定的消息类,让处理程序和代理实现特定于消息类型的通用接口。

    我们将使用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();
        }
    }