requerido referencia que programacion por pase pasaje parametros parametro parameter ningun funciones formal enviar dado corresponda argumento parameters refactoring constructor coding-style

parameters - referencia - parametros programacion c#



¿Cuántos argumentos de constructor son demasiados? (14)

A menos que sea más de 1 argumento, siempre uso matrices u objetos como parámetros de constructor y confío en la comprobación de errores para asegurarme de que los parámetros requeridos estén allí.

Supongamos que tiene una clase llamada Cliente, que contiene los siguientes campos:

  • Nombre de usuario
  • Email
  • Nombre de pila
  • Apellido

Digamos también que según su lógica comercial, todos los objetos del Cliente deben tener estas cuatro propiedades definidas.

Ahora, podemos hacer esto bastante fácilmente forzando al constructor a especificar cada una de estas propiedades. Pero es muy fácil ver cómo esto se puede descontrolar cuando se ve obligado a agregar más campos obligatorios al objeto Cliente.

He visto clases que incluyen más de 20 argumentos en su constructor y es muy doloroso usarlos. Pero, como alternativa, si no necesita estos campos, corre el riesgo de tener información no definida o, peor aún, errores de referencia de objeto si confía en el código de llamada para especificar estas propiedades.

¿Hay alguna alternativa a esto o solo tienes que decidir si la cantidad X de argumentos del constructor es demasiada para que puedas vivir?


Creo que la forma más fácil sería encontrar un valor predeterminado aceptable para cada valor. En este caso, cada campo se ve como necesario para construir, por lo que posiblemente sobrecargue la llamada de función para que, si algo no está definido en la llamada, para establecerlo en un valor predeterminado.

Luego, realice las funciones getter y setter para cada propiedad para que los valores predeterminados puedan cambiarse.

Implementación de Java:

public static void setEmail(String newEmail){ this.email = newEmail; } public static String getEmail(){ return this.email; }

Esta también es una buena práctica para mantener sus variables globales seguras.


Creo que la respuesta de "OOP puro" es que si las operaciones en la clase son inválidas cuando ciertos miembros no se inicializan, entonces estos miembros deben ser establecidos por el constructor. Siempre existe el caso de que se puedan usar valores predeterminados, pero supongo que no estamos considerando ese caso. Este es un buen enfoque cuando la API es fija, porque cambiar el único constructor permitido después de que la API se haga pública será una pesadilla para usted y para todos los usuarios de su código.

En C #, lo que entiendo acerca de las pautas de diseño es que esta no es necesariamente la única forma de manejar la situación. Particularmente con objetos WPF, encontrará que las clases .NET tienden a favorecer constructores sin parámetros y arrojarán excepciones si los datos no se han inicializado a un estado deseable antes de llamar al método. Sin embargo, esto es probablemente principalmente específico del diseño basado en componentes; No puedo encontrar un ejemplo concreto de una clase .NET que se comporte de esta manera. En su caso, definitivamente causaría una mayor carga en las pruebas para garantizar que la clase nunca se guarde en el almacén de datos a menos que las propiedades hayan sido validadas. Sinceramente, debido a esto prefiero el enfoque de "constructor establece las propiedades requeridas" si su API está en piedra o no es pública.

De lo único que estoy seguro es de que probablemente haya innumerables metodologías que pueden resolver este problema, y ​​cada una de ellas presenta su propio conjunto de problemas. Lo mejor que puede hacer es aprender tantos patrones como sea posible y elegir el mejor para el trabajo. (¿No es eso una excusa de una respuesta?)


Creo que su pregunta es más sobre el diseño de sus clases que sobre la cantidad de argumentos en el constructor. Si necesitaba 20 datos (argumentos) para inicializar con éxito un objeto, probablemente consideraría dividir la clase.


Creo que todo depende de la situación. Para algo como tu ejemplo, una clase de cliente, no arriesgaría la posibilidad de que esos datos no estén definidos cuando sea necesario. Por otro lado, pasar una estructura aclararía la lista de argumentos, pero aún tendría muchas cosas que definir en la estructura.


