2009-06-23 11 views
7

Tiendo a escribir con éxito aplicaciones compactas que pueden encapsular muchas lógicas comerciales de una manera simple y no redundante. Tiendo a crear métodos pequeños, pero con el tiempo llego a métodos que tienen demasiados parámetros. Sé que cada código requiere su diseño, pero veo un patrón contrario a mi comportamiento y no estoy seguro de cuál sería la mejor manera de luchar contra él.¿Cómo evitar muchos parámetros en métodos críticos de mis aplicaciones?

Una situación típica sería algo así como:

public loanStructure CalculateSomeComplicatedInterestRate(enum clientType, 
     int totalAmout, bool useTaxes, bool doYearlySummary, bool doNotReloadClient, etc) 
    { 
     loanStructure ret = new loanStructure(); 
     if (doNotReloadClient) ... ; 
     ... 
     if (doYearlySummary) GroupResults(ret); 
     return ret; 
    } 

y dentro del método, un árbol de llamadas hacia delante la configuración de booleanos (doYearlySummary, doNotReloadClient, etc.) a diferentes reglas de negocio que actúa sobre el cálculo.

IE, el problema no reside en el hecho de que todos los parámetros pueden ser encapsulados en un objeto (bigParameterStructure) ... No me siento cómodo con el patrón masterMethod, pero haciendo sobrecargas como CalculateSomeComplicatedInterestRateMonthly y CalculateSomeComplicatedInterestRateYearly acaba de ocultar una private CalculateSomeComplicatedInterestRate .... ¡¡algún problema !!

trabajos de proyecto y orientar objeto gruesa ayudaría ... pero aún así terminar arriba tener este tipo de métodos en algún lugar de mis objetos ...

Bueno chicos ... cualquier ayuda es bienvenida.

Pablo.

+0

La lógica de negocio es lo que es. Si es complicado, es complicado, no es tu culpa. Esto me parece bien, a menos que también tenga ejemplos de esto en el código de lógica no comercial. –

Respuesta

4

Si se encuentra cambiando entre algunos conjuntos de parámetros múltiples, considere ocultar el código en un método complicado privado y proporcione algunos métodos públicos que toman menos parámetros y llaman al privado proporcionando los valores predeterminados adecuados para una parte de los parámetros. Esto limpiará la interfaz pública.

Luego, a menos que realmente no pueda hacerlo por motivos de rendimiento (lo cual no es probable), divida el método privado en llamadas a otros métodos privados, para mantener el código más legible. Puede deshacerse de los comentarios de esta manera, eligiendo nombres de métodos autodescriptivos.

