2009-06-23 1 views
7

Tendo a scrivere con successo applicazioni compatte in grado di incapsulare molte logiche di business in modo semplice e non ridondante. Tendo a creare piccoli metodi, ma con il tempo arrivo a metodi che hanno troppi parametri. So che ogni codice richiede il suo design, ma vedo un anti-pattern nel mio comportamento e non sono sicuro quale sarebbe il modo migliore per combatterlo.Come evitare molti parametri nei metodi critici delle mie applicazioni?

Una situazione tipica sarebbe qualcosa di simile:

public loanStructure CalculateSomeComplicatedInterestRate(enum clientType, 
     int totalAmout, bool useTaxes, bool doYearlySummary, bool doNotReloadClient, etc) 
    { 
     loanStructure ret = new loanStructure(); 
     if (doNotReloadClient) ... ; 
     ... 
     if (doYearlySummary) GroupResults(ret); 
     return ret; 
    } 

e all'interno del metodo, un albero di chiamate in avanti le impostazioni booleane (doYearlySummary, doNotReloadClient, ecc) a diverse regole di business che agisce sul calcolo.

IE, il problema non risiede sul fatto che tutti i parametri possono essere incapsulati in un oggetto (bigParameterStructure) ... Io non trovo a mio agio con il modello masterMethod, ma facendo sovraccarichi quali CalculateSomeComplicatedInterestRateMonthly e CalculateSomeComplicatedInterestRateYearly sarebbe solo nascondere un private CalculateSomeComplicatedInterestRate .... qualche problema !!

di progettazione object-orientare grossolano avrebbe aiutato ... ma ho ancora finale per avere questo tipo di metodi da qualche parte sulla miei oggetti ...

Bene ragazzi ... ogni aiuto è benvenuto.

Pablo.

+0

La logica aziendale è quella che è. Se è complicato, è complicato, non è colpa tua. Mi sembra soddisfacente, a meno che non abbiate esempi di questo anche nel codice di logica non commerciale. –

risposta

4

Se ci si trova a passare da alcuni insiemi di più parametri, prendere in considerazione la memorizzazione del codice in un metodo privato complicato e fornire alcuni metodi pubblici che richiedono meno parametri e chiamano quello privato fornendo i valori predefiniti appropriati per una parte di parametri. Questo pulirà l'interfaccia pubblica.

Quindi, a meno che non sia davvero impossibile farlo per ragioni di prestazioni (che non è probabile), suddividere il metodo privato in chiamate ad altri metodi privati, per mantenere il codice più leggibile. Puoi sbarazzarti dei commenti in questo modo, scegliendo i nomi dei metodi auto-descrittivi.

