"Statico" come indizio semantico sull'apolidia?


11

Di recente ho intrapreso un refactoring di un progetto di medie dimensioni in Java per tornare indietro e aggiungere test unitari. Quando mi sono reso conto che dolore è stato deridere i singoli e la statica, ho finalmente "capito" quello che ho letto su di loro tutto questo tempo. (Sono una di quelle persone che hanno bisogno di imparare dall'esperienza. Vabbè.)

Quindi, ora che sto usando Spring per creare gli oggetti e collegarli, mi sto sbarazzando delle staticparole chiave a destra e sinistra. (Se potessi potenzialmente volerlo deridere, non è proprio statico nello stesso senso in cui Math.abs () è, giusto?) Il fatto è che mi ero abituato statica usare per indicare che un metodo non faceva affidamento su qualsiasi stato dell'oggetto. Per esempio:

//Before
import com.thirdparty.ThirdPartyLibrary.Thingy;
public class ThirdPartyLibraryWrapper {
    public static Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
ThirdPartyLibraryWrapper.newThingy(input);
//After
public class ThirdPartyFactory {
    public Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
thirdPartyFactoryInstance.newThingy(input);

Quindi, ecco dove diventa permaloso. Mi piaceva alla vecchia maniera perché la lettera maiuscola mi diceva che, proprio come Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) faceva la stessa cosa ogni volta allo stesso modo. Non esiste uno stato oggetto per cambiare il modo in cui l'oggetto fa quello che gli sto chiedendo di fare. Ecco alcune possibili risposte che sto prendendo in considerazione.

  • Nessun altro si sente così, quindi c'è qualcosa che non va in me. Forse non ho proprio interiorizzato il modo OO di fare le cose! Forse sto scrivendo in Java ma sto pensando a FORTRAN o qualcosa del genere. (Il che sarebbe impressionante dato che non ho mai scritto FORTRAN.)
  • Forse sto usando la staticità come una sorta di proxy per l'immutabilità ai fini del ragionamento sul codice. Detto questo, quali indizi dovrei avere nel mio codice per qualcuno che viene per mantenerlo per sapere cosa è stato e cosa no?
  • Forse questo dovrebbe venire gratis se scelgo metafore di oggetti buoni? ad esempio thingyWrapper, non suona come se avesse uno stato indipendente dall'involucro Thingyche può essere esso stesso mutabile. Allo stesso modo, thingyFactorysembra che dovrebbe essere immutabile, ma potrebbe avere diverse strategie che sono state scelte al momento della creazione.

Risposte:


12

Mi piaceva alla vecchia maniera perché la lettera maiuscola mi diceva che, proprio come Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) faceva la stessa cosa ogni volta allo stesso modo. Non esiste uno stato oggetto per cambiare il modo in cui l'oggetto fa quello che gli sto chiedendo di fare.

Questo è il modo giusto per farlo, sì. Ciò detto, staticnon offre alcuna garanzia di immutabilità o persistenza statale. Il fatto che tu lo usi come suggerimento per tali garanzie ha senso, ma non è affatto garantito.

Considera quanto segue:

public static class Logger
{
    public static int LogLevel { get; set; }

    public static void Log(string message, int logLevel)
    {
        if (logLevel >= LogLevel)
        {
            // logs the message, but only if it is important enough.
        }
    }
}

Questa classe non solo mantiene lo stato, ma il comportamento del Logger.Log()metodo cambia quando lo stato viene modificato. È un esercizio perfettamente legittimo di un staticmodello di classe, ma non ha nessuna delle garanzie semantiche che stai suggerendo.


Ti ho dato il segno di spunta perché presenti un utile contrappunto alla base della mia intuizione, ma mi piacerebbe comunque avere le tue opinioni su come dovrei rappresentare quel contratto / aspettativa sulla mancanza di stato e la mancanza di effetti collaterali tramite la codifica convegni.
Leoger,

1
Normalmente tali aspettative sono incluse nella documentazione per il metodo; lo stesso vale per la sicurezza del thread.
Robert Harvey,

5

Secondo me quello che stai dicendo - tutte le funzioni statiche per essere nullipotent - è un buon modo OO di scrivere codice nella maggior parte dei casi, ma non credo che quando guardi una funzione statica dovresti presumere automaticamente che non abbia effetti collaterali . La situazione potrebbe essere più complessa; prendiamo ad esempio la funzione Class.forName(String)che sembra essere stateless ma in realtà carica una classe nella memoria e alla fine crea un'istanza di campi statici / esegue l'inizializzatore statico; questo è un esempio di funzione idempotente (eventuali chiamate dopo la prima non fanno alcuna differenza) ma non è una funzione pura (non si può dire che non abbia effetti collaterali). Anche questa è una buona scelta, ma potrebbero esserci casi in cui tre chiamate distinte alla stessa funzione producono tre risultati diversi; per esempio se chiamiThread.currentThread() in un'applicazione multi-thread non c'è garanzia che riceverai sempre lo stesso thread.

Detto questo, quali indizi dovrei avere nel mio codice per qualcuno che viene per mantenerlo per sapere cosa è stato e cosa no?

Una soluzione adeguata è documentare (Javadoc per esempio); inoltre, dal nome della funzione e da ciò che fa a volte si può dedurre che la funzione statica è una funzione pura. Ad esempio, non c'è motivo per ritenere che qualcuno Assert.assertTrue(boolean)da JUnit cambierebbe qualche stato da qualche parte. D'altra parte, quando System.clearProperty(String)viene chiamata una funzione come , è abbastanza ovvio che ci saranno effetti collaterali.


Qual è la differenza tra "nullipotent" e "stateless"?
Pacerier

@Pacerier Non ci dovrebbero essere differenze.
m3th0dman,

4

Detto questo, quali indizi dovrei avere nel mio codice per qualcuno che viene per mantenerlo per sapere cosa è stato e cosa no?

Puoi renderli statici. FxCop nel mondo C # ti spingerà a rendere statiche cose che non fanno riferimento a variabili membro. Questa è la cosa corretta da fare (di solito).

Quando mi sono reso conto che dolore è stato deridere i singoli e la statica, ho finalmente "capito" quello che ho letto su di loro tutto questo tempo.

Spostarli tutti in istanze non è forse l'approccio giusto. Disaccoppiare invece le dipendenze dai metodi statici dalle classi che le utilizzano. È possibile testare i metodi statici isolatamente e quindi sostituire la dipendenza nei consumatori quando devono essere testati.


Puoi farmi un esempio di "disaccoppiamento delle dipendenze dai metodi statici"? Non mi è chiaro cosa stai attivando. Ho già nascosto i dettagli di implementazione in un metodo statico. Alla fine della giornata, la classe che la utilizza deve raggiungere quella funzionalità. Stai dicendo che dovrei creare un oggetto di istanza con wrapper sottili per tutti i metodi statici?
Leoger,

1
@leoger - la classe che utilizza il metodo statico non ha bisogno di raggiungere la funzionalità se stai testando quella classe, altrimenti non deriderei il metodo statico.
Telastyn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.