2010-03-18 10 views
45

Sto eseguendo la revisione del codice e ho trovato una classe che utilizza tutti i metodi statici. Il metodo di ingresso accetta diversi argomenti e quindi inizia a chiamare gli altri metodi statici che passano lungo tutti o alcuni degli argomenti ricevuti dal metodo di ingresso.C'è qualcosa che non va in una classe con tutti i metodi statici?

Non è come una classe di matematica con funzioni di utilità in gran parte non correlate. Nella mia normale programmazione, raramente scrivo metodi in cui Resharper si apre e dice "questo potrebbe essere un metodo statico", quando lo faccio, tendono a essere metodi di utilità insensati.

C'è qualcosa di sbagliato in questo modello? È solo una questione di scelta personale se lo stato di una classe viene tenuto in campi e proprietà o passato tra i metodi statici che usano gli argomenti?

UPDATE: lo stato particolare che viene trasmesso è il set di risultati del database. La responsabilità della classe è di compilare un modello di foglio di calcolo Excel da un set di risultati dal DB. Non so se questo faccia differenza.

+1

correlati http://stackoverflow.com/questions/790281/ –

+1

Anche http://stackoverflow.com/questions/2410584/is-it-ok-to-wind-up-using-mostly-static-classes/ 2410849 # 2410849 – ewernli

+1

Dopo la modifica, direi di sì, sicuramente dovrebbe essere una classe istanziata perché stai mantenendo lo stato (il foglio di calcolo di Excel), quindi avrei una classe che prende il set di risultati nel costruttore e un'altra metodo come "Salva (stringa nome file)" o "Salva (foglio di lavoro Excel)" per creare il foglio di calcolo Excel. Tutti gli altri metodi che indovinerei sono metodi di supporto e dovrebbero essere privati. –

risposta

20

C'è qualcosa di sbagliato in questo schema ? È solo questione di scelta personale se lo stato di una classe viene tenuto nei campi e nelle proprietà o passato tra i metodi statici utilizzando gli argomenti?

Parlando della mia esperienza personale, ho lavorato su 100 applicazioni Kloc che hanno hiearchies oggetto molto molto profonde, tutto eredita e ignora tutto il resto, tutto quello che implementa una mezza dozzina di interfacce, anche le interfacce ereditano una mezza dozzina interfacce, il sistema implementa ogni modello di progettazione nel libro, ecc.

Risultato finale: un'architettura veramente OOP-tastica con così tanti livelli di riferimento indiretto che occorrono ore per eseguire il debug di tutto. Recentemente ho iniziato un lavoro con un sistema come questo, dove la curva di apprendimento mi è stata descritta come "un muro di mattoni, seguito da una montagna".

A volte l'OOP troppo zelante risulta in classi così granulari da essere in realtà un danno netto.

Al contrario, molti linguaggi di programmazione funzionale, anche quelli OO come F # e OCaml (e C#!), Incoraggiano l'hiearching piatto e superficiale. Biblioteche in queste lingue tendono ad avere le seguenti proprietà:

  • maggior parte degli oggetti sono pocos, o hanno al massimo uno o due livelli di ereditarietà, dove gli oggetti non sono molto di più di contenitori per i dati logicamente correlati.
  • Invece di classi che chiamano l'una nell'altra, si hanno moduli (equivalenti a classi statiche) che controllano le interazioni tra gli oggetti.
  • I moduli tendono ad agire su un numero molto limitato di tipi di dati, e quindi hanno un ambito ristretto. Ad esempio, il modulo Lista OCaml rappresenta operazioni su elenchi, un modulo Cliente facilita le operazioni sui clienti. Sebbene i moduli abbiano più o meno le stesse funzionalità dei metodi di istanza di una classe, la differenza fondamentale con le librerie basate su moduli è che i moduli sono molto più indipendenti, molto meno granulari e tendono ad avere poche o eventuali dipendenze da altri moduli.
  • Di solito non è necessario creare una sottoclasse di metodi di override degli oggetti poiché è possibile passare le funzioni come oggetti di prima classe per la specializzazione.
  • Sebbene C# non supporti questa funzionalità, functors fornisce un mezzo per creare una sottoclasse di moduli specializzati.

maggior parte delle grandi librerie tendono ad essere più largo che profondo, ad esempio l'API Win32, librerie PHP, BIFs Erlang, librerie OCaml e Haskell, stored procedure in un database, ecc Quindi questo stile di programmazione è il test di battaglia e sembra funzionare bene nel mondo reale.

