Domanda di progettazione riguardante l'estensibilità

1

Sto costruendo una nuova classe che si occupa di decidere quale classe dovrebbe elaborare le richieste da un servizio web. Sto cercando una soluzione per un problema che sto avendo riguardo all'estensibilità con questa nuova classe. Ogni richiesta verrà elaborata in modo diverso e la logica per elaborarle ciascuna si troverà nella propria classe. Il problema che spero di risolvere è come progettare le classi in modo tale che l'aggiunta di un nuovo tipo di richiesta non richieda modifiche al codice esistente. Considera il codice che ho di seguito. Per favore perdona qualsiasi refuso nei seguenti esempi:

public interface IRequestData
{

}

public class OldRequestData : IRequestData
{
    // Some properties holding data.
}

public class NewRequestData : IRequestData
{
    // Some properties holding data.
}

public class ProcessOldRequestData
{
    public void Process(OldRequestData Data)
    {
        // Some code to process the request.
    }
}

public class ProcessNewRequestData
{
    public void Process(NewRequestData Data)
    {
        // Some code to process the request.
    }
}

public class RequestProcessor
{
    public void ProcessRequest(IRequestData Data)
    {
        // Get and load base type of Data

        // Based on if it's OldRequestData, NewRequestData, or *FutureRequestData*,
        // Instantiate the appropriate class to process the request.
    }
}

Questo è il modello con cui sto attualmente lavorando. Il problema è che ogni nuovo tipo di richiesta richiede modifiche al RequestProcessor per chiamare la nuova classe. C'è un modo per far sì che questa classe "sappia" quale classe deve chiamare per elaborare ogni tipo di richiesta? Se c'è un modo migliore di gestirlo, sarei molto aperto per altri suggerimenti.

Grazie in anticipo per il tuo contributo.

    
posta Tama198 24.06.2015 - 17:06
fonte

3 risposte

0

Ci sono due modi per farlo.

  1. Il modo OO: rendi le tue classi OldRequestData e NewRequestData più attive e permetti a loro di implementare un metodo che sta eseguendo il comportamento desiderato. Ciò significa che i tuoi metodi Process verranno inseriti nelle tue classi IRequestData . Il vantaggio è che ora puoi aggiungere le classi implementa IRequestData e lasciare che implementino i corrispondenti metodi Process . L'inconveniente qui viene attivato non appena si desidera avere un metodo diverso per il metodo Process per ogni classe IRequestData (ad esempio due modi diversi di elaborare OldRequestData e anche due diversi modi di elaborare NewRequestData )

  2. Risolvere gli svantaggi della prima soluzione significa immergersi nel Problema di espressione (anche se usiamo dinamicamente lingua digitata qui). Ci sono diversi modi per risolvere questo problema, ma portano a una maggiore complessità. Devi pesare i benefici contro la complessità. Se non vuoi le classi IRequestData , dovresti rimanere con la prima soluzione.

risposta data 24.06.2015 - 17:43
fonte
4

crea una collezione (mappa, dizionario, ecc.) di richieste agli oggetti. In ProcessRequest, si scorre la collezione per trovare una voce che corrisponda alla richiesta di input e chiamare l'oggetto associato.

Quindi devi solo compilare la raccolta, ma questo può essere fatto leggendo la configurazione o facendo in modo che ciascuna classe RequestData si registri all'avvio (aggiungendosi alla raccolta insieme all'insieme di richieste che può gestire).

ovviamente, se devi creare una nuova classe 'FutureRequestData' con logica personalizzata, dovrai aggiornare il tuo codice. Non c'è modo di aggirare questo a meno che non si esegua codice arbitrario che viene letto da config (ad esempio un linguaggio di scripting) e si ha un motore o un compilatore che può causare l'esecuzione di questo codice.

    
risposta data 24.06.2015 - 17:20
fonte
0

Guarda utilizzando Fabbrica astratta , Builder e modelli di metodo di fabbrica.

Questi ti permetteranno di fare qualcosa di simile al seguente (ci scusiamo per eventuali errori di sintassi):

IRequestData rd = RequestDataBuilder.Build('old').GetData();
// not strictly necessary but follow the pattern throughout
IRequestProcessor rp = RequestProcessorBuilder.Build().GetProcessor();

su cui ora puoi fare

rp.ProcessRequest(rd);

senza sapere - o preoccuparti - su quale (vecchio o nuovo) set di dati o processore stai lavorando.

Il RequestProcessorBuilder può capire quale processore di richieste costruire in base agli argomenti passati al metodo Build () o ad altri parametri usati dal builder.

Quindi il valore predefinito potrebbe essere un ProcessSpeed 'veloce', ma possiamo anche specificare una velocità 'lenta', nel qual caso viene restituito un oggetto diverso. È importante sottolineare che il chiamato non interessa, dal momento che interagisce solo con un'interfaccia.

IRequestProcessor rp = RequestProcessorBuilder.Build()
                         .ProcessSpeed('slow') 
                         .GetProcessor();

In alternativa, la decisione su quale oggetto il costruttore può restituire può provenire da config (file, db, dovunque).

Da quei link (che hanno esempi), una breve descrizione:

  • Fabbrica astratta: fornisce un'interfaccia per la creazione di famiglie di oggetti correlati o dipendenti senza specificare le loro classi concrete
  • Builder: separa la costruzione di un oggetto complesso dalla sua rappresentazione, consentendo allo stesso processo di costruzione di creare varie rappresentazioni
  • Metodo factory: definisce un'interfaccia per la creazione di un singolo oggetto, ma lascia che le sottoclassi decidano quale classe istanziare. Il metodo Factory consente a una classe di posticipare l'istanza alle sottoclassi
risposta data 24.06.2015 - 17:56
fonte