Funzione singola da una classe di alto livello VS Multi funzioni da classi di basso livello

1

Supponiamo che io abbia classi di livello basso A, B e C indipendenti l'una dall'altra. Le funzioni in esse prendono un po 'di input e compiono alcuni calcoli e restituiscono un po' di output.

Ora supponiamo di voler creare un eseguibile dove legge in alcuni dati e generare statistiche con il flusso di

input-> A.func1- > B.func1- > C.func1- > uscita

Per me sono disponibili due opzioni,

  1. Crea una classe di alto livello chiamata ABC. Non esegue calcoli, semplicemente riceve l'input dalla funzione principale, analizza l'input e lo passa in A.func1 e inoltra l'output di A.func1 in B.func1 e inoltra l'output di B.func1 in C. func1 e lo invia alla funzione principale.

  2. Nella funzione principale, analizzare direttamente l'input stesso, passarlo in A.func1 e poi B.func1 e poi C.func1 e quindi salvare l'output.

Se la classe ABC esegue internamente alcuni calcoli durante il richiamo di funzioni da A, B e C, non è una cattiva idea che la classe ABC debba esistere. Tuttavia, sto attraversando un periodo difficile per giustificare la necessità di una classe ABC poiché chiama solo funzioni di classe bassa senza alcun calcolo aggiuntivo. Significa anche che se vogliamo creare 10 eseguibili che utilizzano le funzioni delle classi di basso livello con combinazioni e ordini diversi, avremo 10 di queste classi di alto livello.

Tuttavia, ho sentito dire che avere una classe ABC di alto livello è meglio perché   Perché main () dovrebbe essere breve?

Quale approccio è migliore?

    
posta user3667089 13.05.2017 - 04:42
fonte

3 risposte

2

Né 1. né 2. sono sempre "migliori". Ecco come vorrei prendere la decisione in genere:

  • iniziare senza una classe addizionale ABC e chiamare le funzioni da A, B e C direttamente dal principale. Se la logica di orchestrazione è molto semplice, non è necessario cambiarla.

  • non appena la logica in main diventa più complessa , richiede test di unità o deve essere riutilizzata da qualche altra parte , refactoring. Ad esempio, "parsing" ha l'aspetto di un'attività autonoma, potrebbe essere inserito in una classe separata.

  • come risultato del refactoring, potresti finire con una classe separata "ABC" (si spera con un nome più descrittivo). Forse finirai solo con una classe che orchestra A e B, una classe di analisi, una classe di elaborazione di output e lascia le chiamate a C separate. Questo dipende dai dettagli del tuo situtation.

Tuttavia, in genere non prenderei questa decisione in anticipo, penso che sia molto meglio decidere durante il processo di refactoring. Le forze trainanti qui dovrebbero essere

  • formare astrazioni migliori
  • testability
  • riusabilità.

Per programmi molto semplici, è perfettamente possibile che tu abbia tutte le astrazioni di cui hai bisogno in main, non abbia bisogno di scrivere test che richiedono una classe separata ABC, e non hai bisogno di riutilizzare nulla. Se questo è il caso, rimani con l'opzione 2.

    
risposta data 13.05.2017 - 08:55
fonte
1

Qui non c'è niente che mi convinca che tu abbia bisogno di qualche lezione. Quello che stai descrivendo è la composizione funzionale: a(b(c(x))) Sì, il codice può essere così semplice. Se sei in una lingua funzionale.

Ma dal momento che hai menzionato le classi non meno di 12 volte, presumo che tu sia in una specie di paradigma OO. Non sono sicuro che sia la tua lingua, la tua base di codice o semplicemente la tua mente. OO può essere anche funzionale. Ci vuole solo un po 'di lavoro.

Quello che non hai menzionato una volta è un oggetto. Vicino come posso dire A, B e C non hanno stato. Usavo i metodi statici in questa situazione in un linguaggio OO come Java o C # ma ho imparato che gli oggetti stateless sono molto più flessibili dei metodi statici. Quindi piuttosto che importare staticamente A.f1 come a ... per consentire questo codice: a(b(c(x))) Io creo gli oggetti a, b e c e uso questo codice: a.f( b.f( c.f(x) ) ) . Perché? perché sono bloccato in una lingua che non tratterà le funzioni come cittadini di prima classe. In questo modo l'ordine della composizione non è hardcoded. Potrei scambiare a e c se mi piacesse. Questo funziona senza essere in procinto di scambiare le funzioni direttamente come è possibile in una lingua con funzioni di prima classe.

Questo mi porta alla tua domanda su main. Mi piace l'opzione 1. Mi piacerebbe un nome migliore di ABC però. Vorrei costruire gli oggetti a, b e c in main e passarli a ABC quando lo costruisco in main. Solo dopo aver completato questa costruzione, inizierò a utilizzare ciò che ho creato con una chiamata a abc.f(x) .

Il mio modo preferito di fare iniezione delle dipendenze è un buon vecchio passaggio di riferimento ma dopo un pattern di separare l'uso dalla costruzione .

Se avessi scelto l'opzione 2, guarderei direttamente a.f( b.f( c.f(x) ) ) . Quale potrebbe non sembrare così male, ma è il primo passo per fare tutto proceduralmente in main. Mi piace mettere le cose in scatole chiaramente etichettate piuttosto che lasciarle sparpagliare.

Il punto di Rob Kennedy sul riutilizzo della funzione di classe superiore è anche valido. Main non è esattamente il costrutto più riusabile.

    
risposta data 13.05.2017 - 06:15
fonte
0

Vedo le seguenti astrazioni nel tuo post.

  1. Dati dell'applicazione - eventualmente divisi in Dati di input e Dati di output
  2. Input Reader
  3. Calcola motore - ABC
  4. Classi di aiuto di ABC - a, b e c
  5. Output Writer

Per implementare ciò per cui stai sparando, main può essere semplice come:

int main()
{
   InputData input( ... );
   InputReader reader(...);
   int status = reader.read(input);
   if ( is_failure(status) )
   {
      // Deal with error.
   }

   OutputData output;
   ABC abc(...);
   status = abc.process(input, output);
   if ( is_failure(status) )
   {
      // Deal with error.
   }

   OutputWrite writer(...);
   status = writer.write(output);
   if ( is_failure(status) )
   {
      // Deal with error.
   }
}

Per rendere main più flessibile, puoi spostarli tutti in un'altra funzione, ad esempio:

int myAppMain1()
{
   InputData input( ... );
   InputReader reader(...);
   int status = reader.read(input);
   if ( is_failure(status) )
   {
      // Deal with error.
   }

   OutputData output;
   ABC abc(...);
   status = abc.process(input, output);
   if ( is_failure(status) )
   {
      // Deal with error.
   }

   OutputWrite writer(...);
   status = writer.write(output);
   if ( is_failure(status) )
   {
      // Deal with error.
   }

   return status;
}

int myAppMain2()
{
   // Similar to myAppMain1
}

int main(int argc, char** argv)
{
   // Based on options passed to the program, you may use
   // myAppMain1() or myAppMain2().
   return myAppMain1();
}

Penso che sia molto pulito e non rende main o myAppMain* più complesso di quello che devono essere. myAppMain* può vivere in file diversi per mantenere main.cpp molto piccolo e senza complicazioni.

    
risposta data 13.05.2017 - 08:22
fonte

Leggi altre domande sui tag