在这种特定情况下,我应该如何对我的代码进行建模以最大限度地重用代码?[英] How should I model my code to maximize code re-use in this specific situation?

本文是小编为大家收集整理的关于在这种特定情况下,我应该如何对我的代码进行建模以最大限度地重用代码?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

更新:有关我如何实现解决方案的问题.

对不起,这个词不好的问题,但我不确定如何最好地问.我不确定如何设计一个可以重复使用的解决方案,而大多数代码在每次实施时都完全相同,但是一部分实现将每次都会改变,但要遵循类似的模式.我正在尝试避免复制和粘贴代码.

我们有一个内部数据消息系统,用于更新不同机器上数据库的表.我们正在扩展消息传递服务以将数据发送到外部供应商,我想编码一个简单的解决方案,如果我们决定将数据发送到多个供应商,则可以重新使用该解决方案.该代码将被编译成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并拥有已经编写的所有数据库代码,但被迫提供自己将数据发送给供应商的实现.

我敢肯定有一个良好的面向对象的原则可以帮助我解决这个问题,但是我不确定哪个是最好使用的.


这是我最终得到的解决方案,灵感来自 VICTOR的答案 reed的答案(和评论)使用接口模型.所有相同的方法都在那里,但是现在它们被藏为消费者可以在必要时可以更新的接口.

我没有意识到接口实现的功能,直到我意识到我允许该类的消费者插入自己的类别以获取数据访问(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()

推荐答案

我会说使用依赖性注入.基本上,您通过发送方法的抽象.

类似:

interface IVendorMessageSender
{
    void SendMessage(Vendor v);
}

public class OutboxManager 
{
    IVendorMessageSender _sender;

    public  OutboxManager(IVendorMessageSender sender)
    {
        this._sender = sender; //Use it in other methods to call the concrete implementation
    }

    ...
}

正如已经提到的另一种方法.

无论哪种情况:尝试从此类别中删除数据库检索代码.为此使用另一个抽象(即:将IDATAPROVIDER接口或类似的界面传递给构造函数).它将使您的代码更具测试.

其他推荐答案

有两种非常简单的方法:

  1. 制作OutboxManager一个抽象类,并提供每个供应商的子类.可以将SendMessagesToVendor标记为抽象,从而迫使每个供应商重新成熟.这种方法很简单,非常适合OO原理,并且还具有允许您为其他方法提供实现的优势,但是如果您想稍后允许,仍然允许对供应商特定版本进行覆盖.

  2. 有OutboxManager封装了一些其他类或接口,这些类别或接口提供了SendMessagesToVendor中所需的特定于供应商的信息.这很容易成为实现每个供应商的小接口,SendMessagesToVendor可以使用此接口实现来发送其消息.这具有允许您在此处编写一些代码的优点 - 可能会减少供应商之间的重复.它还可能允许您的SendMessagesToVendor方法更加一致,更容易测试,因为您只需要依靠此处所需的特定供应商功能.这也可以作为代表通过作为一种相关方法(但稍有不同)的方法实施(我个人更喜欢通过代表实现的界面).

其他推荐答案

如果将其作为抽象基类,因此必须继承它,您可以强制该方法在混凝土对象中实现.

using System;
using System.Collections.Generic;

public abstract class OutboxManagerBase
{
private List<string> _OutboxMsgs;

public 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.
}

protected abstract void SendMessagesToVendor();

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.
}
}



public class OutBoxImp1 : OutboxManagerBase
{
    protected override void SendMessagesToVendor()
    {
        throw new NotImplementedException();
    }
}

本文地址:https://www.itbaoku.cn/post/627702.html

问题描述

Updated: See end of question for how I implemented the solution.

Sorry for the poorly-worded question, but I wasn't sure how best to ask it. I'm not sure how to design a solution that can be re-used where most of the code is the exact same each time it is implemented, but part of the implementation will change every time, but follow similar patterns. I'm trying to avoid copying and pasting code.

We have an internal data messaging system for updating tables across databases on different machines. We're expanding our messaging service to send data to external vendors and I want to code a simple solution that can be re-used should we decide to send data to more than one vendor. The code will be compiled into an EXE and run on a regular basis to send messages to the vendor's data service.

Here's a rough outline of what the code does:

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.
    }
}

I want to write this code in such a way that I can re-use the parts that don't change without having to resort to copying and pasting and filling in the code for SendMessagesToVendor(). I want a developer to be able to use an OutboxManager and have all of the database code written already written, but be forced to supply their own implementation of sending data to the vendor.

I'm sure there are good object-oriented principles that can help me solve that problem, but I'm not sure which one(s) would be best to use.


This is the solution I ended up going with, inspired by Victor's answer and Reed's answer (and comments) to use an interface model. All of the same methods are there, but now they are tucked away into interfaces that the consumer can update if necessary.

I didn't realize the power of the interface implementation until I realized that I allow the consumer of the class to plug in their own classes for the data access (IOutboxMgrDataProvider) and error logging (IErrorLogger). While I still provide default implementations since I don't expect this code to change, it's still possible for the consumer to override them with their own code. Except for writing out multiple constructors (which I may change to named and optional parameters), it really didn't take a lot of time to change my implementation.

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

推荐答案

I would say use Dependecy Injection. Basically, you pass an abstraction of the send method.

Something like:

interface IVendorMessageSender
{
    void SendMessage(Vendor v);
}

public class OutboxManager 
{
    IVendorMessageSender _sender;

    public  OutboxManager(IVendorMessageSender sender)
    {
        this._sender = sender; //Use it in other methods to call the concrete implementation
    }

    ...
}

Another approach, as already mentioned, inheritance.

In either case: try to remove DB retrieval code from this class. Use another abstraction for that (ie: passing an IDataProvider interface or something like that to the constructor). It will make your code more testable.

其他推荐答案

There are two very simple approaches:

  1. Make OutboxManager an abstract class, and provide a subclass per vendor. The SendMessagesToVendor can be marked abstract, forcing it to be reimplemented by each vendor. This approach is simple, fits OO principles well, and also has the advantage of allowing you to supply the implementation for the other methods, but still allowing overriding for a vendor specific version if you want to allow that later.

  2. Have OutboxManager encapsulate some other class or interface which provides the vendor-specific information required in SendMessagesToVendor. This could easily be a small interface that is implemented per-vendor, and SendMessagesToVendor could use this interface implementation to send its messages. This has the advantage of allowing you to write some of the code here - potentially reducing duplication across vendors. It also potentially allows your SendMessagesToVendor method to be more consistent, and more easily testable, since you only have to rely on the specific vendor functionality required here. This could also, potentially, be implemented as a delegate passed in as a related (but slightly different) approach (I personally prefer an interface to be implemented over a delegate, however).

其他推荐答案

If you make this an abstract base class so it has to be inherited you can force this method to be implemented in the concrete object.

using System;
using System.Collections.Generic;

public abstract class OutboxManagerBase
{
private List<string> _OutboxMsgs;

public 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.
}

protected abstract void SendMessagesToVendor();

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.
}
}



public class OutBoxImp1 : OutboxManagerBase
{
    protected override void SendMessagesToVendor()
    {
        throw new NotImplementedException();
    }
}