2009-01-30 15 views

Respuesta

5

Desea utilizar una refactorización que reemplaza un condicional utilizando una clase polimórfica. Para example.

O aquí es otra example

En esencia, el ideal es muy simple de crear una jerarquía de objetos, y se mueven los distintos comportamientos en un método sobreescrito. Todavía necesitará un método para crear la clase correcta, pero esto se puede hacer usando un patrón de fábrica.

Editar

quiero añadir que esto no es una solución perfecta en todos los casos. Como (olvidé su nombre, lo siento) señaló en mis comentarios, algunas veces esto puede ser un dolor especialmente si tiene que crear un modelo de objeto solo para hacer esto. Este refactorización excells si tienen esta:

function doWork(object x) 
{ 

    if (x is a type of Apple) 
    { 
     x.Eat(); 

    } else if (x is a type of Orange) 
    { 
     x.Peel(); 
     x.Eat(); 
    } 

} 

Aquí puede refactorizar el interruptor en algún nuevo método que cada fruta manejará.

Editar

Como alguien ha señalado, ¿cómo crear el tipo correcto para entrar en DoWork, hay más formas de resolver este problema, entonces yo podría enumerar PROBally por lo que algunas formas básicas. El primero y más sencillo (y sí va en contra de esta pregunta) es un interruptor:

class FruitFactory 
{ 
    Fruit GetMeMoreFruit(typeOfFruit) 
    { 
     switch (typeOfFruit) 
     ... 
     ... 
    } 
} 

Lo bueno de este enfoque es que es fácil de escribir, y es generalmente el primer método que utilizo. Si bien todavía tiene una instrucción switch está aislada de un área de código y es muy básica, todo lo que devuelve es un n objeto. Si solo tiene un par de objetos y no están cambiando, esto funciona muy bien.

Otros patrones más compelx que puedes mirar es un Abstract Factory. También podría crear dinámicamente Fruit si su plataforma lo admite. También podría usar algo como el Provider Pattern. Lo que esencialmente para mí significa que configuras tu objeto y luego tienes una fábrica que, según la configuración y una clave que le das a la fábrica, crea dinámicamente la clase correcta.

+0

Funciona si tiene objetos y polimorfismo a mano. Dolor si no lo haces. –

+0

Sí, es muy cierto también si la lógica es bastante simple, entonces puede que no valga la pena ... pero si hay mucho en juego dentro de cada cuerpo del condicional, simplemente romperlo puede reducir la complejidad que tiene que preocuparse en cualquier momento. – JoshBerke

+0

¿Cómo se le entrega el tipo correcto a doWork()? –

1

No dice qué idioma está utilizando, pero si está utilizando un lenguaje OO como C++, C# o Java, a menudo puede usar funciones virtuales para resolver el mismo problema que está resolviendo actualmente con una declaración switch, y de una manera más extensible. En el caso de C++, comparar:

class X { 
public: 
    int get_type();  /* Or an enum return type or similar */ 
    ... 
}; 

void eat(X& x) { 
    switch (x.get_type()) { 
    TYPE_A: eat_A(x); break; 
    TYPE_B: eat_B(x); break; 
    TYPE_C: eat_C(x); break; 
    } 
} 

void drink(X& x) { 
    switch (x.get_type()) { 
    TYPE_A: drink_A(x); break; 
    TYPE_B: drink_B(x); break; 
    TYPE_C: drink_C(x); break; 
    } 
} 

void be_merry(X& x) { 
    switch (x.get_type()) { 
    TYPE_A: be_merry_A(x); break; 
    TYPE_B: be_merry_B(x); break; 
    TYPE_C: be_merry_C(x); break; 
    } 
} 

con

class Base { 
    virtual void eat() = 0; 
    virtual void drink() = 0; 
    virtual void be_merry() = 0; 
    ... 
}; 

class A : public Base { 
public: 
    virtual void eat() { /* Eat A-specific stuff */ } 
    virtual void drink() { /* Drink A-specific stuff */ } 
    virtual void be_merry() { /* Be merry in an A-specific way */ } 
}; 

class B : public Base { 
public: 
    virtual void eat() { /* Eat B-specific stuff */ } 
    virtual void drink() { /* Drink B-specific stuff */ } 
    virtual void be_merry() { /* Be merry in an B-specific way */ } 
}; 

