Refactoring codice numerico per TDD e incapsulamento

4

Sto venendo a patti con TDD e il fatto che ho bisogno di ridimensionare il codice che sto scrivendo (ri). Sto avendo un problema che penso sia un classico conflitto --- TDD vs. incapsulamento di metodi / dati privati --- e ho bisogno di consigli su come strutturare il mio codice correttamente in modo che tutto sia verificabile tramite la sua interfaccia pubblica.

Sto scrivendo una classe che esegue un calcolo pesante per un codice critico delle prestazioni. Contiene ~ 10% distd::valarray s che, nell'uso tipico, possono avere tra 50 e 200 galleggianti. Tutti i metodi si basano su alcuni o tutti quegli array --- di solito comprimendoli in loop. Inoltre, c'è un pugno pieno di costanti comuni a tutti i metodi.

Poiché questi metodi utilizzano tutti gli stessi dati, appartengono davvero a una classe insieme. (Nella mia prima iterazione del codice (in C) le mie firme di chiamata di funzione erano orripilanti ma ripetitive.)

La classe ha bisogno solo di uno o due metodi pubblici che restituisca una serie relativamente semplice di strutture POD. Quindi, l'input è semplice, l'output è semplice ma c'è un calcolo HEAVY su molti dati autogenerati.

Un'illustrazione semplificata potrebbe essere simile a questa:

class HighPerformingDoohicky(){
 private:
  std::valarray a, b, c,
   ...
  float some_constants // all methods depend on these.

  void method1() // pounds on a, b, c.
  void method2() // pounds on c, d, e.
  void method3() // pounds on all of them    

 public:
  HighPerformingDoohicky(a_few_numbers) some_constants{a_few_numbers} : {}
  std::valarray<float> method4() // loop/iterate calls to methods 1, 2 and 3 to produce output
}

Quindi c'è un * LOT * di dati privati quindi qualsiasi dato è solo una piccola parte dell'output generato. Testare i metodi di output in realtà non mi aiuta a sviluppare i valori numerici e sarebbe facile per i problemi sottili passare inosservati nell'interfaccia pubblica.

Sono felice di ristrutturare (classi di amici forse?) e non sono fissato sulla soluzione con una forma particolare a patto che sia facile da leggere, sia verificabile, sia corretta ed efficiente.

Addendum: Nel caso qualcuno lo trovi illuminante per sapere in modo più specifico sui calcoli, ecco un riassunto. Sei delle variabili sono lo spazio di stato per un sistema dinamico. Uno dei metodi è un integratore di Eulero che usa le equazioni del movimento (e del controllo) per proiettare una traiettoria dello spazio degli stati in base alle condizioni iniziali (input). Un altro metodo calcolerà un insieme di moltiplicatori di Lagrange basati su quella traiettoria. Un altro metodo calcolerà un vettore gradiente che dipende dalla traiettoria e dai moltiplicatori di Lagrange. Il metodo 3 chiamerà ripetutamente quelle funzioni per risolvere un problema di ottimizzazione. Tutte queste cose (incluso il gradiente) sono valarray che sono N elementi lunghi, con N da qualche parte tra 20 e 200 nel funzionamento normale.

    
posta Timtro 04.09.2015 - 23:40
fonte

1 risposta

5

Six of the variables are the state space for a dynamical system. One of the methods is an Euler integrator that use the equations of motion (and control) to project a state space trajectory based on initial conditions (input). Another method will compute a set of Lagrange multipliers based on that trajectory. Another method will compute a gradient vector that depends on the trajectory and the Lagrange multipliers. Method 3 will repeatedly call those functions to solve an optimization problem. All of these things (including the gradient) are valarray which are N elements long, with N somewhere between 20 and 200 in normal operation.

Non conosco i componenti interni della tua classe, ma questa descrizione suona molto come le diverse responsabilità, che potrebbero essere separate in classi diverse, ognuna con la propria interfaccia pubblica. Il fatto che alcuni di essi operino sugli stessi dati non rende obbligatorio metterli tutti nella stessa classe - nessuno ti vieta di progettare classi prendendo in considerazione i dati di un'altra classe se ciò ti aiuta a ottenere le cose in un modo migliore forma. La chiave per farlo funzionare è come sempre per ottenere le diverse astrazioni a destra (e in C ++: avendo un piano chiaro per la proprietà dei dati).

Tuttavia, se pensi davvero di non essere in grado di progettare questo programma in modo più separato, potrebbe essere a scopo di prestazioni, quindi potrebbe essere un compromesso tra diversi obiettivi di progettazione: prestazioni rispetto a "non testare privati" funzioni "mantra. In realtà, non esiterei a interrogare quest'ultimo in una situazione del genere un po '. A volte l'aggiunta di "botole di manutenzione" alle tue classi può aiutarti - metodi di supporto pubblico, che ti permetteranno di accedere ad alcuni interni / meccanismi interni solo a scopo di test.

    
risposta data 05.09.2015 - 08:12
fonte

Leggi altre domande sui tag