我有“处理程序”能够触发“经纪人”(做某事的对象-在这里并不重要)。
处理程序正在侦听不同类型的事件:
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));
};
}
最佳答案
您要实现的目标有几个方面:
该架构应易于理解,并为程序员所遵循。它应该在他们编程时为他们提供指导,并防止他们犯错。
它应该很健壮。例如,您应保证已处理在监视文件夹中创建的每个文件。
在我的回答中,我将忽略健壮性方面。请认真看这个。不保证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();
}
}