Cosa dovrebbe contenere un costruttore?

2

Che cosa dovrebbe contenere un costruttore?

In entrambi i casi, sono necessari tutti e tre gli argomenti per far funzionare la classe.

Quale approccio è migliore e perché?

1)

class Language {
  LanguageRepository languageRepository;

  Language(LanguageRepository languageRepository) {
    this.languageRepository = languageRepository;
  }

  public void doThat(String firstArg, String secondArg) {  
      languageRepository(firstArg, secondArg);
  }
}

2)

class LanguageDoThat {
  Language(LanguageRepository languageRepository, String firstArg, String secondArg) {
    languageRepository(firstArg, secondArg)
  }
}

3)

class Language {
  LanguageRepository languageRepository;
  String firstArg;
  String secondArg;

  Language(LanguageRepository languageRepository, String firstArg, String secondArg) {
    this.languageRepository = languageRepository;
    this.firstArg = firstArg;
    this.secondArg = secondArg;
  }

  public void doThat() {  
      languageRepository(firstArg, secondArg);
  }
}

o altro?

Ci sono solo esempi, ma in queste classi non ci saranno più metodi.

    
posta jarer 25.02.2018 - 11:37
fonte

5 risposte

3

L'opzione migliore sembra 1 o 3 , ma abbiamo bisogno di maggiori dettagli su ciò che è firstArg e secondArg. Ci sono molte domande:

  1. Qual è lo scopo della classe Language ? È un oggetto? È una classe di supporto? C'è qualche tipo di classe di servizio e il repository può essere iniettato?
  2. Chi è il primo e il secondo argomento? Sono campi di Language ? Verranno utilizzati da altri metodi all'interno di Language ? Il repository è utilizzato da un solo metodo?

Solo cercando ciò che ci mostri, l'opzione migliore è 4 . Qual è l'opzione 4?

Aspetto:

class Language {
  public void doThat(LanguageRepository repository, String firstArg, String secondArg) {  
      // code
  }
}

Perché?

  • La tua classe di lingua sembra una classe di supporto . Non si menziona se firstArgs e secondArgs sono campi di Language . Se vengono passati solo per essere elaborati per un metodo casuale all'interno di Language , sembra preferibile avere un metodo esclusivo per accettarli.
  • Nessun argomento è il campo della lingua . Passare il repository o gli argomenti sul costruttore sarebbe valido se verrà utilizzato da altri metodi all'interno della classe Language . Se solo un metodo utilizza il repository, non è necessario inserire il costruttore.
  • Le dipendenze della classe e i metodi sono più chiari perché solo un metodo richiede repository e gli argomenti.
  • Più facile al test delle unità perché solo un metodo ( doThat ) ha bisogno di loro. Se passi il repository e gli argomenti nel costruttore per un solo metodo, nasconderai le dipendenze reali della tua classe.
risposta data 25.02.2018 - 14:11
fonte
3

Da quello che hai chiesto, la tua classe sembra essere un veicolo per chiamare doThat() . E doThat() ha bisogno di tre "parametri", un languageRepository , un firstArg e un secondArg .

Per me, "orientato agli oggetti" significa che ogni singola istanza che hai nella tua applicazione dovrebbe rappresentare qualcosa (corrispondente al nome della classe), e non la vedo nella tua domanda.

Da ora in poi, devo fare qualche congettura.

Suppongo che durante la vita della tua applicazione, vuoi chiamare doThat() più di una volta, con i singoli valori firstArg e secondArg , ma con costante languageRepository (o o alcuni enumerati).

Approccio 3

Quindi l'approccio 3 (che imposta tutti e tre i parametri nel costruttore) crea un'istanza di eliminazione per ogni singola chiamata doThat() . Facendo ciò con un nome di classe di Language si genera Great Astonishment (perché dovresti creare una nuova lingua per ogni chiamata di doThat() - il numero di lingue nel mondo è noto, limitato ed enumerabile).

L'utilizzo tipico sarebbe come:

Language lang = new Language(repository, firstArg, secondArg);
lang.doThat();

E in seguito, l'istanza lang diventa inutile: l'istanza non ha alcuno scopo e può essere sostituita da un metodo statico (raramente una buona scelta).

Approccio 2

Con il tuo approccio 2, le cose vanno ancora peggio:

LanguageDoThat lang = new LanguageDoThat(repository, firstArg, secondArg);

Questo diventa inutile spazzatura nel momento stesso in cui hai finito di creare l'istanza. E il tuo utente deve imparare che qui, creare un'istanza da buttare fa un lavoro reale - Very Great Astonishment.

