Qual è il design migliore: determinare se una funzione deve essere eseguita al di fuori di essa o all'interno di essa?

3

Spesso mi vengono presentate queste due opzioni. Dal punto di vista del design, quale di questi è soggettivamente migliore, e perché?

Opzione A

class foo {
  private boolean bar() {
    //Stuff
  }

  private void A() {
     if(bar())
       B();
  }

  private void B() {
      // more stuff
  }
}

Opzione B

class foo {
  private boolean bar() {
    //Stuff
  }

  private void A() {
      B();
  }

  private void B() {
     if(!bar())
        return;

     // more stuff
  }
}

Supponiamo che la duplicazione del codice non sia un problema qui, e la divisione è solo per separare, logicamente, il codice per una migliore leggibilità / comprensione (A, B e la barra hanno nomi significativi nel contesto). È più leggibile che il lettore sappia che B potrebbe non fare nulla guardando solo A? O l'incapsulamento dello stato è meglio inserito in B? Ovviamente, questo può essere rifattorizzato per separare completamente B e bar () in una classe separata, che separa ulteriormente le preoccupazioni, ma assumendo lo stato prevalentemente condiviso, che è meglio?

    
posta Neal Tibrewala 23.03.2011 - 23:39
fonte

5 risposte

7

Il bit importante è chi possiede la barra ? Se non è chiaro, considera se B può essere all'altezza del suo nome del metodo considerando il valore di Bar. Se è così, allora rendilo parte di B. Se così non fosse, allora rendilo parte di A.

In uno scenario reale, generalmente troverai metodi che soddisfano una responsabilità di progettazione e metodi di supporto che soddisfano i passaggi di implementazione. Generalmente, la decisione di chiamare un metodo di implementazione risiede nel metodo API, che è generalmente il metodo pubblico.

public class Control {
    public bool IsVisible;
    public void Render() {
       if (this.IsVisible) {
          this.PrepareForRender();
       }
    }
    private void PrepareForRender() {
       // just do it. Don't check IsVisible
    }
 }

È possibile che tu abbia due diversi metodi API che si chiamano a vicenda. In tal caso, suggerirei di ricorrere alla semantica del perché B non viene eseguito quando Bar è falso? È perché B non è necessario, quindi B dovrebbe decidere.

 public class List {
    public int Count;
    public void SetTo(object[] values) {
       this.Clear(); // need to clear values; don't really care how
    }
    public void Clear() {
       // Count = 0 is just a shortcut to fulfilling Clear's responsibility
       if (this.Count == 0) return; 
    }
 }

È perché B non è corretto? Quindi A dovrebbe decidere.

public class File {
    public bool IsBinary;
    public void Write() {
       if (this.IsBinary) {
          this.OpenBinaryStream();
       } else {
          this.OpenTextStream();
       }     
    }
    private void OpenBinaryStream() {
    }
    private void OpenTextStream() {
    }
 }

Perché è attivamente dannoso? A dovrebbe decidere e B dovrebbe lanciare un'eccezione:

public class File {
    public bool UseSecureDelete;
    public void Delete() {
       if (this.UseSecureDelete) {
          this.ZeroBytes();
       }
    }
    private void ZeroBytes() {
       if (!this.UseSecureDelete) throw new InvalidOperationException();
    }
 }
    
risposta data 22.06.2013 - 20:22
fonte
6

Non sono assolutamente d'accordo con @jmquigley.

Qualunque "guadagno in termini di prestazioni" è qui in gioco è minuscolo e trascurabile.

La condizione "se" dovrebbe risiedere nel membro in cui è logicamente (o anche semanticamente) una parte di.

Dal momento che non ci hai indizi su quali siano le reali funzioni dell'oggetto o del membro, solo tu puoi prendere questa decisione.

    
risposta data 24.03.2011 - 03:49
fonte
3

Dipende se foo() e B() giacciono sullo stesso livello di astrazione.

Se lo fanno, allora Opzione A è preferibile.

Altrimenti ( foo() è un dettaglio di implementazione di B() e quindi inferiore a B() ) L'opzione B è migliore.

Quindi, il principio guida qui è: non mescolare i livelli di astrazione .

    
risposta data 09.07.2014 - 12:23
fonte
1

Vince il codice più breve. Non vuoi avere più aspetti di:

 if(!bar.contains(baz))
   B(baz, stuff);

Se non vuoi MAI aggiungere qualcosa al bar già presente, la seconda implementazione è chiaramente superiore.

Tuttavia, in quasi tutti i linguaggi moderni, puoi creare bar un set, quindi pronunciare qualcosa del tipo:

if (bar.add(baz))
  // more stuff
    
risposta data 24.03.2011 - 02:38
fonte
0

Ti suggerisco di consultare la mitica anatra di gomma del "Anatra debugging dell'anima" famosa. Non lasciatevi ingannare dai suoi poteri di debugging perché è anche abile nel capire i problemi di progettazione.

    
risposta data 23.03.2011 - 23:50
fonte

Leggi altre domande sui tag