software reusable patterns pattern patrones oriented gof geeksforgeeks diseño book design-patterns

design patterns - reusable - campaña anti-si



gof patrones de diseño (15)

...¿Qué?

No puedo imaginarme cómo hacer algo así sin algún tipo de comparación. En absoluto.

Una campaña anti-goto que me puedo imaginar, pero que me parece una estructura de flujo de control bastante necesaria.

Hace poco me encontré con un sitio muy interesante que expresa una idea muy interesante: la campaña anti-si. Puedes ver esto aquí en www.antiifcampaign.com . Tengo que aceptar que las declaraciones IF anidadas complejas son un dolor absoluto en la retaguardia. Actualmente estoy en un proyecto que hasta hace muy poco tenía algunas locas anidadas que se desplazaban hacia la derecha de muchas maneras. Curamos nuestros problemas de dos maneras: utilizamos Windows Workflow Foundation para abordar las inquietudes de enrutamiento (o flujo de trabajo). Y estamos en el proceso de implementar todas nuestras reglas comerciales utilizando ILOG Rules para .NET (¡recientemente adquirido por IBM!). Esto en su mayor parte ha curado nuestros dolores de IF anidados ... pero me pregunto cuántas personas curan sus dolores de la manera que sugieren las buenas personas de AntiIfCampaign ( vea un ejemplo aquí ) al crear numerosas cantidades de clases abstractas para representan un escenario dado que originalmente estaba cubierto por el IF anidado. Me pregunto si otra forma de abordar la eliminación de esta complejidad podría ser utilizar un contenedor IoC como StructureMap para entrar y salir de diferentes partes de la funcionalidad. De cualquier manera...

Pregunta: Dado un escenario en el que tengo una declaración IF o SWITCH compleja anidada que se usa para evaluar un tipo dado de cosas (por ejemplo, evaluar un Enum) para determinar cómo quiero manejar el procesamiento de esa cosa por tipo de enumeración, ¿cuáles son algunas formas de hacer la misma forma de procesamiento sin usar la estructura jerárquica IF o SWITCH?

public enum WidgetTypes { Type1, Type2, Type3, Type4 } ... WidgetTypes _myType = WidgetTypes.Type1; ... switch(_myType) { case WidgetTypes.Type1: //do something break; case WidgetTypes.Type2: //do something break; //etc... }


Creo que entiendo lo que significan. Probablemente no sobreviviremos sin if''s y switches, pero es cierto que los bloques condicionales profundamente anidados son difíciles de leer.

Un ejemplo de mi propio trabajo: una función para calcular la tensión simulada de un puente de Wheatstone en derivación. Esta función toma como 5 parámetros de configuración (qué rama de derivación, hay líneas de detección de derivación, líneas de detección de suministro, ...), y no estaba esperando escribir la rutina de despacho.

Se me ocurrió una estructura de código con plantillas que me permitía ''declarar'' las funciones adecuadas (alrededor de 30 o más) en lugar de tener que procesarlas en una sola rutina de despacho.

Esto nos permitió agregar rápidamente nuevos casos de parámetros de configuración, y ''copiar'' las ecuaciones de la descripción matemática. Una gran victoria, de verdad.


Depende de cómo manejes la situación. Si llamaba a una función diferente con la misma firma, podría tener un diccionario donde la clave sería el tipo y el valor de un delegado que apunta al manejador real. Por lo tanto, puede deshacerse del interruptor y simplemente hacer

_myDictionary[ _myType ]( param1, ... );


El polimorfismo es el camino correcto a seguir cuando la lógica debe integrarse en las clases.

Pero dado que muchas aplicaciones en estos días ya interactúan con su propia base de datos, otra solución es usar lógica basada en tablas. Ventajas de usar tablas:

  1. Ajuste la lógica de negocios sin recompilar.
  2. Las instancias de la base de datos pueden compartir el mismo código pero una lógica diferente.

No estoy hablando de incrustar directamente el código fuente en un campo de base de datos varchar () y compilarlo sobre la marcha.

Pero supongamos que tiene una lista de campos de entrada, y para cada uno, debe realizar (a) algunos RegEx para eliminar el valor, y (b) una o más pruebas para validar el resultado.

En un mundo de solo código, tendría una subclase para cada tipo de campo, cada uno implementando, digamos, sus propios métodos llamados ScrubValue () y Validate ().

En un patrón basado en tablas, una sola clase Field alcanzaría la base de datos (en caché por supuesto) en base a una propiedad FieldType y recuperaría un conjunto de cadenas de búsqueda / reemplazo de RegEx, y una lista de parámetros de prueba de validación (longitud mínima y máxima, patrones de expresiones regulares para que coincidan, etc.).

Esto funciona bien para cualquier situación donde la cantidad de subtipos aumenta con el tiempo y la lógica se puede expresar como un conjunto de cadenas configurables, números, etc. en lugar de tener que implementarse directamente como código.


El problema no es la afirmación ''si'', sino los programadores que escriben el código incorrecto.

EDITAR: Además, como han señalado otros, debe usar polimorfismo (si está disponible) cuando usa sentencias if para verificar el tipo de un objeto, pero si las declaraciones en sí mismas son constructos muy útiles y fundamentales.


En Java es fácil usar enumeraciones como agentes polimórficos anti-if.

public class AntiIf { public enum WidgetTypes { Type1 { public void doSomething() { //... }}, Type2 { public void doSomething() { //... }}, Type3 { public void doSomething() { //... }}, Type4 { public void doSomething() { //... }}; public abstract void doSomething(); } WidgetTypes _myType; // set by someone to one of the types. public void someFunction() { //... _myType.doSomething(); //... } }


Implementar esa idea en un lenguaje tipo C es muy poco convincente ... pero me atrevo a agregar que en un lenguaje funcional adecuado con patrones de patrones reservados es mucho más idiomático y una manera más rica de expresar condicionales.


Las declaraciones de casos funcionan muy bien para organizar if anidados.


Las instrucciones de cambio a menudo se pueden reemplazar por polimorfismo:

abstract class WidgetBase { public abstract void DoSomething(); } class Widget1 : WidgetBase { public override void DoSomething() { // Do something ... } } class Widget2 : WidgetBase { public override void DoSomething() { // Do something ... } } ... Widget widget = new Widget1(); // Use a factory pattern instead. widget.DoSomething();


No se preocupe por la simple declaración if o la declaración de cambio directo.

Preocúpate por esto:

