2008-12-11 26 views
8

Acabo de ver el video del código limpio de Google en YouTube (consulte link, primer artículo) para eliminar las declaraciones if de su código y usar polimorfismo en su lugar.¿Cómo podría refactorizar este condicional para usar el polimorfismo?

Después de ver el video, eché un vistazo al código que estaba escribiendo antes de ver el video y noté algunos lugares donde podría usar este método, principalmente lugares donde se implementó el mismo tipo de lógica muchas veces. Así que un ejemplo:

Tengo un código como este.

public int Number 
{ 
    get 
    { 
     string returnValue; 
     if (this.internalTableNumber == null) 
      returnValue = this.RunTableInfoCommand(internalTableName, 
                TableInfoEnum.TAB_INFO_NUM); 
     else 
      returnValue = this.RunTableInfoCommand(internalTableNumber.Value, 
                TableInfoEnum.TAB_INFO_NUM); 
     return Convert.ToInt32(returnValue); 
    } 
} 

Lo que hace RunTableInfoCommand no es realmente importante, pero lo más importante es que tengo muchas propiedades con exactamente el mismo if statments lo único que cambia es el TableInfoEnum.

Me preguntaba si alguien podría ayudarme a refactorizar esto para que siga haciendo lo mismo pero sin ningún tipo de declaraciones if?

+0

¿Por qué votar abajo? –

+1

Como originalmente dijo "use polimorfismo", dijo "oh no, no necesito polimorfismo", y ya había gastado una buena cantidad de lo que pensé que era perder el tiempo respondiendo una pregunta que no le interesaba.Ahora que el "polimorfismo de uso" ha vuelto, eliminé el voto negativo y anulé mi respuesta. – tvanfosson

+1

Disculpa, no quería perder el tiempo de nadie, porque uno de esos días, todavía me interesa ver cómo la gente lo haría sin embargo. Nuevamente, lo siento mucho. –

Respuesta

8

Sólo una nota de advertencia aquí después de ver algunas de estas respuestas (técnicamente correctas), simplemente deshacerse de una declaración If no debe ser su único objetivo, el objetivo debe ser hacer su código extensible, fácil de mantener y simple, si eso significa deshacerse de una declaración if, genial, pero no debería ser un objetivo en sí mismo.

En el ejemplo de código que ha proporcionado, y sin saber más acerca de su aplicación, y suponiendo que no extenderá muchas pruebas pasadas para un valor nulo, creo que un If (o incluso un ternario) es el más solución sostenible para ser perfectamente franco.

+0

Sí, este asunto de tratar de reemplazar todas sus declaraciones if con un enfoque polimórfico es un poco loco. – BobbyShaftoe

+0

Sí, he publicado que no es lo más inteligente que se puede hacer en este caso, pero aún me gustaría ver cómo la gente lo atacaría. –

0

Tal vez considere pasar la recuperación del valor de retorno a otra clase que podría inyectarse en tiempo de ejecución.

public class Thing 
{ 
    public IValueFetcher ValueFetcher { get; set; } 

    public int Number 
    { 
    get 
    { 
     return this.ValueFetcher.GetValue<int>(/* parameters to identify the value to fetch */); 
    } 
    } 
} 

Eso haría cargo de una gran cantidad de código repetitivo y reducir su dependencia de la fuente del valor de una interfaz.

Creo que en algún momento es probable que tenga una declaración if, ya que todavía necesita decidir qué versión de RunTableInfoCommand desea llamar.

4

En realidad, implementará algo así como el patrón de Estrategia. Comience definiendo una superclase, llámenlo AbstractTableInfoCommand. Esta clase puede ser abstracta pero debe especificar un método llamado runTableInfoCommand().

Puede definir varias sub clases que implementen el método runTableInfoCommand(). Su clase, la que tiene la propiedad Number, tendrá una nueva propiedad de tipo AbstractTableInfoCommand (vamos a llamarlo tableInfoCommand) que se instanciará a una de las subclases concretas de AbstractTableInfoCommand.

El código será entonces:

public int Number 
    { 
     get 
     { 

      return this.tableInfoCommand.runTableInfoCommand(); 
     } 
    } 

para que pueda crear un NullTableInfoCommand y SomeOtherTableInfoCommand etc. La ventaja es que si usted tiene alguna nueva condición para el retorno de una tableinfocommand entonces agregar una nueva clase en lugar de editar este código

