更新:请参阅问题的结尾,了解我如何实施解决方案。
对于措辞不好的问题,我很抱歉,但我不确定如何最好地提出这个问题。我不确定如何设计一个可以重复使用的解决方案,其中大多数代码在每次实现时都完全相同,但实现的一部分每次都会改变,但遵循类似的模式。我试图避免复制和粘贴代码。
我们有一个内部数据消息系统,用于在不同机器上跨数据库更新表。我们正在扩展我们的消息服务以向外部供应商发送数据,我想编写一个简单的解决方案,如果我们决定向多个供应商发送数据,可以重复使用。代码将被编译为EXE并定期运行以将消息发送到供应商的数据服务。
以下是代码的大致概述:
public class OutboxManager
{
private List<OutboxMsg> _OutboxMsgs;
public void DistributeOutboxMessages()
{
try {
RetrieveMessages();
SendMessagesToVendor();
MarkMessagesAsProcessed();
}
catch Exception ex {
LogErrorMessageInDb(ex);
}
}
private void RetrieveMessages()
{
//retrieve messages from the database; poplate _OutboxMsgs.
//This code stays the same in each implementation.
}
private void SendMessagesToVendor() // <== THIS CODE CHANGES EACH IMPLEMENTATION
{
//vendor-specific code goes here.
//This code is specific to each implementation.
}
private void MarkMessagesAsProcessed()
{
//If SendMessageToVendor() worked, run this method to update this db.
//This code stays the same in each implementation.
}
private void LogErrorMessageInDb(Exception ex)
{
//This code writes an error message to the database
//This code stays the same in each implementation.
}
}
我想以这样一种方式编写这段代码,即我可以重新使用不会改变的部分,而不必诉诸于复制和粘贴并填写代码 SendMessagesToVendor()
。我希望开发人员能够使用 OutboxManager
并且已经编写了所有编写的数据库代码,但是被迫提供他们自己的向供应商发送数据的实现。
我确信有很好的面向对象原则可以帮助我解决这个问题,但我不确定哪一个最好用。
这是我最终选择的解决方案,受到启发 维克多的答案 和 里德的回答(和评论) 使用接口模型。所有相同的方法都存在,但现在它们隐藏在消费者可以根据需要更新的界面中。
直到我意识到我允许类的使用者插入自己的类进行数据访问时,我才意识到接口实现的强大功能(IOutboxMgrDataProvider
)和错误记录(IErrorLogger
)。虽然我仍然提供默认实现,因为我不希望这些代码发生变化,但消费者仍然可以使用自己的代码覆盖它们。除了写出多个构造函数(其中 我可以更改为命名和可选参数),它确实没有花很多时间来改变我的实现。
public class OutboxManager
{
private IEnumerable<OutboxMsg> _OutboxMsgs;
private IOutboxMgrDataProvider _OutboxMgrDataProvider;
private IVendorMessenger _VendorMessenger;
private IErrorLogger _ErrorLogger;
//This is the default constructor, forcing the consumer to provide
//the implementation of IVendorMessenger.
public OutboxManager(IVendorMessenger messenger)
{
_VendorMessenger = messenger;
_OutboxMgrDataProvider = new DefaultOutboxMgrDataProvider();
_ErrorLogger = new DefaultErrorLogger();
}
//... Other constructors here that have parameters for DataProvider
// and ErrorLogger.
public void DistributeOutboxMessages()
{
try {
_OutboxMsgs = _OutboxMgrDataProvider.RetrieveMessages();
foreach om in _OutboxMsgs
{
if (_VendorMessenger.SendMessageToVendor(om))
_OutboxMgrDataProvider.MarkMessageAsProcessed(om)
}
}
catch Exception ex {
_ErrorLogger.LogErrorMessage(ex)
}
}
}
//...interface code: IVendorMessenger, IOutboxMgrDataProvider, IErrorLogger
//...default implementations: DefaultOutboxMgrDataProvider(),
// DefaultErrorLogger()