  1. Horriblemente anidado si las declaraciones (el anti-patrón Arrowhead). Estas cosas son imposibles de leer y depurar.
  2. El if (o caso) con un bagilion líneas de código en ellos. "OK, hacemos esto y esto ... en este caso, y eso y eso ... en ese caso. También es muy difícil de leer. Casi imposible de probar o depurar.
  3. Condiciones súper complicadas: if (bowl AND KitchenSink OR BathroomTub AND ...). ¿Qué estás probando de nuevo? Envuelva eso en un método, en este caso: CanHoldWater ().

Puede usar el patrón de comando para realizar comandos basados ​​en algún valor o entrada.

En respuesta a su pregunta, puede usar una tabla de búsqueda simple o polimorfismo para lograr esto, aunque coloca la lógica en una parte diferente del programa, lo que de hecho puede ser lo correcto. Aquí hay un par de ejemplos en C #:

var commands = new Dictionary<WidgetTypes, Action>() { { WidgetTypes.Type1, () => Console.WriteLine("Type 1") }, { WidgetTypes.Type2, () => Console.WriteLine("Type 2") }, { WidgetTypes.Type3, () => Console.WriteLine("Type 3") }, { WidgetTypes.Type4, () => Console.WriteLine("Type 4") } }; commands[WidgetTypes.Type1]();

public interface ICommandHandler { void HandleCommand(); } public class Command1Handler : ICommandHandler { public void HandleCommand() { Console.WriteLine("Type 1"); } } // ... var commands = new Dictionary<WidgetTypes, ICommandHandler>() { { WidgetTypes.Type1, new Command1Handler() }, { WidgetTypes.Type2, new Command2Handler() }, { WidgetTypes.Type3, new Command3Handler() }, { WidgetTypes.Type4, new Command4Handler() } }; commands[WidgetTypes.Type1].HandleCommand();

Y un método de reflexión adicional para invocar el método en el objeto actual con el mismo nombre:

public void Type1() { Console.WriteLine("Type 1"); } //... var type = WidgetTypes.Type2; typeof(MyClass).GetMethod(type.ToString()).Invoke(this, new object[] { });

Nota: no hagas eso


Ser anti-si es tonto.

Algunas veces reemplazar el polimorfismo condicional es lo correcto, pero en esos casos no era la afirmación if el verdadero problema. El problema real era trabajar con tipos abstractos de formas no abstractas, es decir, no pensar en el nivel de abstracción de la clase base.

Otras veces es posible reemplazar un polimorfismo condicional, pero hacerlo sería una mala idea. La lógica que lo lleva a tratar un tipo diferente de otro puede pertenecer al algoritmo en sí, no a las clases individuales. Mover esa lógica a las clases puede hacer que el código de las clases sea demasiado consciente del contexto en el que se usan.

Pero la mayoría de las veces, una declaración if no tiene nada que ver con el polimorfismo en absoluto. Deshacerse de las declaraciones if es el objetivo equivocado aquí.

Ser anti-si es tonto.


Una vez que sus declaraciones if comienzan a hacerse grandes y anidadas, una de las posibilidades es que la cantidad de condiciones que tiene que evaluar haya aumentado. En ese caso, a medida que agrega más condiciones a la situación, comienza a ser exponencialmente más fácil dejar que una combinación de condiciones caiga por las rendijas, y luego, bam, tiene un error.

Siempre pensé que en casos como estos, algún tipo de tabla de verdad sería buena. Acerca de la aproximación más cercana que puedo obtener, aunque termina siendo algo desconcertante para el programador promedio. Implica el ensamblaje de todas sus condiciones en un solo entero, y luego tratar de cubrir cada número en el rango que se crea con una declaración de cambio o una tabla de búsqueda.

Sin embargo, no estoy seguro de que el polimorfismo sea la respuesta correcta para este. ¿Alguien tiene mejores ideas?


Wow ... ¿es esto real?

Podrías atrapar absolutamente todas las excepciones posibles cuando pases por la ruta de ejecución incorrecta hasta que llegues a un camino de ejecución que NO se rompa, pero eso es mucho peor que si lo hicieras.

Puede usar un patrón de comando o un mapa para cuando la orientación a objetos y el polimorfismo son naturales, pero ¿qué ocurre si se trata de resultados de entradas de usuarios que son complejos y NO son en sí mismos objetos?