Domanda

Supponiamo che io abbia una classe che intende svolgere una singola funzione. Dopo aver eseguito la funzione, può essere distrutta. C'è qualche motivo per preferire uno di questi approcci?

// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();

// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);

// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);

Ero intenzionalmente vago sui dettagli, per cercare di ottenere linee guida per situazioni diverse. Ma non avevo in mente semplici funzioni di libreria come Math.random (). Sto pensando a più classi che svolgono compiti specifici e complessi, ma richiedono solo un metodo (pubblico) per farlo.

È stato utile?

Soluzione

Adoravo le classi di utilità riempite con metodi statici. Fecero un grande consolidamento dei metodi di aiuto che altrimenti sarebbero dovuti causare ridondanza e inferno di manutenzione. Sono molto facili da usare, nessuna istanza, nessuno smaltimento, solo il fuoco e lo scordo. Immagino che questo sia stato il mio primo inconsapevole tentativo di creare un'architettura orientata ai servizi - molti servizi senza stato che hanno fatto il loro lavoro e nient'altro. Man mano che cresce un sistema, tuttavia, arrivano i draghi.

Il polimorfismo
Supponiamo di avere il metodo UtilityClass.SomeMethod che ronza felicemente. All'improvviso dobbiamo modificare leggermente la funzionalità. La maggior parte delle funzionalità è la stessa, ma dobbiamo comunque cambiare un paio di parti. Se non fosse stato un metodo statico, avremmo potuto creare una classe derivata e modificare i contenuti del metodo secondo necessità. Essendo un metodo statico, non possiamo. Certo, se abbiamo solo bisogno di aggiungere funzionalità prima o dopo il vecchio metodo, possiamo creare una nuova classe e chiamare quella vecchia al suo interno - ma questo è solo grossolano.

Problemi di interfaccia
I metodi statici non possono essere definiti tramite interfacce per motivi logici. E poiché non possiamo ignorare i metodi statici, le classi statiche sono inutili quando dobbiamo passarle in giro dalla loro interfaccia. Questo ci rende incapaci di usare le classi statiche come parte di un modello di strategia. Potremmo correggere alcuni problemi con delegati di passaggio anziché interfacce .

Test
Questo in pratica va di pari passo con i problemi dell'interfaccia menzionati sopra. Poiché la nostra capacità di scambiare le implementazioni è molto limitata, avremo anche problemi a sostituire il codice di produzione con il codice di prova. Ancora una volta, possiamo racchiuderli ma ci richiederà di modificare grandi parti del nostro codice solo per poter accettare wrapper invece degli oggetti reali.

Promuove BLOB
Poiché i metodi statici vengono generalmente utilizzati come metodi di utilità e i metodi di utilità di solito hanno scopi diversi, finiremo rapidamente con una grande classe piena di funzionalità non coerente - idealmente, ogni classe dovrebbe avere un unico scopo all'interno del sistema. Preferirei avere un numero di lezioni cinque volte maggiore se i loro scopi sono ben definiti.

Strisciamento dei parametri
Per cominciare, quel piccolo metodo statico carino e innocente potrebbe prendere un singolo parametro. Man mano che la funzionalità aumenta, vengono aggiunti un paio di nuovi parametri. Presto verranno aggiunti ulteriori parametri che sono facoltativi, quindi creiamo sovraccarichi del metodo (o semplicemente aggiungiamo valori predefiniti, nelle lingue che li supportano). In poco tempo, abbiamo un metodo che accetta 10 parametri. Sono richiesti solo i primi tre, i parametri 4-7 sono opzionali. Ma se viene specificato il parametro 6, è necessario compilare anche 7-9 ... Se avessimo creato una classe con il solo scopo di fare ciò che ha fatto questo metodo statico, potremmo risolverlo prendendo i parametri richiesti nel costruttore e consentire all'utente di impostare valori opzionali tramite proprietà o metodi per impostare più valori interdipendenti contemporaneamente. Inoltre, se un metodo è cresciuto fino a raggiungere questo livello di complessità, molto probabilmente deve essere comunque nella sua classe.

Consumatori esigenti di creare un'istanza di classi senza motivo
Uno degli argomenti più comuni è, perché chiedere ai consumatori della nostra classe di creare un'istanza per invocare questo singolo metodo, senza averne successivamente bisogno? La creazione di un'istanza di una classe è un'operazione molto economica nella maggior parte delle lingue, quindi la velocità non è un problema. L'aggiunta di una riga aggiuntiva di codice al consumatore è un costo basso per porre le basi di una soluzione molto più sostenibile in futuro. Infine, se si desidera evitare la creazione di istanze, è sufficiente creare un wrapper singleton della propria classe che consenta un facile riutilizzo, sebbene ciò richieda che la classe sia apolide. Se non è stateless, è comunque possibile creare metodi wrapper statici che gestiscono tutto, offrendo comunque tutti i vantaggi a lungo termine. Infine, puoi anche creare una classe che nasconda l'istanza come se fosse un singleton: MyWrapper.Instance è una proprietà che restituisce semplicemente il nuovo MyClass ();