Dicho esto, no todas las situaciones son necesariamente adecuadas para este patrón. Por lo tanto, hace que el código sea más extensible, pero si se encuentra en una situación que no requiere esa capacidad de extensión, podría ser excesivo.

0

Supongo que internTableName y InternalTableNumber son algún tipo de valor para la misma cosa.¿Por qué no se envuelve en el interior de una clase y pasar una instancia de esa clase a this.RunTableInfoCommand así:

public int Number 
     { 
      get 
      { 
       string returnValue; 
       internalTableClass myinstance(parameters); 
       return Convert.ToInt32(this.RunTableInfoCommand(myinstance, TableInfoEnum.TAB_INFO_NUM)); 
      } 
     } 

si aún desea utilizar el polimorfismo a continuación, puede hacerlo de esa clase por la sobrecarga, por ejemplo, un giveInternalTableIdentifier que devuelve el número o el nombre

el código aquí, entonces podría tener este aspecto:

public int Number 
     { 
      get 
      { 
       string returnValue; 
       internalTableClass myinstance(parameters); 
       return Convert.ToInt32(this.RunTableInfoCommand(myinstance.giveInternalTableIdentifier, TableInfoEnum.TAB_INFO_NUM)); 
      } 
     } 

y el código de la internalTableClass será trivial (usando: internalAbstractTableClass, y dos clases que heredan de él, uno dando el nombre y el otro el número)

0

EDIT 2 Cómo realmente resolvería el problema.

Convertiría InternalTableNumber en una propiedad que está cargada de forma diferida. Si no está disponible, lo buscaría a través de InternalTableName. Entonces siempre usaría la propiedad InternalTableNumber para mis métodos.

private int? internalTableNumber; 
private int InternalTableNumber 
{ 
    get 
    { 
     if (!internalTableNumber.HasValue) 
     { 
      internalTableNumber = GetValueFromTableName(internalTableName); 
     } 
     return internalTableNumber; 
    } 
    set 
    { 
     internalTableNumber = value; 
    } 
} 

public int Number 
{ 
    get 
    { 
     string value = this.RunTableInfoCommand(InternalTableNumber, 
               TableInfoEnum.TAB_INFO_NUM); 
     return Convert.ToInt32(value); 
    } 
} 

EDITAR Utilización de polimorfismo ...

Vamos a suponer que la clase actual se denomina Foo, entonces yo refactorearlo en dos clases, FooWithName y FooWithNumber. FooWithName sería la clase que usarías cuando tuvieras el nombre de la tabla y FooWithNumber fuera la clase que usarías cuando tuvieras el número de la tabla. Luego escribía cada clase con un método numérico; de hecho, escribiré una interfaz IFoo también que cada una de ellas implemente para que puedan usarse de forma intercambiable.

public interface IFoo 
{ 
    int Number { get; }| 

} 

public class FooWithName : IFoo 
{ 
    private string tableName; 
    public FooWithName(string name) 
    { 
     this.tableName = name; 
    } 

    public int Number 
    { 
     get { return this.RunTableInfoCommand(this.tableName, 
             TableInfoEnum.TAB_INFO_NUM); 
    } 

    ... rest of class, including RunTableInfoCommand(string,int); 
} 

public class FooWithNumber : IFoo 
{ 
    private int tableNumber; 
    public FooWithNumber(int number) 
    { 
     this.tableNumber = number; 
    } 