A mio parere, le API basate su modulo più progettate tendono a essere più facili da utilizzare rispetto alle API OOP meglio progettate. Tuttavia, lo stile di codifica è altrettanto importante nella progettazione dell'API, quindi se tutti gli altri membri del team utilizzano OOP e qualcuno si disconnette e implementa qualcosa in uno stile completamente diverso, probabilmente dovresti chiedere una riscrittura più simile alla codifica dei team standard.

+0

Il tuo ultimo punto mi parla. Suppongo che si possa scrivere OOP in linguaggi procedurali e procedurali nei linguaggi OOP e magari anche tentare di scrivere uno stile funzionale o dinamico in un linguaggio OOP o procedurale. In tutti i casi scriverebbe un codice che sta combattendo la struttura in cui si trova e sarà più difficile da leggere per un programmatore che si aspetta un OOP in un programma Java o C#. – MatthewMartin

+9

Le grandi applicazioni che descrivi probabilmente non soffrono di OOP eccessivamente zelanti, ma di cattiva progettazione. Il buon design OOP NON sta sottoclasse tutto, sebbene la maggior parte dei corsi OOP tendano a generare terribili esempi di Animal-Mammal-Cow che ti portano a credere il contrario. Buon design L'IMO si occupa della suddivisione della complessità in un modo utile, che consente cambiamenti previsti nel futuro senza interruzioni. –

+2

+1 "un muro di mattoni, seguito da una montagna" –

5

Aiuta a riformulare la domanda:

Può descrivere i dati che i metodi statici opera come un'entità dotata:

  • un significato chiaro
  • responsabilità di mantenere è stato interno coerente.

In tal caso, dovrebbe essere un oggetto istanziato, altrimenti potrebbe essere solo un gruppo di funzioni correlate, proprio come una libreria matematica.

3

ritengo che se la classe è necessario mantenere una certa forma di stato (ad esempio, proprietà) allora dovrebbe essere istanziata (ovvero di una classe "normale".)

Se ci dovrebbe essere solo un'istanza di questa classe (quindi tutti i metodi statici) quindi dovrebbe esserci una proprietà/metodo singleton o un metodo factory che crea un'istanza della classe la prima volta che viene chiamata, e quindi fornisce quell'istanza quando qualcun altro lo richiede.

Detto questo, questa è solo la mia opinione personale e il modo in cui l'avrei implementata. Sono sicuro che gli altri non sarebbero d'accordo con me. Senza sapere niente di più è difficile dare ragioni per/contro ogni metodo, per essere onesti.

+2

Singleton non è praticamente un modello obsoleto da IoC? –

+0

IoC? Non vedo come possa essere obsoleto - ci sono molti usi in cui hai sempre bisogno di una sola istanza di una classe, e IMO il pattern singleton è il modo migliore per assicurarti questo. –

1

Dipende se gli argomenti passati possono realmente essere classificati come stato.

Avere metodi statici che si chiamano l'un l'altro è OK nel caso in cui tutte le funzionalità di utilità si suddividano in più metodi per evitare la duplicazione. Ad esempio:

public static File loadConfiguration(String name, Enum type) { 
    String fileName = (form file name based on name and type); 
    return loadFile(fileName); // static method in the same class 
} 
2

Non c'è niente di sbagliato in questo modello. C# ha infatti un costrutto chiamato classi statiche che viene usato per supportare questa nozione imponendo il requisito che tutti i metodi siano statici. Inoltre ci sono molte classi nel framework che hanno questa caratteristica: Enumerable, Math, ecc ...

1

Bene, personalmente, tendo a pensare che un metodo che modifica lo stato di un oggetto dovrebbe essere un metodo di istanza di quell'oggetto classe. In effetti, lo considero una regola un pollice: un metodo che modifica un oggetto è un metodo di istanza della classe di quell'oggetto.

Ci sono però alcune eccezioni:

  • metodi che elaborano le stringhe (come maiuscolo le prime lettere, o quel tipo di funzione)
  • metodo che sono apolidi e semplicemente assemblare alcune cose per la produzione di una nuova uno, senza uno stato interno. Ovviamente sono rari, ma è generalmente utile renderli statici.

In effetti, considero la parola chiave statica come ciò che è: un'opzione che dovrebbe essere utilizzata con attenzione poiché rompe alcuni dei principi OOP.

2

Niente è sbagliato. È un modo più "funzionale" di codificare. Può essere più semplice testare (perché non esiste uno stato interno) e prestazioni migliori in fase di esecuzione (poiché non è necessario un sovraccarico per istanziare un oggetto altrimenti inutile).

Ma si perdono immediatamente alcune funzionalità OO I metodi statici non rispondono correttamente (per nulla) all'ereditarietà. Una classe statica non può partecipare a molti schemi di progettazione come il localizzatore di fabbrica/servizio.