D'altra parte, se alcuni dei parametri non riguardano COSA fare, ma COME farlo (cioè si può avere un enum che passa tra il compounding semplice e continuo), si consideri l'uso del polimorfismo e l'implementazione di due classi un'interfaccia comune, ciascuno in un modo diverso, OPPURE delegando questa funzionalità pezzo a un componente della classe principale che può essere modificato da questa a quella implementazione (a'la "iniezione di dipendenza"). Il secondo approccio renderà il tuo codice più flessibile, in quanto gli utenti della tua classe saranno in grado di fornire i propri componenti e modificare il comportamento della classe principale senza toccare il suo codice.

Si dovrebbe anche pensare a chi usa la classe. Forse la lista dei parametri nel tuo metodo è così lunga, perché il metodo è cresciuto nel tempo per soddisfare le diverse esigenze dei diversi utenti ("utente" in questo contesto significa un altro pezzo di codice, anche scritto da te)? Se è così, per lo meno vorrei creare un metodo per uno scopo, un altro metodo per un altro scopo. Possono ancora condividere codice comune. Idealmente, vorrai avere due classi che servono a due scopi, per poterle modificare in futuro senza rompere le altre funzionalità.

2

Generalmente creo le strutture "filtro" che posso passare a quei metodi, quindi nel tuo caso.

(rapido e sporco, non la qualità della produzione.)

public struct InterestRateCalculationFilter 
{ 
    public enum ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    public bool UseTaxes {get;set;} 
    public bool DoYearlySummary {get;set;} 
    public bool DoNotReloadClient {get;set;} 

    //Constructors go here, set defaults, etc. 
} 

Usage:

InterestRateCalculationFilter filter = new InterestRateCalculationFilter(); 
filter.ClientType = Customer; 
filter.TotalAmount = 700; 
filter.UserTaxes = true; 
filter.DoYearlySummary = false; 

LoanStructure loanStructure = CalculateSomeComplicatedInterestRate(filter); 

Questo non è sempre una buona idea, ma lo faccio andare ad esso abbastanza spesso, quando il parametro le liste ottengono più di dieci articoli e non necessariamente rientrano in una struttura di oggetti.

+1

Meglio non lasciare http://stackoverflow.com/users/23354/marc-gravell vedere quelle strutture, o sarà su di te come un'eruzione ;-) tuttavia, trovo la classe piuttosto che la struct più utile (perché struct è passato per copia) in questo caso ** _ if _ ** il callee modifica i valori di input, altrimenti devi passare per ref. Guarda anche l'inizializzazione dell'oggetto (csc> = 3 credo) per tagliare il grasso durante l'uso. – si618

+0

Richiami un buon punto. Ho sempre usato le strutture per rappresentare questi tipi di cose, anche se trovo che molte persone dicono di usare le classi. Ma mi fa chiedere dove le strutture si adattano allo spazio di sviluppo delle applicazioni di oggi. A Google !! –

+0

Sono d'accordo sulla sintassi di inizializzazione dell'oggetto, ma non ha specificato una lingua nella sua domanda, e non ho voluto lanciare una curva troppo grande con la sintassi. Nota la dichiarazione di non responsabilità :) –

1

Questo approccio ha alcuni aspetti negativi, ma un modo per semplificare la lista degli argomenti è quello di creare una classe di "argomenti" che ha proprietà per contenere tutti i parametri che possono essere passati in.

Per esempio (C#) :

public class InterestCalculationArguments // or maybe a struct... 
{ 
    public EClientType ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    // etc. 
} 

una cosa bella di questo approccio è che si può creare una sottoclasse della classe argomenti di estendere gli argomenti che possono essere passati in, oppure si può anche aggiungere un "NameValueCollection" (ad esempio, una mappa di stringa-string) a consentire al chiamante di passare argomenti aggiuntivi che il metodo può elaborare. È possibile utilizzare questo stesso approccio per l'oggetto return, una classe per contenere vari valori di ritorno. Questo approccio potrebbe anche isolare il chiamante da piccole modifiche agli argomenti/oggetti di ritorno.

+0

ottima idea! Lo userò adesso – chester89

2

Un'alternativa a "argomenti struct" approccio, che spesso funziona meglio in casi complicati, è quello di spostare la logica di un grande metodo per una classe separata:

InterestRateCalculator rateCalc = new InterestRateCalculator(); 
rateCalc.ClientType = ... 
rateCalc.TotalAmount = ... 
rateCalc.CalculateSomeComplicatedInterestRate(); 

Può essere combinato con una sorta di fabbrica:

InterestRateCalculator myCalc 
    = sharedCalc.MakeMeAnInterestRateCalculator(); 

E dal momento che lei ha citato funzione di sovraccarico per semplificare lista argomenti, a volte è possibile sostituire argomenti booleani con classe di eredità.

metodi
1

A volte è semplice spostare tali elenchi di parametri in una classe, se ha senso. Ad esempio, potresti avere InterestRateDefinition che potrebbe essere utilizzato in molti posti diversi.

Tuttavia, spesso promuove la creazione di classi che semplicemente esistono come elenco di parametri e la classe non ha senso. Ho fatto questo un po 'me stesso in passato e tendono a ingombrare il codice piuttosto che fornire chiarezza. In questi giorni, se la lista degli argomenti non si presta a una classe, allora trasformo la funzione stessa in una classe.

Quindi potresti avere una classe InterestRateCalculator, che accetta un numero di input o proprietà pubbliche.Questo risolve il problema e mantiene un senso di "scopo codice" più si può fornire valori di default e modi alternativi di chiamare la vostra routine attraverso la classe (ad es CalculateNextCoupon, CalculateAnnual ecc)