Design di classe: più chiamate da un metodo o una chiamata da più metodi?

2

Recentemente ho lavorato su un codice che si interfaccia con un CMS che utilizziamo e mi ha presentato una domanda sul design della classe che ritengo sia applicabile in una serie di situazioni. In sostanza, ciò che sto facendo è estrarre informazioni dal CMS e trasformare queste informazioni in oggetti che posso utilizzare in modo programmatico per altri scopi. Questo consiste di due passaggi:

  1. Recupera i dati dal CMS (abbiamo un DAL che uso, quindi questo è essenzialmente solo specificando quali dati dal CMS voglio - nessuna logica di connessione o qualcosa del genere)
  2. Mappare i dati analizzati sui miei [C #] oggetti

Ci sono fondamentalmente due modi in cui posso avvicinarmi a questo:

Una chiamata da più metodi

public void MainMethodWhereIDoStuff()
{
    IEnumerable<MyObject> myObjects = GetMyObjects();
    // Do other stuff with myObjects
}

private static IEnumerable<MyObject> GetMyObjects()
{
    IEnumerable<CmsDataItem> cmsDataItems = GetCmsDataItems();
    List<MyObject> mappedObjects = new List<MyObject>();
    // do stuff to map the CmsDataItems to MyObjects
    return mappedObjects;
}

private static IEnumerable<CmsDataItem> GetCmsDataItems()
{
    List<CmsDataItem> cmsDataItems = new List<CmsDataItem>();
    // do stuff to get the CmsDataItems I want
    return cmsDataItems;
}

Chiamate multiple da un metodo

public void MainMethodWhereIDoStuff()
{
    IEnumerable<CmsDataItem> cmsDataItems = GetCmsDataItems();
    IEnumerable<MyObject> myObjects = GetMyObjects(cmsDataItems);
    // do stuff with myObjects
}

private static IEnumerable<MyObject> GetMyObjects(IEnumerable<CmsDataItem> itemsToMap)
{
    // ...
}

private static IEnumerable<CmsDataItem> GetCmsDataItems()
{
    // ...
}

Sono tentato di dire che quest'ultimo è migliore del primo, dato che GetMyObjects non dipende da GetCmsDataItems ed è esplicito nel metodo chiamante i passi che vengono eseguiti per recuperare gli oggetti (sono preoccupato che il primo approccio è una specie di versione orientata agli oggetti del codice spaghetti).

D'altra parte, i due metodi helper non saranno mai usati al di fuori della classe, quindi non sono sicuro che sia importante sapere se uno dipende dall'altra. Inoltre, mi piace il fatto che nel primo approccio gli oggetti possano essere recuperati da una linea - molto probabilmente a chi lavora con il metodo principale non interessa come vengono recuperati gli oggetti, hanno solo bisogno di recuperare gli oggetti, e il " "i metodi di supporto concatenati a margherita" nascondono i passaggi esatti necessari per recuperarli (in pratica, ho effettivamente qualche altro metodo ma sono ancora in grado di recuperare la raccolta di oggetti che voglio in una riga).

Uno di questi metodi è giusto e l'altro è sbagliato? O è semplicemente una questione di preferenza o dipendente dal contesto?

    
posta Andrew 02.05.2012 - 17:14
fonte

6 risposte

3

La chiave è "non verrà mai utilizzata al di fuori della classe"

La struttura non ha importanza quando l'implementazione rimane privata. Sei libero di modificarlo / modificarlo come vuoi perché nessuno dipenderà dalla struttura.

Membri pubblici / interfacce OTOH, do importa. Quando esponi un membro pubblicamente / internamente, dovresti avere una ragione esplicita per farlo, perché qualcuno potrebbe / dipenderà da esso in futuro. Questa è la linea di demarcazione tra lo sviluppo di applicazioni e API. Nelle API il controllo dell'accesso diventa molto importante perché qualsiasi membro / interfaccia da cui dipendono le persone è uno che non può essere modificato / rimosso senza infrangere il codice di qualcuno.

In sostanza:

your private parts are nobody's business but your own

    
risposta data 02.05.2012 - 18:21
fonte
2

Il precedente esempio fornisce più accuratamente la sezione di codice "principale" (che presumo vede il maggior traffico degli sviluppatori) da problemi secondari, quindi credo che sia l'approccio migliore. Tuttavia, come te, non mi piace avere un sacco di metodi di supporto che intasano le mie classi principali. Per renderlo un po 'più pulito, prendi in considerazione la possibilità di separare i metodi di supporto in una classe di supporto. Puoi farlo con una classe internal separata o se vuoi che la classe sia privata per i problemi di incapsulamento, crea un file di classe parziale e rendi il tuo helper una classe nidificata privata. Mi piace così:

// helper class definition file
public partial class MyClass
{
    // private class modifier only works on nested classes like this one
    // only the parent class MyClass will be able to see this class
    private static class MyHelper
    {
        public static IEnumerable<MyObject> GetMyObjects() { ... }
        private static IEnumerable<CmsDataItem> GetCmsDataItems() { ... }
    }
}

// normal, high traffic file
public partial class MyClass
{
    public void MainMethodWhereIDoStuff()
    {
        IEnumerable<MyObject> myObjects = MyHelper.GetMyObjects();
    }

    // removed side-concerns of getting / transforming data into MyObjects
}

Qualcosa di simile non è strettamente necessario, ma può aiutare a chiarire le preoccupazioni se questo è importante per te.

Come nota a margine, per le trasformazioni di dati (ad esempio da CmsDataItem a MyObject), potresti prendere in considerazione l'utilizzo di una libreria di mappatura come AutoMapper (che è disponibile tramite NuGet in Visual Studio). Sebbene sia ancora necessario mantenere le trasformazioni di mappatura, renderà semanticamente più chiaro al tuo codice che stai solo mappando le proprietà da un oggetto a un altro. Inoltre AutoMapper eseguirà automaticamente molti mapping più semplici.

    
risposta data 02.05.2012 - 18:31
fonte
1

Il primo esempio è ciò che in genere viene chiamato Method Chain . Non c'è nulla di intrinsecamente sbagliato nelle catene di metodi, anche se le catene lunghe possono essere difficili da gestire e capire, anche se potrebbero rendere il codice più chiaro e chiaro da leggere. Se necessario, a volte devi solo tollerarli, tuttavia a volte può essere preferibile sostituire una catena di metodi con un metodo o una classe helper che gestisca tutte le chiamate concatenate, in particolare se ti trovi a dover utilizzare un catena particolare spesso.

Il secondo esempio va bene per metodi relativamente piccoli che non devono fare più di alcune cose. Tuttavia, con l'aumentare del numero di chiamate, il codice diventa spesso meno chiaro e più difficile da leggere. Ancora una volta, non c'è nulla di intrinsecamente negativo nell'avere un metodo con più chiamate, tuttavia il rischio è che per alcune operazioni si possa trovare il codice duplicato. Se il metodo cresce oltre una manciata di righe, è probabile che ci siano altri metodi e forse anche una classe in attesa di essere scoperti.

Ciò che è importante, è considerare se la tecnica che usi sia appropriata all'attività coinvolta e se avrà un impatto sull'interfaccia della classe a cui appartiene il metodo. Mentre l'obiettivo immediato è soddisfare un requisito, altri obiettivi possono includere anche l'estensibilità e il basso costo di manutenzione. L'esperienza con le tue particolari applicazioni ti insegnerà a privilegiare una tecnica piuttosto che un'altra.

    
risposta data 03.05.2012 - 03:51
fonte
1

Penso che dovresti considerarlo più in termini di manutenibilità e punto di vista della correttezza del codice.

Ad esempio, nel primo caso getMyObjects e getCMSObjects sono accoppiati in modo che gli unici oggetti che getMyObjects sarà mai in grado di mappare siano gli oggetti restituiti da getCmsObjects, mentre nel secondo esempio il metodo funziona su qualsiasi argomento che riceve.

Se la tua implementazione di getMyObjects è abbastanza flessibile da consentire qualsiasi elenco di oggetti come input, dovresti errare per il secondo esempio, poiché è più flessibile e non costa molto. Tuttavia, se la logica in getMyObjects funziona solo su un elenco di oggetti cms restituiti di recente, l'inserimento di un parametro fungerà solo da luogo per consentire di introdurre errori (passando il parametro errato), quindi preferiresti invece la versione senza parametri.

    
risposta data 04.05.2012 - 00:21
fonte
0

Non penso che nessuno dei due approcci sia giusto o sbagliato. È solo una questione di preferenza. Come hai detto, entrambi i metodi sono privati e quindi non incideranno sull'usabilità esterna. Ciò che è importante è che è chiaro ciò che è necessario per svolgere il compito necessario e sembra che entrambi gli approcci siano chiari.

    
risposta data 02.05.2012 - 17:38
fonte
0
  • Sarà praticamente impossibile darti la "risposta giusta" finché non cambierai i nomi dei tuoi metodi e darai loro un significato semantico migliore piuttosto che semplicemente "DoStuff".

  • Solo così saremo in grado di identificare le responsabilità e separarle rispettivamente.

risposta data 04.05.2012 - 00:56
fonte

Leggi altre domande sui tag