    public int Number 
    { 
     get { return this.RunTableInfoCommand(this.tableNumber, 
             TableInfoEnum.TAB_INFO_NUM); 
    } 

    ... rest of class, including RunTableInfoCommand(int,int); 
} 

El usaría como así:

IFoo foo; 

if (tableNumber.HasValue) 
{ 
    foo = new FooWithNumber(tableNumber.Value); 
} 
else 
{ 
    foo = new FooWithName(tableName); 
} 

int number = foo.Number; 

Obviamente, a menos que tenga una gran cantidad de las construcciones then-else if-en su clase existente, esta solución no mejora realmente se mucho. Esta solución crea IFoo usando polimorfismo y luego simplemente utiliza los métodos de interfaz sin preocuparse por la implementación. Esto podría extenderse fácilmente para heredar una implementación común de RunTableCommand (int) en una clase abstracta que hereda IFoo y es la clase base para FooWithNum y FooWithName.

0

Me refactorearlo así:

table = this.internalTableNumber == null ? internalTableName : internalTableNumber.Value; 
return Convert.ToInt32(this.RunTableInfoCommand(table, TableInfoEnum.TAB_INFO_NUM)); 

Amor del operador ternario.

1

Siento que su código está perfectamente bien. Legible. Sencillo. (Y espero que funcione). Si tiene este bloque de código que se repite n veces, debe eliminar la duplicación al aplicar el método Extraer.

La refactorización que indicas tiene la intención de reemplazar las cajas de interruptores recurrentes ... no es simple si las instrucciones son como en tu ejemplo. Replace Conditional With Polymorphism. Recuerde lo más simple que funciona ... lo que significa la cantidad mínima de clases y métodos necesarios para realizar el trabajo.

+0

Si cada propiedad realizara lo mismo si/then/else aún sería un buen candidato para el polimorfismo. – tvanfosson

+0

¿Por qué no aislar el constructo if en un método, que cada propiedad puede llamar? La línea de etiqueta de refactorización de RCWP es 'Usted tiene un condicional que elige un comportamiento diferente según el tipo de objeto'. - En mi humilde opinión ... no aplica aquí. – Gishu

+0

Eso es lo que he hecho ahora, realmente no estaba pensando cuando arrojé esto aquí. –

0

Buscar una refactorización que involucre polimorfismo puede ser excesivo en este caso (dependiendo de qué otras ventajas pueda obtener del polimorfismo). En este caso, agregar una sobrecarga simple que encapsula la lógica de la cual RunTableInfoCommand() llamar puede estar en orden.

Desde RunTableInfoCommand(), internalTableNumber, y internalTableName todos parecen ser miembros de la misma misma clase, un bonito, refactorización fácil podría ser añadir una sobrecarga de RunTableInfoCommand() que simplemente toma un valor TableInfoEnum y hace la lógica de averiguar qué otra RunTableInfoCommand() sobrecarga necesita ser llamado:

private string RunTableInfoCommand(TableInfoEnum infoEnum) 
{ 
    if (this.internalTableNumber == null) { 
     return this.RunTableInfoCommand(internalTableName, infoEnum); 
    } 

    return this.RunTableInfoCommand(internalTableNumber.Value, infoEnum); 
} 

a continuación, los numerosos sitios de llamadas que tienen la misma lógica if decisión puede plegarse a:

returnValue = this.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM);  
             // or whatever enum is appropriate 
0

¿cómo podría refactorizar estas declaraciones if particulares para usar polimorfismo?

Bueno ... No lo haría. No necesita polimorfismo para eliminar las declaraciones if.

Tenga en cuenta que las sentencias if como no 'malo' per se, las sentencias if son causados ​​por la elección de representación, donde

(internalTableNumber == null) ==> 
    internalTableName else internalTableNumber.Value 

Esta asociación implica una clase que falta, es decir, que tendría más sentido para tiene una clase InternalTable que posee internalTableName e internalTablenumber, ya que estos dos valores están fuertemente asociados por la regla de null-check.

  • Esta nueva clase podría proporcionar una propiedad tablenumber que realizó la internalTableNumber == cheque nulo
  • y la RunTableInfoCommand podría tomar una instancia InternalTable como un parámetro (pero no tiene que, ver siguiente punto).
  • Pero aún mejor, la nueva clase debe tener una fachada para el método RunTableInfoCommand (que es probablemente estático) que realizó la conversión entera.

Suponiendo que la instancia InternalTable se llama ITable, el código refactorizado de preocupación sería el siguiente aspecto:

public int Number 
{ 
    get 
    { 
     return iTable.RunTableInfoCommand(TableInfoEnum.TAB_INFO_NUM); 
    } 
} 

Esto encapsular el cheque nulo en la clase InternalTable. La modificación de la firma RunTableInfoCommand original es opcional.

Tenga en cuenta que esto no hace no "reemplace las sentencias if con polimorfismo", pero elimina las sentencias if de la clase consumidora a través de la encapsulación.

+0

podría simplemente hacer una función para ocultar la verificación nula, pero la firma de esta función sería un subconjunto del estado del objeto, lo que lógicamente puede implicar una nueva clase ... –

Cuestiones relacionadas