【问题标题】:Design pattern: child class calling base class设计模式:子类调用基类
【发布时间】:2016-08-01 04:30:13
【问题描述】:

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

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

  • 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));
        };
    }

【问题讨论】:

  • 你打算如何注入IHandler > Handler > TimedHandler
  • “只有专门的儿童处理程序知道如何做到这一点。”——听起来像是多态性/虚拟方法的工作。但无法确定,因为您提供了足够的细节来创建一个非常广泛的问题。代码片段没有提供足够的上下文,有很多方法可以解决这个含糊不清的问题。如果你得到一个好的答案,它只会在 cmets 中来回很多之后才会出现。否则,你只会得到很多随机猜测。请改进问题。从一个好的minimal reproducible example 开始,并准确地解释你的困扰。

标签: c# events design-patterns


【解决方案1】:

您要达到的目标有几个方面:

  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();
    }
}

【讨论】:

    猜你喜欢
    • 2015-04-18
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    • 1970-01-01
    相关资源
    最近更新 更多