Por otro lado, si algunos de los parámetros no son sobre QUÉ hacer, sino CÓMO hacerlo (es decir, puede tener una enumeración que cambia entre compuestos simples y continuos), considere usar polimorfismo y tener dos clases implementando una interfaz común, cada uno de una manera diferente, O delegando la funcionalidad de esta pieza a un componente de la clase principal que se puede cambiar de esta a la implementación (a'la "inyección de dependencia"). El segundo enfoque hará que su código sea más flexible, ya que los usuarios de su clase podrán proporcionar sus propios componentes y cambiar el comportamiento de su clase principal sin tocar su código.

También debe pensar en quién utiliza su clase. Tal vez la lista de parámetros en su método es tan larga, porque el método ha crecido con el tiempo para satisfacer diferentes necesidades de diferentes usuarios ("usuario" en este contexto significa otro código, incluso escrito por usted). Si es así, al menos crearía un método para un propósito, otro método para otro propósito. Aún pueden compartir código común. Idealmente, le conviene tener dos clases con dos propósitos, para poder cambiarlas en el futuro sin romper la otra funcionalidad.

2

Normalmente creo estructuras de "filtro" que puedo pasar a esos métodos, por lo que en su caso.

(y no la calidad de producción rápida y sucia.)

public struct InterestRateCalculationFilter 
{ 
    public enum ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    public bool UseTaxes {get;set;} 
    public bool DoYearlySummary {get;set;} 
    public bool DoNotReloadClient {get;set;} 

    //Constructors go here, set defaults, etc. 
} 

Uso:

InterestRateCalculationFilter filter = new InterestRateCalculationFilter(); 
filter.ClientType = Customer; 
filter.TotalAmount = 700; 
filter.UserTaxes = true; 
filter.DoYearlySummary = false; 

LoanStructure loanStructure = CalculateSomeComplicatedInterestRate(filter); 

Esto no siempre es una buena idea, pero me voy a él con bastante frecuencia, cuando el parámetro las listas obtienen más de diez elementos, y no necesariamente caen en una estructura de objeto muy bien.

+1

Mejor no dejes que http://stackoverflow.com/users/23354/marc-gravell vea esas estructuras, o él te domesticará como una erupción ;-) aunque creo que la clase en lugar de la estructura es más útil (porque struct pasa por copia) en este caso ** _ si _ ** el destinatario modifica los valores de entrada, de lo contrario, debe pasar por ref. También mire la inicialización del objeto (csc> = 3, creo) para recortar la grasa en el uso. – si618

+0

Usted trae a colación un buen punto. Siempre he usado estructuras para representar este tipo de cosas, aunque encuentro que muchas personas dicen que usen clases. Pero sí me pregunto dónde encajan las estructuras en el espacio actual de desarrollo de aplicaciones. ¡¡A Google!! –

+0

Estoy de acuerdo con la sintaxis de inicialización de objetos, pero no especificó un lenguaje en su pregunta, y no quería lanzar una bola curva demasiado grande con sintaxis. Tenga en cuenta la exención de responsabilidad :) –

1

Este enfoque tiene ciertos inconvenientes, pero una manera de simplificar la lista de argumentos es crear una clase de "argumentos" que tiene propiedades para contener todos los parámetros que se pueden pasar en.

Por ejemplo (C#) :

public class InterestCalculationArguments // or maybe a struct... 
{ 
    public EClientType ClientType {get;set;} 
    public int TotalAmount {get;set;} 
    // etc. 
} 

una cosa buena con este enfoque es que se puede subclasificar la clase argumentos para extender los argumentos que se pueden pasar en, o también puede agregar un "NameValueCollection" (por ejemplo, un mapa cadena-cadena) a Permitir que la persona que llama transmita argumentos adicionales que el método puede procesar. Puede usar este mismo enfoque para el objeto de retorno, una clase para contener varios valores devueltos. Este enfoque también puede aislar a la persona que llama de pequeños cambios en los argumentos/objetos de retorno.

+0

¡gran idea! Voy a usarlo en este momento – chester89

2

Una alternativa al enfoque de "argumentos struct", que a menudo funciona mejor en los casos complicados, es mover la lógica de un gran método a una clase separada:

InterestRateCalculator rateCalc = new InterestRateCalculator(); 
rateCalc.ClientType = ... 
rateCalc.TotalAmount = ... 
rateCalc.CalculateSomeComplicatedInterestRate(); 

Se puede combinar con una especie de de fábrica:

InterestRateCalculator myCalc 
    = sharedCalc.MakeMeAnInterestRateCalculator(); 

Y ya que ha mencionado sobrecarga de funciones para simplificar la lista de argumentos, a veces se puede sustituir argumentos booleanos con la herencia de clases.

métodos
1

A veces es sencillo mover tales listas de parámetros a una clase, si tiene sentido. Por ejemplo, puede tener InterestRateDefinition que podría usarse en muchos lugares diferentes.

Sin embargo, a menudo promueve la creación de clases que simplemente existen para servir como lista de parámetros y la clase no tiene sentido. He hecho esto un poco en el pasado y tienden a desordenar el código en lugar de proporcionar claridad. En estos días, si la lista de argumentos no se presta a una clase, entonces convierto la función en una clase.

Por lo tanto, es posible que tenga una clase InterestRateCalculator que toma una serie de entradas o propiedades públicas.Esto resuelve el problema y mantiene una sensación de "propósito código" además de que puede proporcionar valores por defecto y formas alternativas para llamar a su rutina a través de la clase (por ejemplo CalculateNextCoupon, CalculateAnnual etc.)

Cuestiones relacionadas