Approccio 1

Questo è l'unico che potrebbe avere senso. Inizialmente crei un Language con un dato (e presumibilmente costante) languageRepository (o alcuni di essi con diversi linguaggi specifici), e chiama doThat() più volte più volte con% di variazione firstArg e secondArg argomenti. Quindi un'istanza Language rappresenta alcuni aspetti del linguaggio rilevanti per la tua applicazione (manifest nel metodo doThat() ) e sopravvive per più di una chiamata doThat() . L'utilizzo potrebbe essere qualcosa come:

Language lang = Language.getInstance("en"); // or whatever way you choose,
                                            // maybe Songleton Pattern,
                                            // maybe Dependency Injection...
lang.doThat(firstArg, secondArg);

Alternative

LanguageRepository

Aggiungi il metodo doThat() alla classe del repository di lingue. Dichiarazione di non responsabilità: dovresti decidere se si adatta in modo semantico.

Metodo statico

Crea doThat() un metodo statico della classe Language, chiarendo che si tratta di una semplice chiamata procedurale:

public static void doThat(LanguageRepository repo, String arg1, String arg2) {  
    ...
}

Runnable

Implementa l'interfaccia Runnable per doThat() :

public class LanguageDoThatRunnable implements Runnable {
    private LanguageRepository repo;
    private String arg1;
    private String arg2;
    public Language(LanguageRepository repo, String arg1, String arg2) {
        this.repo = repo;
        this.arg1 = arg1;
        this.arg2 = arg2;
    }
    public void run() {
        ...
    }
}

Con Runnable, gli sviluppatori sono usati per creare un'istanza con tutti i parametri necessari per l'esecuzione già nel costruttore e avere un metodo no-args run () per il lavoro. Tecnicamente, siamo vicini al tuo approccio 3, ma nominarlo come Runnable (e implementare quell'interfaccia) evita lo stupore. Ma potrebbe essere eccessivo.

Consigli

Decidi quale istanza di una classe intende rappresentare, quindi la struttura lo segue.

Il costruttore dovrebbe rendere l'istanza una rappresentazione valida del concetto e "fare" qualsiasi cosa dovrebbe essere il dovere dei metodi.

    
risposta data 25.02.2018 - 18:43
fonte
1

Sono contrario a 2 a causa di tre idee importanti:

  1. È bene separare l'uso dalla costruzione
  2. Un costruttore che fa il vero lavoro è un odore di codice
  3. Principio di Least Astonishment

1) Combinare la costruzione e l'uso insieme rende il codice inflessibile e lega le conoscenze allo stesso momento piuttosto che lasciare che le cose vengano decise in momenti diversi.

2) Se un costruttore svolge un lavoro reale, hai forzato gli atti di apprendimento dei tuoi collaboratori e di esibire il tuo comportamento affinché accadesse sempre allo stesso tempo.

3) Perché questo:

new Language(languageRepository, firstArg, secondArg);

sembra una perdita di tempo per il garbage collector. Non sembra che avrebbe dovuto fare qualcosa oltre a confermare.

BinaryMethod.invoke(languageRepository, firstArg, secondArg);

Sembra che farà qualcosa. La codifica è abbastanza difficile senza creare enigmi.

    
risposta data 25.02.2018 - 12:52
fonte
0

Generalmente opterei per l'approccio "costruzione dovrebbe produrre un oggetto funzionale valido", quindi crea un costruttore che prende tutti e 3 altri costruttori che richiedono meno, ma imposta valori predefiniti validi per gli altri argomenti.

Ma potrebbe essere necessario fare cose di fantasia con le fabbriche di oggetti che hanno bisogno di un costruttore predefinito per funzionare. Questo dipende dalla tua lingua.

In ogni caso, se dovessi avere costruttori che non producono un oggetto in uno stato operativo, dovresti controllare in ogni metodo e lanciare una InvalidOperationException o qualcosa di simile nel dettaglio del problema, per fare assicurati che le proprietà necessarie siano impostate prima che venga chiamato un metodo.

    
risposta data 25.02.2018 - 12:27
fonte
0

L'approccio 1 o 3. La convenzione è che il costruttore crea un oggetto vuoto che è pronto per funzionare, non un oggetto che inizia immediatamente a funzionare.

Sebbene funzionino entrambi, dopo di ciò, dipende da come si desidera utilizzare la funzione doThat ..

    
risposta data 25.02.2018 - 12:50
fonte