Aggiornamento: vedere la fine della domanda su come ho implementato la soluzione.Come dovrei modellare il mio codice per massimizzare il riutilizzo del codice in questa situazione specifica?
Ci scusiamo per la domanda mal formulata, ma non ero sicuro del modo migliore per chiederlo. Non sono sicuro di come progettare una soluzione che può essere riutilizzata dove la maggior parte del codice è la stessa identica ogni volta che viene implementata, ma parte dell'implementazione cambierà ogni volta, ma seguirà modelli simili. Sto cercando di evitare di copiare e incollare il codice.
Disponiamo di un sistema di messaggistica interno per l'aggiornamento di tabelle su database su macchine diverse. Stiamo espandendo il nostro servizio di messaggistica per inviare dati a fornitori esterni e voglio codificare una soluzione semplice che può essere riutilizzata nel caso decidessimo di inviare dati a più di un fornitore. Il codice verrà compilato in un file EXE e verrà eseguito regolarmente per inviare messaggi al servizio dati del fornitore.
Ecco un abbozzo di quello che fa il codice:
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.
}
}
voglio scrivere questo codice in modo tale che posso riutilizzare le parti che non cambiano, senza dover ricorrere alla copia e incollare e compilare il codice per SendMessagesToVendor()
. Voglio che uno sviluppatore sia in grado di utilizzare uno OutboxManager
e che abbia già scritto tutto il codice del database scritto, ma che sia costretto a fornire la propria implementazione dei dati di invio al fornitore.
Sono sicuro che ci sono buoni principi orientati agli oggetti che possono aiutarmi a risolvere questo problema, ma non sono sicuro di quale (i) sarebbe meglio usare.
Questa è la soluzione che ho finito per andare con, ispirato da Victor's answer e Reed's answer (and comments) di utilizzare un modello di interfaccia. Esistono tutti gli stessi metodi, ma ora sono nascosti in interfacce che il consumatore può aggiornare se necessario.
Non mi sono reso conto della potenza dell'implementazione dell'interfaccia fino a quando non mi sono reso conto che consentivo al consumatore della classe di collegare le proprie classi per l'accesso ai dati (IOutboxMgrDataProvider
) e la registrazione degli errori (IErrorLogger
). Mentre continuo a fornire implementazioni predefinite poiché non mi aspetto che questo codice cambi, è comunque possibile che il consumatore li sostituisca con il proprio codice. Tranne che per la scrittura di più costruttori (che è I may change to named and optional parameters), non ci è voluto molto tempo per cambiare la mia implementazione.
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()
@Victor Grazie per il suggerimento. Qual è il vantaggio di Dipendenza Iniezione sul modello astratto? –
@ Ben: Sono davvero solo due opzioni per lavorare. DI ha il vantaggio di consentire ai tuoi clienti di lavorare sempre con una singola classe e potenzialmente di essere più facili a scopo di test. L'utilizzo dell'ereditarietà è potenzialmente più chiaro, dal momento che l'utente lavora con una singola classe, e il Principio di Sottoscrizione di Liskov dice che possono essere usati in modo intercambiabile dal lato dell'utente - quindi una classe contro due per l'utente. –
@ Ben: La mia risposta era fondamentalmente solo questa: l'opzione 1 è "modello astratto"/ereditarietà, l'opzione 2 è DI. –