21

Quello che descrivi è semplicemente una programmazione strutturata, come si potrebbe fare in C, Pascal o Algol. Non c'è nulla di intrinsecamente sbagliato in questo. Lì sono le situazioni erano OOP più appropriate, ma OOP non è la risposta definitiva e se il problema in questione è meglio servito dalla programmazione strutturata, allora una classe piena di metodi statici è la strada da percorrere.

+0

Esattamente, non c'è nulla di intrinsecamente negativo con la programmazione strutturata in un linguaggio OO. Ma uno sta essenzialmente sprecando un sacco di potenziale come l'uso della classe e l'implementazione come casi base per un comportamento più specializzato usando l'ereditarietà, ecc. –

+1

+1, solo perché hai una motosega non significa che ogni singolo problema sia risolto al meglio con una motosega. A volte hai davvero bisogno di un martello. A volte è solo più facile mantenerlo semplice. – wasatz

+0

+1 per aver affermato che la domanda è semplicemente una programmazione strutturata. Plumbing tra Excel e a, fammi indovinare, DB relazionale? Quello non è OO. Finché non hai bisogno di OO e non chiami OO, non c'è problema. – SyntaxT3rr0r

0

"lo stato di una classe è ... passato tra i metodi statici che usano gli argomenti?" Così funziona la programmazione procedurale.

Una classe con tutti i metodi statici e nessuna variabile di istanza (ad eccezione delle costanti finali statiche) è normalmente una classe di utilità, ad esempio Math. Non c'è niente di sbagliato nel fare una classe di unilence, (non in sé stessa) BTW: Se si crea una classe di utilità, si potrebbe impedire che la classe venga utilizzata per crittare un oggetto. in Java lo si fare definendo esplicitamente il costruttore, ma rendendo privato il costruttore. Mentre come ho detto non c'è niente di sbagliato nella creazione di una classe di utilità, Se la maggior parte del lavoro viene eseguita da una classe di utiulity (che esc. Non è una classe nel senso comune) è più di una raccolta di funzioni) quindi questo è il segno prob che il problema non è stato risolto usando il paradim orientato agli oggetti. questo può o potrebbe non essere una buona cosa

Il metodo di ingresso accetta diversi argomenti e quindi inizia a chiamare gli altri metodi statici che passano lungo tutti o alcuni degli argomenti il ​​metodo di ingresso ricevuto. dal suono di questo, l'intera classe è solo un metodo efficace (questo sarebbe sicuramente il caso in cui tutti gli altri metodi statici sono privati ​​(e sono solo funzioni di supporto) e non ci sono variabili di istanza (costanti di bar)) Questa potrebbe essere e OK, È esc. programmazione strutturata/procedurale, piuttosto ordinata averli (la funzione e il suo aiuto) tutti raggruppati in una classe. (in C devi solo metterli tutti in un file e dichiarare l'helper statico (il significato non può essere accesso da questo file))

2

No, molte persone tendono a creare classi completamente statiche per le funzioni di utilità che desiderano raggruppare in uno spazio dei nomi correlato. Ci sono molti validi motivi per avere classi completamente statiche.

Una cosa da considerare in C# è che molte classi precedentemente scritte completamente statiche ora possono essere considerate come classi di estensione .net che sono anche nel loro cuore classi statiche. Molte estensioni Linq sono basate su questo.

Un esempio:

namespace Utils { 
    public static class IntUtils  { 
      public static bool IsLessThanZero(this int source) 
      { 
       return (source < 0); 
      } 
    } 
} 

Che poi ti permette di fare semplicemente il seguente:

var intTest = 0; 
var blNegative = intTest.IsLessThanZero(); 
+0

Pedantic note: dovrebbe essere "return source <0" come intVal è il tipo? (E le parentesi non sono strettamente necessarie sebbene utili per la leggibilità.) –

+0

oops, ho digitato troppo velocemente :-), modificato di conseguenza. –

0

se non c'è bisogno di creare un oggetto di una classe, allora non c'è alcun problema nella creazione di tutto metodo come statico di quella classe, ma voglio sapere cosa stai facendo con una classe fullof di metodi statici.

0

io non sono abbastanza sicuro che cosa si intende con il metodo di ingresso, ma se si sta parlando di qualcosa di simile a questo:

MyMethod myMethod = new MyMethod(); 
myMethod.doSomething(1); 

public class MyMethod { 
     public String doSomething(int a) { 
      String p1 = MyMethod.functionA(a); 
      String p2 = MyMethod.functionB(p1); 
      return p1 + P2; 
     } 
     public static String functionA(...) {...} 
     public static String functionB(...) {...} 
} 

