Java - È una cattiva idea avere classi completamente statiche?


16

Sto lavorando a un progetto solista più ampio e in questo momento, e ho diverse classi in cui non vedo alcun motivo per creare un'istanza di.

La mia classe di dadi in questo momento, ad esempio, memorizza staticamente tutti i suoi dati e anche tutti i suoi metodi sono statici. Non ho bisogno di inizializzarlo perché quando voglio tirare i dadi e ottenere un nuovo valore, lo uso semplicemente Dice.roll().

Ho diverse classi simili che hanno solo una funzione principale come questa e sto per iniziare a lavorare su una sorta di classe "controller" che sarà responsabile di tutti gli eventi (come quando un giocatore si muove e quale turno corrente lo è) e ho scoperto che avrei potuto seguire la stessa idea per questa lezione. Non ho mai intenzione di creare più oggetti per queste classi specifiche, quindi sarebbe una cattiva idea renderli completamente statici?

Mi chiedevo se questo è considerato "cattiva pratica" quando si tratta di Java. Da quello che ho visto, la comunità sembra essere un po 'divisa su questo argomento? Ad ogni modo, mi piacerebbe qualche discussione su questo e anche i collegamenti alle risorse sarebbero fantastici!


1
Se il tuo programma è completamente procedurale. Perché hai scelto Java?
Laiv

12
Prova a scrivere unit test per queste classi e scoprirai perché alla gente non piacciono i metodi statici che accedono allo stato statico.
Joeri Sebrechts,

@Laiv Sono ancora un po 'nuovo nella programmazione, circa un anno di C ++, seguirò una lezione Java questo semestre e inizierò ad apprezzare Java molto di più, specialmente le librerie grafiche.
HexTeke,


5
Per quanto riguarda le classi statiche. Se i metodi statici sono puri (non conservare alcuno stato, hanno argomenti di input e un tipo restituito). Non c'è nulla di cui preoccuparsi
Laiv,

Risposte:


20

Non c'è niente di sbagliato nelle classi statiche che sono veramente statiche . Vale a dire, non esiste uno stato interno di cui parlare che provocherebbe la modifica dell'output dei metodi.

Se Dice.roll()sta semplicemente restituendo un nuovo numero casuale da 1 a 6, non sta cambiando stato. Certo, potresti condividere Randomun'istanza, ma non riterrei che un cambio di stato come per definizione, l'output sarà sempre a posto, casuale. È anche thread-safe quindi non ci sono problemi qui.

Vedrai spesso "Helper" finali o altre classi di utilità che hanno un costruttore privato e membri statici. Il costruttore privato non contiene alcuna logica e serve solo a impedire a qualcuno di creare un'istanza della classe. Il modificatore finale porta solo a casa questa idea che questa non è una classe da cui vorresti mai derivare. È semplicemente una classe di utilità. Se fatto correttamente, non dovrebbero esserci singoli membri della classe o altri membri che non siano essi stessi statici e finali.

Fintanto che segui queste linee guida e non crei singoli, non c'è assolutamente nulla di sbagliato in questo. Lei menziona una classe di controller, e questo richiederà quasi certamente cambiamenti di stato, quindi sconsiglio di usare solo metodi statici. Puoi fare molto affidamento su una classe di utilità statica, ma non puoi renderla una classe di utilità statica.


Cosa viene considerato un cambio di stato per una classe? Bene, lasciamo escludere numeri casuali per un secondo, poiché non sono deterministici per definizione e quindi il valore restituito cambia spesso.

Una funzione pura è deterministica, vale a dire, per un dato input, otterrai uno e esattamente un output. Volete che i metodi statici siano pure funzioni. In Java ci sono modi di modificare il comportamento dei metodi statici per mantenere lo stato, ma non sono quasi mai buone idee. Quando dichiarate un metodo come statico , il programmatore tipico assumerà subito che è una funzione pura. Deviare dal comportamento previsto è il modo in cui si tende a creare bug nel programma, in generale e dovrebbe essere evitato.

Un singleton è una classe che contiene metodi statici all'opposto della "funzione pura" come puoi essere. Un singolo membro privato statico viene mantenuto internamente alla classe utilizzata per garantire che esista esattamente un'istanza. Questa non è la migliore pratica e può metterti nei guai in seguito per una serie di motivi. Per sapere di cosa stiamo parlando, ecco un semplice esempio di singleton:

// DON'T DO THIS!
class Singleton {
  private String name; 
  private static Singleton instance = null;

  private Singleton(String name) {
    this.name = name;
  }

  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton("George");
    }
    return instance;
  }

  public getName() {
    return name;
  }
}

assert Singleton.getInstance().getName() == "George"

7
Se voglio provare cosa succede quando viene lanciato un doppio sei, sono bloccato qui perché hai una classe statica con un generatore di numeri casuali statici. Quindi no, Dice.roll()non è un'eccezione valida alla regola "nessuno stato globale".
David Arno,

