¿Cómo debo modelar mi código para maximizar la reutilización del código en esta situación específica?

Actualizado: vea el final de la pregunta para saber cómo implementé la solución.

Perdón por la pregunta mal redactada, pero no estaba seguro de cómo hacerla mejor. No estoy seguro de cómo diseñar una solución que se pueda reutilizar donde la mayor parte del código sea exactamente el mismo cada vez que se implemente, pero parte de la implementación cambiará cada vez, pero seguirá patrones similares. Estoy tratando de evitar copiar y pegar código.

Tenemos un sistema interno de mensajería de datos para actualizar tablas en bases de datos en diferentes máquinas. Estamos ampliando nuestro servicio de mensajería para enviar datos a proveedores externos y quiero codificar una solución simple que pueda reutilizarse si decidimos enviar datos a más de un proveedor. El código se compilará en un EXE y se ejecutará regularmente para enviar mensajes al servicio de datos del proveedor.

Aquí hay un resumen de lo que hace el código:

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

Quiero escribir este código de tal manera que pueda reutilizar las partes que no cambian sin tener que recurrir a copiar, pegar y completar el código paraSendMessagesToVendor(). Quiero que un desarrollador pueda usar unOutboxManager y tener todo el código de la base de datos escrito ya escrito, pero se verá obligado a proporcionar su propia implementación de envío de datos al proveedor.

Estoy seguro de que hay buenos principios orientados a objetos que pueden ayudarme a resolver ese problema, pero no estoy seguro de cuál (es) sería mejor usar.

Esta es la solución con la que terminé, inspirada enLa respuesta de Victor yLa respuesta de Reed (y comentarios) para usar un modelo de interfaz. Todos los mismos métodos están ahí, pero ahora están escondidos en interfaces que el consumidor puede actualizar si es necesario.

No me di cuenta del poder de la implementación de la interfaz hasta que me di cuenta de que permitía que el consumidor de la clase conectara sus propias clases para el acceso a los datos (IOutboxMgrDataProvider) y registro de errores (IErrorLogger) Si bien todavía proporciono implementaciones predeterminadas, ya que no espero que este código cambie, aún es posible que el consumidor las anule con su propio código. Excepto por escribir múltiples constructores (quePuedo cambiar a parámetros con nombre y opcionales), realmente no me llevó mucho tiempo cambiar mi implementación.

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

Respuestas a la pregunta(7)

Su respuesta a la pregunta