Tengo un proyecto En este proyecto, deseé refactorizarlo para agregar una característica, y refactoré el proyecto para agregar la característica.
El problema es que cuando terminé, resultó que necesitaba hacer un pequeño cambio de interfaz para acomodarlo. Entonces hice el cambio. Y luego la clase consumidora no se puede implementar con su interfaz actual en términos de la nueva, por lo que también necesita una nueva interfaz. Ahora son tres meses después, y he tenido que solucionar innumerables problemas virtualmente no relacionados, y estoy buscando resolver problemas que fueron trazados durante un año a partir de ahora o simplemente enumerados como que no se solucionarán debido a la dificultad antes de que la cosa se compile de nuevo.
¿Cómo puedo evitar este tipo de refactorizaciones en cascada en el futuro? ¿Es solo un síntoma de que mis clases anteriores dependen demasiado entre sí?
Edición breve: en este caso, el refactor fue la característica, ya que el refactor aumentó la extensibilidad de un fragmento de código en particular y disminuyó el acoplamiento. Esto significaba que los desarrolladores externos podían hacer más, que era la característica que quería ofrecer. Entonces el refactor original en sí mismo no debería haber sido un cambio funcional.
Edición más grande que prometí hace cinco días:
Antes de comenzar este refactorizador, tenía un sistema donde tenía una interfaz, pero en la implementación, simplemente hice dynamic_cast
todas las implementaciones posibles que envié. Obviamente, esto significaba que no podía simplemente heredar de la interfaz, por un lado, y en segundo lugar, que sería imposible para cualquiera sin acceso de implementación implementar esta interfaz. Así que decidí que quería solucionar este problema y abrir la interfaz para el consumo público para que cualquiera pudiera implementarla y que implementar la interfaz fuera todo el contrato requerido, obviamente una mejora.
Cuando estaba encontrando y matando con fuego todos los lugares donde había hecho esto, encontré un lugar que resultó ser un problema particular. Depende de los detalles de implementación de todas las diversas clases derivadas y la funcionalidad duplicada que ya se implementó pero mejor en otro lugar. Podría haberse implementado en términos de la interfaz pública y reutilizado la implementación existente de esa funcionalidad. Descubrí que requería un contexto particular para funcionar correctamente. En términos generales, la implementación previa de la llamada se parecía un poco
for(auto&& a : as) {
f(a);
}
Sin embargo, para obtener este contexto, necesitaba cambiarlo a algo más parecido
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);
Esto significa que para todas las operaciones que solían ser parte de ellas f
, algunas de ellas deben formar parte de la nueva función g
que opera sin contexto, y algunas de ellas deben formar parte de la parte diferida ahora f
. Pero no todos los métodos f
llaman necesidad o quieren este contexto, algunos de ellos necesitan un contexto distinto que obtienen por medios separados. Entonces, para todo lo que f
termina llamando (que es, más o menos, casi todo ), tuve que determinar qué contexto, si es que necesitaban, de dónde deberían obtenerlo y cómo dividirlos de lo antiguo f
en lo nuevo f
y lo nuevo g
.
Y así es como terminé donde estoy ahora. La única razón por la que seguí adelante es porque necesitaba esta refactorización por otras razones de todos modos.