1
@HexTeke Risposta aggiornata.
Neil,

1
@DavidArno Vero, ma suppongo che a quel punto siamo davvero nei guai se stiamo testando una singola chiamata a random.nextInt(6) + 1. ;)
Neil,

3
@Neil, mi scuso, non mi sono spiegato molto bene. Non stiamo testando la sequenza numerica casuale, stiamo influenzando quella sequenza per assistere altri test. Se stiamo testando ad es. Il RollAndMove()lancio di nuovo quando forniamo un doppio sei, il modo più semplice e robusto per farlo è quello di deridere Diceo il generatore di numeri casuali. Ergo, Dicenon vuole essere una classe statica usando un generatore casuale statico.
David Arno,

12
Il tuo aggiornamento risolve comunque il problema: i metodi statici dovrebbero essere deterministici; non dovrebbero avere effetti collaterali.
David Arno,

9

Per dare un esempio dei limiti di una staticclasse, cosa succede se alcuni dei tuoi giocatori vogliono ottenere un leggero bonus sui loro tiri di dado? E sono disposti a pagare un sacco di soldi! :-)

Sì, potresti aggiungere un altro parametro, quindi Dice.roll(bonus),

Più tardi avrai bisogno dei D20.

Dice.roll(bonus, sides)

Sì, ma alcuni giocatori hanno l'impresa "estremamente capace", quindi non possono mai "armeggiare" (tirare un 1).

Dice.roll(bonus, sides, isFumbleAllowed).

Questo sta diventando disordinato, no?


questo sembra essere ortogonale alla domanda, sta diventando confuso se si tratta di metodi statici o metodi normali
jk.

4
@jk, penso di aver capito il suo punto. Non ha senso avere una classe statica Dadi quando si pensa in più tipi di dadi. In tal caso, possiamo avere diversi oggetti dadi, modellandoli con buone pratiche OOP.
Dherik,

1
@TimothyTruckle Non lo sto contestando, tutto quello che sto dicendo è che questa risposta non risponde effettivamente alla domanda, perché questo tipo di scorrimento non ha nulla a che fare con il metodo statico o meno.
nvoigt,

2
@nvoigt "Non lo sto contestando" - Beh, lo faccio. La caratteristica più potente di un linguaggio OO è il polimorfismo . E l' accesso statico ci impedisce effettivamente di usarlo. E hai ragione: new Dice().roll(...)deve essere concidered accesso statica così come Dice.roll(...). Ne beneficiamo solo se iniettiamo quella dipendenza.
Timothy Truckle,

2
@jk la differenza è che il disordine si staticverifica in ogni chiamata e la conoscenza necessaria è richiesta in ogni chiamata, quindi viene schizzata a livello globale in tutta l'applicazione. In una struttura OOP, solo il costruttore / fabbrica deve essere disordinato e i dettagli di quel dado sono incapsulati. Successivamente, usa il polimorfismo e chiama roll(). Potrei modificare questa risposta per chiarire.
user949300,

3

Nel caso particolare di una classe di dadi, penso che l'uso di metodi di istanza piuttosto che di statica renderà i test significativamente più facili.

Se vuoi testare in unità qualcosa che utilizza un'istanza di dadi (ad esempio una classe di gioco) dai tuoi test puoi iniettare una forma di test doppio di dadi che restituisce sempre una sequenza fissa di valori. Il tuo test può verificare che il gioco abbia il risultato giusto per quei tiri di dado.

Non sono uno sviluppatore Java, ma penso che sarebbe molto più difficile farlo con una classe di dadi completamente statica. Vedi /programming/4482315/why-does-mockito-not-mock-static-methods


Solo per i record: in Java abbiamo PowerMock per sostituire le dipendenze statiche manipolando il codice byte. Ma usarlo è solo una resa al cattivo design ...
Timothy Truckle,

0

Questo è in realtà noto come modello Monostate , in cui ogni istanza (e persino una "non istanza") condivide il proprio stato. Ogni membro è un membro della classe (ovvero, nessun membro di istanza). Sono comunemente usati per implementare classi "toolkit" che raggruppano un insieme di metodi e costanti relativi a una singola responsabilità o requisito ma non hanno bisogno di uno stato per funzionare (sono puramente funzionali). In effetti, Java viene fornito con alcuni di essi in bundle (ad esempio, Math ).

Un po 'fuori tema: raramente sono d'accordo con la denominazione delle parole chiave in VisualBasic, ma in questo caso penso che sharedsia certamente più chiaro e semanticamente migliore (è condiviso tra la classe stessa e tutte le sue istanze) rispetto a static(rimane dopo il ciclo di vita dell'ambito in cui è dichiarato).

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.