Ho un progetto. In questo progetto ho voluto refactoring per aggiungere una funzionalità, e ho refactored il progetto per aggiungere la funzionalità.
Il problema è che quando ho finito, ho scoperto che dovevo apportare una piccola modifica all'interfaccia per adattarla. Così ho fatto il cambiamento. E quindi la classe consumante non può essere implementata con la sua interfaccia attuale in termini di quella nuova, quindi ha bisogno anche di una nuova interfaccia. Ora sono passati tre mesi e ho dovuto risolvere innumerevoli problemi praticamente non correlati, e sto cercando di risolvere problemi che sono stati tracciati per un anno da adesso o semplicemente elencati come non risolveranno a causa di difficoltà prima che la cosa si compili ancora una volta.
Come posso evitare questo tipo di rifattori a cascata in futuro? È solo un sintomo delle mie precedenti lezioni che dipendono troppo strettamente l'una dall'altra?
Breve modifica: in questo caso, il refattore era la funzione, poiché il refactor aumentava l'estensibilità di un particolare pezzo di codice e diminuiva alcuni accoppiamenti. Ciò significava che gli sviluppatori esterni potevano fare di più, che era la funzione che volevo offrire. Quindi il refactore stesso non avrebbe dovuto essere un cambiamento funzionale.
Modifica più grande che ho promesso cinque giorni fa:
Prima di iniziare questo refactator, avevo un sistema in cui avevo un'interfaccia, ma nell'implementazione, ho semplicemente dynamic_cast
attraverso tutte le possibili implementazioni che ho spedito. Questo ovviamente significava che non si poteva semplicemente ereditare dall'interfaccia, per una cosa, e in secondo luogo, che sarebbe impossibile per chiunque senza l'accesso all'implementazione implementare questa interfaccia. Così ho deciso che volevo risolvere questo problema e aprire l'interfaccia per il consumo pubblico in modo che chiunque potesse implementarlo e che l'implementazione dell'interfaccia richiedesse l'intero contratto, ovviamente un miglioramento.
Quando stavo cercando e uccidendo con il fuoco tutti i posti in cui l'avevo fatto, ho trovato un posto che si è rivelato un problema particolare. Dipende dai dettagli di implementazione di tutte le varie classi derivate e dalle funzionalità duplicate già implementate ma migliori altrove. Potrebbe invece essere implementato in termini di interfaccia pubblica e riutilizzare l'implementazione esistente di tale funzionalità. Ho scoperto che richiedeva un particolare contesto per funzionare correttamente. In parole povere, l'implementazione precedente della chiamata sembrava un po 'come
for(auto&& a : as) {
f(a);
}
Tuttavia, per ottenere questo contesto, avevo bisogno di cambiarlo in qualcosa di più simile a
std::vector<Context> contexts;
for(auto&& a : as)
contexts.push_back(g(a));
do_thing_now_we_have_contexts();
for(auto&& con : contexts)
f(con);
Ciò significa che per tutte le operazioni che erano una parte di f
, alcune di esse devono essere rese parte della nuova funzione g
che opera senza contesto, e alcune di esse devono essere fatte di una parte del% co_de ora differito. Ma non tutti i metodi di chiamata di f
necessitano o vogliono questo contesto - alcuni di essi necessitano di un contesto distinto che ottengono attraverso mezzi separati. Quindi, per tutto ciò che f
finisce per chiamare (che è, grosso modo, tutto ), ho dovuto determinare quali, se del caso, il contesto di cui avevano bisogno, da dove avrebbero dovuto riceverlo, e come dividerli dal vecchio f
in nuovo f
e nuovo f
.
Ed è così che sono finito dove sono ora. L'unica ragione per cui ho continuato è perché avevo bisogno di questo refactoring per altri motivi comunque.