El arte de convertir 6 líneas en 92 líneas reemplazando if-then-else
y cambiando con objetos – para mejor.
Por supuesto, if-else
y switch
permite un código simple y condensado. Pero su software no debe estar compuesto por la menor cantidad de líneas posibles, sacrificando la legibilidad, el mantenimiento o la flexibilidad.
Veo que se producen muchas ramificaciones en las enumeraciones u otros valores discretos. Algunos desarrolladores incluso se molestan cuando se les dice que no utilicen if-then-else
.
if-then-else
.?La ramificación en valores discretos dificulta el cambio de su software. Cada característica nueva requiere que rastree dónde está ocurriendo la ramificación y modifique su código existente en consecuencia.
Definitivamente no es así como queremos desarrollar un gran software. Es quizás un primer paso perfecto para hacer que su código funcione. Pero, a medida que avanza mejorando tu código, switch
y if-then-else
debe haber desaparecido hace mucho tiempo.
El enfoque tradicional de la ramificación mediante if-else y switch es obsoleto. No es SOLIDO. No es flexible.
Es simplemente horrible.
Seguramente no hay nada orientado a objetos en el enfoque tradicional. Pero, sin embargo, está floreciendo. Lo cual tiene sentido, ya que a los estudiantes se les obliga a pensar que es correcto, o incluso, una mejor práctica.
Seguro, el código está funcionando, pero sabes que puedes hacerlo mucho mejor. El objetivo debería ser implementar un nuevo requisito creando una nueva clase.
Índice
Supongamos que tiene que implementar una forma de actualizar a los usuarios, dado algún motivo. Por simplicidad, un usuario solo tiene dos razones para estar actualizado en su sistema.
Implementa estos dos casos simples en el siguiente fragmento de código.
Tómese un segundo para leer este código mal diseñado. Sin duda, muchos desarrolladores senior están teniendo pesadillas sobre esto e incluso podrían ser considerados un desencadenante de PTSD
.
Sí, he visto código loco como este en la naturaleza. Es una implementación increíblemente ingenua que asume que nunca habrá más razones para que un usuario cambie.
Lo único bueno que se puede decir sobre este código es el intento de lograr un patrón de diseño semi-similar a CQS.
Si su inclinación es decir «¡eso debería ser un cambio!» debe tomarse un momento para reflexionar sobre lo que es importante en el desarrollo de software. Switch
terminado if-else
es completamente irrelevante.
Llegan nuevos requisitos. ¿Quién lo hubiera pensado? Estabas tan seguro de que no pasaría nada.
Digamos que sus requisitos ahora se ven así.
La pregunta es, ¿realmente va a implementar estas dos nuevas razones para actualizar a un usuario agregando valores de enumeración adicionales y agregando dos else-if
?
Así es como se vería si decidiera tomar el camino equivocado. Este código no se lee bien.
Este tipo de implementación es esencialmente el polimorfismo de un pobre.
Además de tener que agregar continuamente ramas adicionales, que es una práctica cuestionable en sí misma, siempre que tenga que depurar o realizar una corrección de errores, lo hará rodeado de un código completamente irrelevante.
Hay un problema más. La firma de este método nos miente, ya que no solo actualiza a un usuario. También está eligiendo qué algoritmo ejecutar según el motivo de la actualización, e incluso conoce cada implementación.
Debe ser obvio para todos en este momento que este método tiene una gran cantidad de responsabilidades.
Estoy seguro de que este ejemplo solidifica todo lo terrible sobre if-else
y switch
.
Echemos un vistazo a cómo puede evitar enfoques desagradables como este.
Refactorizará el desordenado código basado en ramificaciones en clases cohesivas y simples, que se alinean bien con los requisitos reales.
Antes de que alguien grite de horror por usar clases, permítanme aclarar una cosa. El costo de instanciar nuevas clases es a menudo despreciable. No intente optimizar su código antes de que sea un cuello de botella probado.
Bien, sigamos.
Mencioné anteriormente que podemos hacerlo mucho mejor. Por mejor, me refiero a escribir código que sea:
2) Mantenible.
3) Flexible.
Al reemplazar la ramificación tradicional con la ejecución polimórfica, existe una relación clara entre la clase y el requisito que administra. Las clases simples, altamente cohesivas con responsabilidades claras son fáciles de mantener.
Detectar y corregir defectos es muy sencillo. Además de eso, su software se adapta fácilmente a nuevas funciones, sin tener que modificar una clase existente.
Verás lo lejos que podemos llegar sin un solo if-then-else
o switch
.
El UpdateAsync(Reason, User)
ahora se ha vuelto así de simple.
Observe cómo ahora está tomando un argumento de interfaz en lugar de la enumeración. Ahora, el método delega la responsabilidad de saber cómo realizar la actualización a un objeto especializado.
Los IUpdateReason
las implementaciones concretas se ven así. Dejo los detalles sobre los argumentos del constructor y la implementación del método a su imaginación.
Cada clase está completamente alineada con los requisitos que gestiona . Depurar, corregir errores y probar es ahora inmensamente más fácil que en comparación con el horrible enfoque obsoleto.
En este caso, cualquier nuevo requisito da como resultado una clase especializada.
Podemos detenernos fácilmente aquí y dar por terminado el día. Refactorizó la desagradable ramificación y la reemplazó con polimorfismo. Su código ahora está orientado a objetos y es increíblemente fácil de mantener. Gran trabajo.
Tu UpdateAsync(Reason, User)
es algo redundante ahora.
Para solucionar esto, ya no estamos refactorizando. Nos estamos moviendo hacia el rediseño de partes del sistema.
Tiene sentido crear objetos de comando y manejadores de comando en este caso. Simplificará el código de llamada, ya que enviaría un comando como UpdateUserAddress
y se invocaría la acción de un manejador correspondiente.
Tu principal comida para llevar. En resumen, la ramificación tradicional es a menudo una herramienta del estudiante hasta que se descubre un enfoque polimórfico más adecuado.
if-then-else
y switch
hace que su código sea mucho más difícil de leer, mantener y ajustar.
La próxima vez que esté implementando una función utilizando la ramificación de múltiples vías tradicional, tómese un momento y analice cómo puede aprovechar el polimorfismo y los enfoques modernos.
Referencias: Recursos para los curiosos
Haciendo su C # más orientado a objetos por Zoran Horvat.
Gracias por leer este artículo.
no explicas como se crean las instancias de cada una de esas clases, que es lo mas importante… pura palabrería y al final no explicas como funciona ni como se invoca ni como se crea cada una de las clases distintas, en definitiva los if los vas a tener en alguna otra parte.
Habria que ver el código completo para ver como queda