Questo non è consigliabile.

Penso che utilizzare tutti i metodi statici/singleton sia un buon modo per codificare la logica aziendale quando non è necessario mantenere nulla nella classe. Io tendo ad usarlo su singleton ma questa è semplicemente una preferenza.

MyClass.myStaticMethod(....); 

in contrapposizione a:

MyClass.getInstance().mySingletonMethod(...); 

Tutti i metodi statici/single tendono ad usare meno memoria come bene, ma a seconda di quanti utenti avete non si può nemmeno accorgersene.

3

Il problema più grande IMO è che se si desidera unità di classi di test che chiamano la classe che si menziona, non c'è modo di sostituire tale dipendenza. Quindi sei costretto a testare sia la classe client, sia la classe chiamata staticamente in una volta.

Se stiamo parlando di una classe con metodi di utilità come Math.floor() questo non è davvero un problema. Ma se la classe è una vera dipendenza, per esempio un oggetto di accesso ai dati, allora lega tutti i suoi clienti alla sua implementazione.

EDIT: Non sono d'accordo con la gente che dice che c'è "niente di sbagliato" con questo tipo di "programmazione strutturata". Direi che una classe come questa è almeno un odore di codice quando viene rilevata all'interno di un normale progetto Java e probabilmente indica l'incomprensione del design orientato agli oggetti da parte del creatore.

+1

"probabilmente indica l'incomprensione del design orientato agli oggetti" ... o, semplicemente, non credono che tutto il codice debba essere OO. –

+2

Tradizionalmente si passavano tutte le dipendenze necessarie in una classe statica, quindi i metodi potrebbero avere più parametri, ma è possibile testare facilmente tutte le dipendenze. Una buona programmazione basata sui moduli scoraggia l'accoppiamento stretto dei moduli, quindi di solito non si hanno moduli che chiamano altri moduli - ma nel caso lo si fare, si riscriverà 'static void A (someState) {B.HandleState (someState)}' as' static void A (somestate, handleState) {handleState (someState)} ', quindi può sostituire la funzione di gestore con qualsiasi implementazione desiderata, compresa una chiamata al modulo di handler originale o una simulazione. – Juliet

+0

@Adriaan Koster: +1 ... L'unità che prova quella classe o che ha bisogno di una simulazione di quella "astrazione" si dimostrerebbe davvero difficile. – SyntaxT3rr0r

4

Ecco un flusso di lavoro di refactoring che incontro spesso con metodi statici. Potrebbe dare qualche idea del tuo problema.

Inizierò con una classe che ha un incapsulamento ragionevolmente buono. Quando comincio ad aggiungere funzionalità, mi imbatto in una funzionalità che non ha realmente bisogno di accedere ai campi privati ​​della mia classe, ma sembra contenere funzionalità correlate. Dopo che questo accade alcune volte (a volte solo una volta) comincio a vedere i contorni di una nuova classe nei metodi statici che ho implementato e in che modo questa nuova classe si relaziona con la vecchia classe in cui inizialmente ho implementato i metodi statici.

Il vantaggio che vedo di trasformare questi metodi statici in una o più classi è, quando lo si fa, spesso diventa più facile capire e gestire il software.

2

Uno degli svantaggi dell'utilizzo di una classe statica è che i suoi client non possono sostituirlo con una doppia di prova per essere sottoposti a test dell'unità.

Allo stesso modo, è più difficile testare un'unità di una classe statica perché i suoi collaboratori non possono essere sostituiti da duplicati di test (in realtà, ciò accade con tutte le classi che non sono iniezioni di dipendenza).

1

Passare tutti gli stati come parametri del metodo può essere un modello di progettazione utile. Assicura che non ci sia uno stato mutabile condiviso, e quindi la classe è intrinsecamente sicura per i thread. I servizi vengono comunemente implementati utilizzando questo modello.

Tuttavia, passare tutto lo stato tramite i parametri del metodo non significa che i metodi devono essere statici: è comunque possibile utilizzare lo stesso modello con metodi non statici. I vantaggi di rendere statici i metodi è che il codice chiamante può semplicemente usare la classe facendo riferimento per nome. Non c'è bisogno di iniezione, ricerca o qualsiasi altro intermediario. Lo svantaggio è la manutenibilità: i metodi statici non sono di tipo dinamico e non possono essere facilmente suddivisi in sottoclassi o rifatti a un'interfaccia. Raccomando di usare metodi statici quando c'è intrinsecamente solo una possibile implementazione della classe e quando c'è una forte ragione per non usare metodi non statici.

Problemi correlati