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.