I metodi init () sono un codice olfattivo?

13

C'è qualche motivo per dichiarare un metodo init() per un tipo?

Non sto chiedendo se dovremmo preferire init() su un costruttore o come evitare di dichiarare init() .

Sto chiedendo se c'è una qualsiasi logica dietro la dichiarazione di un metodo init() (vedendo quanto è comune) o se si tratta di un odore di codice e dovrebbe essere evitato.

L'idioma init() è abbastanza comune, ma non ho ancora visto alcun beneficio reale.

Sto parlando di tipi che incoraggiano l'inizializzazione tramite un metodo:

class Demo {
    public void init() {
        //...
    }
}

Quando sarà mai utile nel codice di produzione?

Ritengo che possa essere un odore di codice poiché suggerisce che il costruttore non inizializza completamente l'oggetto, risultando in un oggetto parzialmente creato. L'oggetto non dovrebbe esistere se non è impostato lo stato.

Questo mi fa pensare che possa far parte di un qualche tipo di tecnica utilizzata per accelerare la produzione, nel senso di applicazioni aziendali. È l'unica ragione logica per cui riesca a pensare di avere un tale idioma, ma non sono sicuro di come sarebbe utile se fosse così.

    
posta Vince Emigh 31.10.2016 - 07:24
fonte

9 risposte

32

Sì, è un odore di codice. Un odore di codice non è qualcosa che deve sempre essere rimosso. È qualcosa che ti fa dare una seconda occhiata.

Qui hai un oggetto in due stati fondamentalmente diversi: pre-init e post-init. Questi stati hanno responsabilità diverse, metodi diversi che possono essere chiamati e comportamenti diversi. Sono effettivamente due classi diverse.

Se riesci fisicamente a renderle due classi separate, rimuovi staticamente un'intera classe di potenziali bug, al costo di far sì che il tuo modello non corrisponda al "modello del mondo reale" abbastanza da vicino. Di solito il nome è il primo Config o Setup o qualcosa del genere.

Quindi la prossima volta prova a rifactoring i tuoi idi idi di costrutto-init in modelli di due classi e scopri come si realizza.

    
risposta data 31.10.2016 - 14:05
fonte
13

Dipende.

Un metodo init è un odore di codice quando non è necessario separare l'inizializzazione dell'oggetto dal costruttore. A volte ci sono casi in cui ha senso separare questi passaggi.

Una rapida ricerca su Google mi ha fornito questo esempio. Posso facilmente immaginare più casi in cui il codice eseguito durante l'allocazione dell'oggetto (il costruttore) potrebbe essere meglio separato dall'inizializzazione stessa. Forse hai un sistema livellato e l'allocazione / costruzione avviene nel livello X, ma l'inizializzazione solo nel livello Y, perché solo Y può fornire i parametri necessari. Forse "init" è costoso e deve essere eseguito solo per un sottoinsieme degli oggetti allocati e la determinazione di tale sottoinsieme può essere eseguita solo a livello Y. O si desidera sovrascrivere il metodo "init" (virtuale) in un derivato classe che non può essere eseguita con un costruttore. Forse il livello X ti fornisce oggetti allocati da un albero di ereditarietà, ma il livello Y non è a conoscenza della derivazione concreta, ma solo dell'interfaccia comune (dove init può essere definita).

Naturalmente, per la mia esperienza questi casi sono solo una piccola percentuale del caso standard in cui tutte le inizializzazioni possono essere eseguite direttamente nel costruttore e ogni volta che vedi un metodo init separato, potrebbe essere una buona idea interrogare la sua necessità.

    
risposta data 31.10.2016 - 07:42
fonte
5

La mia esperienza si divide in due gruppi:

  1. Codice in cui è effettivamente richiesto init (). Ciò può verificarsi quando una superclasse o un framework impedisce al costruttore della classe di ottenere tutte le sue dipendenze durante la costruzione.
  2. Codice in cui viene utilizzato init () ma potrebbe essere stato evitato.

Nella mia esperienza personale ho visto solo alcune istanze di (1) ma molte più istanze di (2). Di conseguenza, di solito suppongo che init () sia un odore di codice, ma questo non è sempre il caso. A volte non riesci a farcela.

Ho trovato che usare il Builder Pattern può spesso aiutare a rimuovere la necessità / il desiderio di avere un init ().

    
risposta data 31.10.2016 - 14:11
fonte
1