Dos enfoques de diseño a considerar

El patrón de essence

El patrón de interfaz fluido

Ambos son similares en su intención, ya que construimos lentamente un objeto intermedio y luego creamos nuestro objeto objetivo en un solo paso.

Un ejemplo de la interfaz fluida en acción sería:

public class CustomerBuilder { String surname; String firstName; String ssn; public static CustomerBuilder customer() { return new CustomerBuilder(); } public CustomerBuilder withSurname(String surname) { this.surname = surname; return this; } public CustomerBuilder withFirstName(String firstName) { this.firstName = firstName; return this; } public CustomerBuilder withSsn(String ssn) { this.ssn = ssn; return this; } // client doesn''t get to instantiate Customer directly public Customer build() { return new Customer(this); } } public class Customer { private final String firstName; private final String surname; private final String ssn; Customer(CustomerBuilder builder) { if (builder.firstName == null) throw new NullPointerException("firstName"); if (builder.surname == null) throw new NullPointerException("surname"); if (builder.ssn == null) throw new NullPointerException("ssn"); this.firstName = builder.firstName; this.surname = builder.surname; this.ssn = builder.ssn; } public String getFirstName() { return firstName; } public String getSurname() { return surname; } public String getSsn() { return ssn; } } import static com.acme.CustomerBuilder.customer; public class Client { public void doSomething() { Customer customer = customer() .withSurname("Smith") .withFirstName("Fred") .withSsn("123XS1") .build(); } }


El estilo cuenta mucho, y me parece que si hay un constructor con más de 20 argumentos, entonces el diseño debe ser alterado. Proporcione los valores predeterminados razonables.


En tu caso, quédate con el constructor. La información pertenece al Cliente y 4 campos están bien.

En el caso de que tenga muchos campos obligatorios y opcionales, el constructor no es la mejor solución. Como dijo @boojiboy, es difícil de leer y también es difícil escribir código de cliente.

@contagious sugirió usar el patrón predeterminado y los setters para atributs opcionales. Eso exige que los campos sean mutables, pero ese es un problema menor.

Joshua Block en Effective Java 2 dice que en este caso deberías considerar un constructor. Un ejemplo tomado del libro:

public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { // required parameters private final int servingSize; private final int servings; // optional parameters private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; } public Builder fat(int val) { fat = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; soduim = builder.sodium; carbohydrate = builder.carbohydrate; } }

Y luego úsalo así:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). calories(100).sodium(35).carbohydrate(27).build();

El ejemplo anterior fue tomado de Effective Java 2

Y eso no solo se aplica al constructor. Citando a Kent Beck en los patrones de implementación :

setOuterBounds(x, y, width, height); setInnerBounds(x + 2, y + 2, width - 4, height - 4);

Hacer que el rectángulo sea explícito como un objeto explica mejor el código:

setOuterBounds(bounds); setInnerBounds(bounds.expand(-2));


Encapsularía campos similares en un objeto propio con su propia lógica de construcción / validación.

Digamos, por ejemplo, si tienes

  • Teléfono de negocios
  • Dirección de Negocios
  • Teléfono de casa
  • Direccion de casa

Haría una clase que almacena el teléfono y la dirección junto con una etiqueta que especifica si es un teléfono / dirección de "casa" o "de negocios". Y luego reduzca los 4 campos a simplemente una matriz.

ContactInfo cinfos = new ContactInfo[] { new ContactInfo("home", "+123456789", "123 ABC Avenue"), new ContactInfo("biz", "+987654321", "789 ZYX Avenue") }; Customer c = new Customer("john", "doe", cinfos);

Eso debería hacer que se vea menos como espagueti.

Seguramente si tienes muchos campos, debe haber algún patrón que puedas extraer y que sea una buena unidad de función propia. Y crea un código más legible también.

Y las siguientes también son posibles soluciones:

  • Extiende la lógica de validación en lugar de almacenarla en una sola clase. Validar cuando el usuario los ingresa y luego validar de nuevo en la capa de la base de datos, etc.
  • Hacer una clase de CustomerFactory que me ayudaría a construir Customer s
  • La solución de @marcio también es interesante ...

Estoy de acuerdo con el límite de 7 elementos que Boojiboy menciona. Más allá de eso, puede valer la pena mirar tipos anónimos (o especializados), IDictionary o indirección a través de la clave principal a otra fuente de datos.


Si tiene desagradables muchos argumentos, simplemente empaquételos en estructuras / clases POD, preferiblemente declaradas como clases internas de la clase que está construyendo. De esta forma, aún puede requerir los campos mientras hace que el código que llama al constructor sea razonablemente legible.


Solo usa argumentos predeterminados. En un lenguaje que admita argumentos de métodos por defecto (PHP, por ejemplo), puede hacer esto en la firma del método:

public function doSomethingWith($this = val1, $this = val2, $this = val3)

Existen otras formas de crear valores predeterminados, como en los idiomas que admiten la sobrecarga de métodos.

Por supuesto, también puede establecer valores predeterminados cuando declare los campos, si lo considera apropiado.

En realidad, todo se reduce a si es apropiado que establezca estos valores predeterminados, o si sus objetos deben especificarse en la construcción todo el tiempo. Esa es realmente una decisión que solo tú puedes tomar.


Steve Mcconnell escribe en Code Complete que la gente tiene problemas para guardar más 7 cosas en su cabeza a la vez, así que ese sería el número con el que trataría de mantenerme.


Veo que algunas personas recomiendan siete como límite superior. Aparentemente no es cierto que la gente pueda tener siete cosas en su cabeza a la vez; solo pueden recordar cuatro (Susan Weinschenk, 100 cosas que todo diseñador debe saber sobre las personas , 48). Aun así, considero que cuatro son una especie de órbita terrestre alta. Pero eso es porque mi pensamiento ha sido alterado por Bob Martin.

En Clean Code , el tío Bob argumenta a favor de tres como un límite superior general para el número de parámetros. Él hace la afirmación radical (40):

El número ideal de argumentos para una función es cero (niladic). Luego viene uno (monádico) seguido de cerca por dos (diádica). Tres argumentos (triádicos) deben evitarse siempre que sea posible. Más de tres (polyadic) requieren una justificación muy especial, y luego no deben usarse de todos modos.

Él dice esto debido a la legibilidad; pero también debido a la capacidad de prueba:

Imagine la dificultad de escribir todos los casos de prueba para asegurarse de que todas las diversas combinaciones de argumentos funcionen correctamente.

Te animo a que encuentres una copia de su libro y leas su discusión completa de los argumentos de la función (40-43).

Estoy de acuerdo con aquellos que han mencionado el Principio de Responsabilidad Individual. Es difícil para mí creer que una clase que necesita más de dos o tres valores / objetos sin valores predeterminados razonables realmente tiene solo una responsabilidad, y no estaría mejor con otra clase extraída.

Ahora, si está inyectando sus dependencias a través del constructor, los argumentos de Bob Martin sobre lo fácil que es invocar al constructor no se aplican tanto (porque normalmente solo hay un punto en su aplicación donde lo conecta, o incluso tener un marco que lo haga por ti). Sin embargo, el Principio de Responsabilidad Individual sigue siendo relevante: una vez que una clase tiene cuatro dependencias, considero que es un olor que está haciendo una gran cantidad de trabajo.

Sin embargo, como en todo lo relacionado con la informática, existen casos válidos para tener una gran cantidad de parámetros de construcción. No distorsione su código para evitar el uso de una gran cantidad de parámetros; pero si usa una gran cantidad de parámetros, deténgase y reflexione, porque puede significar que su código ya está contorsionado.