Solo un Sith si occupa di assoluti
Certo, ci sono eccezioni alla mia antipatia per i metodi statici. Le vere classi di utilità che non comportano alcun rischio di gonfiamento sono ottimi casi per metodi statici, come System.Convert. Se il tuo progetto è unico e non richiede requisiti per la manutenzione futura, l'architettura complessiva non è davvero molto importante - statica o non statica, non importa - la velocità di sviluppo, comunque.

Standard, standard, standard!
L'uso dei metodi di istanza non impedisce di utilizzare anche metodi statici e viceversa. Finché c'è un ragionamento dietro la differenziazione ed è standardizzata. Non c'è niente di peggio che guardare un livello aziendale tentacolare con diversi metodi di implementazione.

Altri suggerimenti

Preferisco il modo statico. Poiché la classe non rappresenta un oggetto, non ha senso crearne un'istanza.

Le classi che esistono solo per i loro metodi devono essere lasciate statiche.

Se non vi è alcun motivo per creare un'istanza della classe per eseguire la funzione, utilizzare l'implementazione statica. Perché i consumatori di questa classe creano un'istanza quando non è necessaria.

Se non è necessario salvare lo stato dell'oggetto, non è necessario istanziarlo in primo luogo. Andrei con il singolo metodo statico a cui passi i parametri.

Avrei anche messo in guardia contro una gigantesca classe Utils che ha dozzine di metodi statici non correlati. Questo può diventare disorganizzato e ingombrante in fretta. È meglio avere molte classi, ognuna con pochi metodi correlati.

Direi che il formato del metodo statico sarebbe l'opzione migliore. E renderei anche la classe statica, in questo modo non dovresti preoccuparti di creare accidentalmente un'istanza della classe.

Non so davvero quale sia la situazione qui, ma guarderei a metterla come metodo in una delle classi a cui appartengono arg1, arg2 o arg3 - Se puoi dire semanticamente che una di quelle classi possederebbe il metodo.

Suggerirei che è difficile rispondere in base alle informazioni fornite.

Il mio istinto è che se hai solo un metodo e stai per buttare via immediatamente la classe, allora rendila una classe statica che accetta tutti i parametri.

Naturalmente, è difficile dire esattamente perché è necessario creare una singola classe solo per questo metodo. È la tipica "classe di utilità"? situazione come la maggior parte stanno assumendo? Oppure stai implementando una sorta di classe di regole, di cui potrebbero essercene altre in futuro.

Ad esempio, questa classe può essere plug-in. Quindi vorresti creare un'interfaccia per il tuo unico metodo, e poi vorresti avere tutti i parametri passati nell'interfaccia, piuttosto che nel costruttore, ma non vorrai che fosse statico.

La tua classe può essere resa statica?

In tal caso, la trasformerei in una classe "Utilità" in cui inserirò tutte le mie classi con una funzione.

Se questo metodo è senza stato e non è necessario passarlo in giro, ha più senso definirlo statico. Se devi passare il metodo, potresti considerare di utilizzare un delegato piuttosto che uno dei tuoi altri approcci proposti.

Per applicazioni semplici e aiutanti interni , utilizzerei un metodo statico. Per le applicazioni con componenti, adoro il Managed Extensibility Framework . Ecco un estratto da un documento che sto scrivendo per descrivere gli schemi che troverai nelle mie API.

  • Servizi
    • Definito da un'interfaccia I [ServiceName] Service .
    • Esportato e importato dal tipo di interfaccia.
    • La singola implementazione è fornita dall'applicazione host e consumata internamente e / o dalle estensioni.
    • I metodi sull'interfaccia del servizio sono thread-safe.

Come esempio inventato:

public interface ISettingsService
{
    string ReadSetting(string name);

    void WriteSetting(string name, string value);
}

[Export]
public class ObjectRequiringSettings
{
    [Import]
    private ISettingsService SettingsService
    {
        get;
        set;
    }

    private void Foo()
    {
        if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
        {
            // whatever
        }
    }
}

Vorrei solo fare tutto nel costruttore. in questo modo:

new MyClass(arg1, arg2, arg3);// the constructor does everything.

o

MyClass my_object(arg1, arg2, arg3);

Un'altra questione importante da considerare è se il sistema sarebbe in esecuzione in un ambiente multithread e se sarebbe sicuro per i thread avere un metodo statico o variabili ...

Dovresti prestare attenzione allo stato del sistema.

Potresti essere in grado di evitare la situazione tutti insieme. Prova a refactoring in modo da ottenere arg1.myMethod1 (arg2, arg3) . Scambia arg1 con arg2 o arg3 se ha più senso.

Se non hai il controllo sulla classe di arg1, decorala:

class Arg1Decorator
    private final T1 arg1;
    public Arg1Decorator(T1 arg1) {
        this.arg1 = arg1;
    }
    public T myMethod(T2 arg2, T3 arg3) {
        ...
    }
 }

 arg1d = new Arg1Decorator(arg1)
 arg1d.myMethod(arg2, arg3)

Il ragionamento è che, in OOP, i dati e i metodi che elaborano tali dati appartengono insieme. Inoltre ottieni tutti i vantaggi menzionati da Mark.

Penso che se le proprietà della tua classe o l'istanza della classe non saranno usate nei costruttori o nei tuoi metodi, i metodi non dovrebbero essere progettati come pattern 'statici'. il metodo statico dovrebbe essere sempre pensato in modo "aiuto".

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top