class C : public Base { 
public: 
    virtual void eat() { /* Eat C-specific stuff */ } 
    virtual void drink() { /* Drink C-specific stuff */ } 
    virtual void be_merry() { /* Be merry in a C-specific way */ } 
}; 

La ventaja es que se pueden añadir nuevos Base clases derivada de D, E, F y así sucesivamente sin tener que tocar cualquier código que solo trata con punteros o referencias a Base, por lo que no hay nada que pueda desactualizarse de la misma manera que una declaración switch en la solución original.(La transformación se ve muy similar en Java, donde los métodos son virtuales por defecto, y estoy seguro de que también se ve similar en C#). En un proyecto grande, este es un enorme ganabilidad de mantenimiento.

+0

¿Por qué los downvotes personas? –

+0

probablemente porque las funciones virtuales son cómo C++ implementa el polimorfismo; +1 para contrarrestar cobarde drive-by downvote –

+0

@Steven: Supongo ...Hubiera esperado que fuera obvio que esto se aplica en cualquier lenguaje de OO (sé que la versión de Java es casi idéntica, pero por el hecho de que los métodos son "virtuales" por defecto, y es probablemente lo mismo con C#), pero en cualquier caso, he editado para hacer esto más explícito. –

7

¿Has leído this en el código de flecha de aplanamiento de Coding Horror?

Puede reemplazar throw con return o goto si está utilizando un idioma sin excepciones.

+1

+1 cláusulas de guardia son una buena forma de reducir los if anidados y salir de una función antes de tiempo. Mucho más limpio y más fácil de leer en mi humilde opinión. – JoshBerke

3

De hecho, escribí sobre cómo resolver estos problemas en mi blog en abril de 2008. Eche un vistazo a here y dígame lo que piensa.

Te recomiendan:

  1. Uso de polimorfismo para obtener el comportamiento correcto en tiempo de ejecución que necesita sin sentencias condicionales.

  2. Tome todas sus declaraciones condicionales y muévalas a algún tipo de "fábrica" ​​que le proporcionará el tipo apropiado en tiempo de ejecución.

  3. Ya ha terminado. ¿No fue tan fácil? :)

Si desea ver un ejemplo de código real sobre cómo transformar su código, diríjase a mi blog.

P.S. Este no es un intento barato de auto promoción; He sido un usuario de SO durante mucho tiempo, y esta es la primera vez que me he vinculado a mi blog, y solo lo he hecho porque creo que es relevante.

+1

Si extrae el artículo, o al menos acierta en los puntos principales de esta publicación (en lugar de forzar al usuario a hacer clic en el enlace para averiguar de qué se trata), probablemente obtendrá más votos ascendentes ... –

+0

@Adam : Sí, estaba pensando que debería haber hecho eso en mi primera edición de esta respuesta, pero es tarde y estoy cansado. Sin embargo, seguí y seguí tu consejo. ¡Gracias! –

+0

útil; sin embargo, cubre solo 1 tipo de if-else flecha – Adrian

1

Es posible que desee mirar el Strategy Pattern, donde en lugar de poner una larga cadena de ifs con condiciones vinculadas, se abstrae cada condición a un objeto diferente, cada uno definiendo cuál es su comportamiento específico.

La clase que define esos objetos implementará una interfaz que será llamada por el objeto principal.

+0

otra variación es un patrón de plantilla .... muy simillar – JoshBerke

0

¿Qué tal:

/* Code Block 1... */ 

if(/* result of some condition or function call */) 
{ 
    /* Code Block 2... */ 

    if(/* result of some condition or function call */) 
    { 
     /* Code Block 3... */ 

     if(/* result of some condition or function call */) 
     { 
     /* Code Block 4... */ 
     } 
    } 
} 

Se convierte en esto:

/* Code Block 1... */ 
IsOk = /* result of some condition or function call */ 

if(IsOK) 
{ 
    /* Code Block 2... */ 
    IsOk = /* result of some condition or function call */ 
} 

if(IsOK) 
{ 
    /* Code Block 3...*/ 
    IsOk = /* result of some condition or function call */ 
} 

if(IsOK) 
{ 
    /* Code Block 4...*/ 
    IsOk = /* result of some condition or function call */ 
} 

/* And so on... */ 

Puede, por supuesto, si alguna vez regreso Isok se convierte en falsa, en su caso.

Cuestiones relacionadas