Classe con metodo singolo – approccio migliore?

Diciamo che ho una class che ha lo scopo di eseguire 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 funzioni di libreria semplici come Math.random (). Sto pensando più a classi che svolgono compiti specifici e complessi, ma richiedono solo un metodo (pubblico) per farlo.

    Mi piacevano le classi di utilità piene di metodi statici. Hanno fatto un grande consolidamento dei metodi di supporto che altrimenti si trovano in giro causando inferno di ridondanza e manutenzione. Sono molto facili da usare, nessuna istanziazione, nessuna eliminazione, solo fire’n’forget. Immagino che questo sia stato il mio primo tentativo inconsapevole di creare un’architettura orientata ai servizi – molti servizi stateless che hanno fatto il loro lavoro e nient’altro. Man mano che un sistema cresce, i draghi stanno arrivando.

    Polimorfismo
    Diciamo che abbiamo il metodo UtilityClass.SomeMethod che ronza allegramente. All’improvviso abbiamo bisogno di cambiare 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, potremmo creare una class derivata e modificare i contenuti del metodo secondo necessità. Poiché si tratta di un metodo statico, non possiamo. Certo, se abbiamo solo bisogno di aggiungere funzionalità prima o dopo il vecchio metodo, possiamo creare una nuova class e chiamare quella vecchia al suo interno – ma questo è solo grossolano.

    Problemi di interfaccia
    I metodi statici non possono essere definiti tramite interfacce per ragioni logiche. E dal momento che non possiamo sovrascrivere i metodi statici, le classi statiche sono inutili quando è necessario passarle attraverso la loro interfaccia. Questo ci rende incapaci di usare le classi statiche come parte di un modello di strategia. Potremmo correggere alcuni problemi passando delegati invece di interfacce .

    analisi
    Questo fondamentalmente va di pari passo con i problemi di interfaccia sopra menzionati. Poiché la nostra capacità di intercambiare le implementazioni è molto limitata, avremo anche difficoltà a sostituire il codice di produzione con il codice di prova. Ancora una volta, possiamo avvolgerli, ma ci richiederà di modificare ampie parti del nostro codice solo per poter accettare wrapper invece degli oggetti reali.

    Promuove i BLOB
    Poiché i metodi statici vengono solitamente utilizzati come metodi di utilità e i metodi di utilità di solito avranno scopi diversi, ci ritroveremo rapidamente con una grande class piena di funzionalità non coerenti – idealmente, ogni class dovrebbe avere un unico scopo all’interno del sistema. Preferisco avere cinque volte le classi finché i loro scopi sono ben definiti.

    Parametro creep
    Per cominciare, quel piccolo metodo statico carino e innocente potrebbe richiedere un singolo parametro. Con l’aumentare della funzionalità, vengono aggiunti un paio di nuovi parametri. A breve vengono aggiunti ulteriori parametri che sono opzionali, quindi creiamo sovraccarichi del metodo (o aggiungiamo solo valori predefiniti, nelle lingue che li supportano). In poco tempo, abbiamo un metodo che richiede 10 parametri. Solo i primi tre sono realmente necessari, i parametri 4-7 sono opzionali. Ma se viene specificato il parametro 6, è necessario compilare anche 7-9 … Se avessimo creato una class con il solo scopo di fare ciò che questo metodo statico ha fatto, potremmo risolvere questo problema prendendo i parametri richiesti nel costruttore, e consentendo all’utente di impostare valori opzionali tramite proprietà, o metodi per impostare più valori interdipendenti allo stesso tempo. Inoltre, se un metodo è cresciuto fino a questa complessità, è probabile che sia comunque nella sua class.

    Esigenti consumatori per creare un’istanza di classi senza motivo
    Uno degli argomenti più comuni è, perché chiedere ai consumatori della nostra class di creare un’istanza per richiamare questo singolo metodo, senza poi averne bisogno per l’istanza in seguito? Creare un’istanza di una class è un’operazione molto economica nella maggior parte delle lingue, quindi la velocità non è un problema. Aggiungere una riga di codice aggiuntiva al consumatore è un costo basso per gettare le basi di una soluzione molto più gestibile in futuro. Infine, se si desidera evitare di creare istanze, è sufficiente creare un wrapper singleton della class che consenta un facile riutilizzo, sebbene ciò comporti il ​​requisito che la class sia senza stato. Se non è stateless, puoi comunque creare metodi di wrapper statici che gestiscono tutto, pur continuando a offrire tutti i vantaggi a lungo termine. Infine, potresti anche creare una class che nasconde l’istanza come se fosse un singleton: MyWrapper.Instance è una proprietà che restituisce appena MyClass ();

    Solo un Sith si occupa di assoluti
    Certo, ci sono delle eccezioni alla mia antipatia per i metodi statici. Le vere classi di utilità che non presentano alcun rischio di gonfiarsi sono casi eccellenti per i metodi statici: System.Convert come esempio. Se il tuo progetto è una tantum senza requisiti per la manutenzione futura, l’architettura generale non è molto importante – statica o non statica, non importa – la velocità di sviluppo, comunque.

    Standard, standard, standard!
    L’utilizzo dei metodi di istanza non ti impedisce di utilizzare anche metodi statici e viceversa. Finché c’è un ragionamento dietro la differenziazione ed è standardizzato. Non c’è niente di peggio che guardare oltre un piano aziendale esteso su diversi metodi di implementazione.

    Preferisco il modo statico. Poiché la class non rappresenta un object, non ha senso crearne un’istanza.

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

    Se non c’è motivo di avere un’istanza della class creata per eseguire la funzione, utilizzare l’implementazione statica. Perché rendere gli utenti di questa class creare un’istanza quando non è necessaria.

    Se non è necessario salvare lo stato dell’object, non è necessario istanziarlo in primo luogo. Vorrei andare con il singolo metodo statico a cui si passano i parametri.

    Metterei anche in guardia contro una gigantesca class 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.

    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 si può semanticamente dire che una di quelle classi sarebbe proprietaria del metodo.

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

    Suggerirei che è difficile rispondere in base alle informazioni fornite.

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

    Certo, è difficile dire esattamente perché è necessario creare una singola class solo per questo metodo. È la tipica situazione della “class di utilità”, come la maggior parte sta assumendo? Oppure stai implementando una sorta di class di regole, di cui potrebbero esserci altre in futuro.

    Ad esempio, avere quella class essere plug-in. Quindi vorresti creare un’interfaccia per il tuo metodo, e poi vorresti avere tutti i parametri passati nell’interfaccia, piuttosto che nel costruttore, ma non vorrai che fosse statico.

    La tua class può essere statica?

    Se è così, allora lo renderei una class ‘Utilities’ in cui metterei tutte le mie classi monofunzionali.

    Se questo metodo è stateless e non è necessario passarlo, allora ha più senso definirlo come statico. Se hai bisogno di passare il metodo, potresti prendere in considerazione l’idea di utilizzare un delegato piuttosto che uno dei tuoi altri approcci proposti.

    Per le applicazioni semplici e internal helper internal , utilizzerei un metodo statico. Per le applicazioni con componenti, sto amando il Managed Extensibility Framework . Ecco un estratto da un documento che sto scrivendo per descrivere i modelli che troverai nelle mie API.

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

    Come un esempio forzato:

     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. così:

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

    o

     MyClass my_object(arg1, arg2, arg3); 

    Un altro aspetto importante da considerare è se il sistema debba essere eseguito in un ambiente multithread e se sia sicuro per i thread avere un metodo statico o variabili …

    Si dovrebbe 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 class di arg1, quindi decoralo:

     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 di elaborazione che i dati appartengono insieme. Inoltre ottieni tutti i vantaggi che Mark ha menzionato.

    Penso che se le proprietà della tua class o l’istanza della class non saranno usate nei costruttori o nei tuoi metodi, i metodi non sono suggeriti per essere progettati come pattern ‘statici’. il metodo statico dovrebbe essere sempre pensato in modo “aiuto”.