Ho un progetto. In questo progetto ho voluto riformattare per aggiungere una funzione e ho riformattato il progetto per aggiungere la funzione.
Il problema è che quando ho finito, ho scoperto che avevo bisogno di apportare una piccola modifica all'interfaccia per adattarla. Quindi ho fatto il cambiamento. E quindi la classe che consuma non può essere implementata con la sua interfaccia attuale in termini di quella nuova, quindi ha bisogno anche di una nuova interfaccia. Ora sono trascorsi tre mesi, e ho dovuto risolvere innumerevoli problemi praticamente indipendenti, e sto cercando di risolvere problemi che erano stati programmati per un anno da oggi o semplicemente elencati come non risolvibili a causa di difficoltà prima che la cosa si compili ancora.
Come posso evitare questo tipo di refactoring a cascata in futuro? È solo un sintomo delle mie lezioni precedenti che dipendono troppo strettamente l'una dall'altra?
Breve modifica: in questo caso, il refactor era la caratteristica, poiché il refactor aumentava l'estensibilità di un particolare pezzo di codice e diminuiva l'accoppiamento. Ciò significava che gli sviluppatori esterni potevano fare di più, che era la funzione che volevo offrire. Quindi lo stesso refattore originale non avrebbe dovuto essere un cambiamento funzionale.
Modifica più grande che avevo promesso cinque giorni fa:
Prima di iniziare questo refattore, avevo un sistema in cui avevo un'interfaccia, ma nell'implementazione, semplicemente dynamic_cast
attraverso tutte le possibili implementazioni che ho spedito. Ciò ovviamente significava che non potevi semplicemente ereditare dall'interfaccia, per prima cosa, e in secondo luogo, che sarebbe impossibile per chiunque senza 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 implementare l'interfaccia fosse l'intero contratto richiesto, ovviamente un miglioramento.
Quando stavo trovando e uccidendo con il fuoco tutti i posti in cui l'avevo fatto, ho trovato un posto che si è rivelato un problema particolare. Dipendeva dai dettagli di implementazione di tutte le varie classi derivate e dalle funzionalità duplicate che erano già implementate ma meglio altrove. Potrebbe invece essere stato implementato in termini di interfaccia pubblica e riutilizzato l'implementazione esistente di tale funzionalità. Ho scoperto che per funzionare correttamente richiedeva un determinato contesto. In parole povere, l'implementazione precedente 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
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 f
facevano parte di alcune, alcune di esse devono essere rese parte della nuova funzione g
che opera senza un contesto e alcune di esse devono essere fatte di una parte dell'ormai differito f
. Ma non tutti i metodi f
chiamano bisogno o vogliono questo contesto, alcuni di essi hanno bisogno di un contesto distinto che ottengono con mezzi separati. Quindi, per tutto ciò che f
finisce per chiamare (che è, in parole povere, praticamente tutto ), ho dovuto determinare quale, se del caso, contesto avessero bisogno, da dove dovrebbero ottenerlo e come dividerli da vecchi f
a nuovi f
e nuovi g
.
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.