Uno scenario tipico quando è utile un metodo Init è quando si ha un file di configurazione che si desidera modificare e si tiene conto della modifica senza riavviare l'applicazione. Questo, ovviamente, non significa che un metodo Init debba essere chiamato separatamente da un costruttore. È possibile chiamare un metodo Init da un costruttore e quindi richiamarlo in un secondo momento se / se i parametri di configurazione cambiano.

Per riassumere: come per la maggior parte dei dilemmi, se questo è un codice olfattivo o meno, dipende dalla situazione e dalle circostanze.

    
risposta data 31.10.2016 - 11:36
fonte
1

Dipende da come li usi.

Io uso questo modello nei linguaggi raccolti con garbage come Java / C # quando non voglio mantenere la riallocazione di un oggetto sull'heap (come quando sto facendo un videogioco e devo mantenere alte le prestazioni, i garbage collector uccidono le prestazioni ). Io uso il costruttore per fare altre allocazioni di heap di cui ha bisogno e init per creare lo stato utile di base giusto prima di ogni volta che voglio riutilizzarlo. Questo è legato al concetto di pool di oggetti.

È anche utile se hai diversi costruttori che condividono un sottoinsieme comune di istruzioni di inizializzazione, ma in questo caso init sarà privato. In questo modo posso ridurre al minimo ogni costruttore il più possibile, quindi ognuno contiene solo le sue istruzioni univoche e una singola chiamata a init per fare il resto.

In generale però, è un odore di codice.

    
risposta data 31.10.2016 - 23:45
fonte
1
I metodi

init() possono avere un senso quando si hanno oggetti che hanno bisogno di risorse esterne (come, ad esempio, una connessione di rete) che sono contemporaneamente utilizzati da altri oggetti. Potresti non volere / aver bisogno di pescare la risorsa per tutta la vita dell'oggetto. In tali situazioni, potresti non voler allocare la risorsa nel costruttore quando è probabile che l'allocazione delle risorse abbia esito negativo.

Soprattutto nella programmazione embedded, vuoi avere un footprint di memoria deterministico, quindi è pratica comune (buona?) chiamare i tuoi costruttori in anticipo, forse anche statico, e inizializzare solo più tardi quando vengono soddisfatte determinate condizioni.

Oltre a questi casi, penso che tutto dovrebbe andare in un costruttore.

    
risposta data 31.10.2016 - 10:18
fonte
0

In genere preferisco un costruttore che riceve tutti gli argomenti richiesti per un'istanza funzionale. Questo chiarisce tutte le dipendenze di quell'oggetto.

D'altra parte, io uso un semplice framework di configurazione che richiede un costruttore pubblico senza parametri e interfacce per l'iniezione di dipendenze e valori di configurazione. Fatto ciò, il framework di configurazione chiama il metodo init dell'oggetto: ora hai ricevuto tutte le cose che ho per te, fai gli ultimi passi per prepararti al lavoro. Ma nota: è il framework di configurazione che chiama automaticamente il metodo init, tale che non dimenticherai di chiamarlo.

    
risposta data 31.10.2016 - 09:28
fonte
0

Non c'è odore di codice se il metodo init () - è semanticamente incorporato nel ciclo di vita dello stato dell'oggetto.

Se hai bisogno di chiamare init () per mettere l'oggetto in uno stato coerente è un odore di codice.

Esistono diverse ragioni tecniche per cui esiste una struttura di questo tipo:

  1. framework hook
  2. reimpostazione dell'oggetto allo stato iniziale (evitare ridondanza)
  3. possibilità di eseguire l'override durante i test
risposta data 31.10.2016 - 23:15
fonte
-4

Il nome init può a volte essere opaco. Prendiamo una macchina e un motore. Per avviare l'auto (solo all'accensione, per ascoltare la radio), si desidera verificare che tutti i sistemi siano pronti per l'uso.

Quindi costruisci un motore, una porta, una ruota, ecc. lo schermo mostra motore spento.

Non è necessario iniziare a monitorare il motore ecc. poiché sono tutti costosi. Quindi, quando si accende la chiave per accendersi, si chiama engine- > inizio. Inizia a eseguire tutti i processi costosi.

Ora vedi engine = on. E inizia il processo di accensione.

L'auto non si accende senza che il motore sia disponibile.

È possibile sostituire il motore con il calcolo complesso. Come una cella di Excel. Non tutte le celle devono essere attive con tutti i gestori di eventi per tutto il tempo. Quando ti concentri su una cella, puoi avviarla. Aumentare le prestazioni in questo modo.

    
risposta data 31.10.2016 - 08:18
fonte