¿Cuál es la mejor manera de refactorizar un método que tiene demasiados (6+) parámetros?

102

De vez en cuando me encuentro con métodos con un número incómodo de parámetros. La mayoría de las veces, parecen ser constructores. Parece que debería haber una forma mejor, pero no puedo ver cuál es.

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

He pensado en usar estructuras para representar la lista de parámetros, pero eso parece cambiar el problema de un lugar a otro y crear otro tipo en el proceso.

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

Entonces eso no parece una mejora. Entonces, ¿cuál es el mejor enfoque?

recursivo
fuente
Dijiste "estructura". Ese término tiene diferentes connotaciones en diferentes lenguajes de programación. ¿Qué pretendes que signifique?
Jay Bazuzi
1
Si está buscando un idioma en particular para eliminar la ambigüedad, elija C #. Pero básicamente, solo una simple bolsa de propiedades. Tiene diferentes propiedades nombradas con diferentes tipos. Podría definirse como una clase, tabla hash, estructura o lo que sea.
recursivo
Este artículo tiene una buena perspectiva sobre el tema. Específico de Javascript, pero los principios se pueden volver a aplicar a otros lenguajes.
lala

Respuestas:

94

La mejor manera sería encontrar formas de agrupar los argumentos. Esto supone, y realmente solo funciona si, terminará con múltiples "agrupaciones" de argumentos.

Por ejemplo, si está pasando la especificación para un rectángulo, puede pasar x, y, ancho y alto o simplemente puede pasar un objeto rectangular que contenga x, y, ancho y alto.

Busque cosas como esta al refactorizar para limpiarlo un poco. Si los argumentos realmente no se pueden combinar, comience a ver si tiene una violación del principio de responsabilidad única.

Matthew Brubaker
fuente
4
Buena idea pero mal ejemplo; el constructor del Rectángulo debería tener 4 argumentos. Esto tendría más sentido si el método esperara 2 conjuntos de coordenadas / dimensiones de rectángulo. Entonces podrías pasar 2 rectángulos en lugar de x1, x2, y1, y2 ...
Programador fuera de la ley
2
Lo suficientemente justo. Como dije, solo tiene sentido hacerlo si terminas con múltiples agrupaciones lógicas.
Matthew Brubaker
23
+1: A Single Responsibility, es uno de los pocos comentarios en todas las respuestas que realmente está abordando el verdadero problema. Qué objeto realmente necesita 7 valores independientes para formar su identidad.
AnthonyWJones
6
@AnthonyWJones No estoy de acuerdo. Los datos de las condiciones meteorológicas actuales pueden tener muchos más valores independientes para formar su identidad.
funct7
107

Voy a asumir que te refieres a C # . Algunas de estas cosas también se aplican a otros idiomas.

Tienes varias opciones:

cambiar de constructor a establecedores de propiedades . Esto puede hacer que el código sea más legible, porque es obvio para el lector qué valor corresponde a qué parámetros. La sintaxis de Object Initializer hace que esto se vea bien. También es fácil de implementar, ya que puede usar propiedades generadas automáticamente y omitir la escritura de los constructores.

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

Sin embargo, pierde inmutabilidad y pierde la capacidad de asegurarse de que se establezcan los valores requeridos antes de usar el objeto en tiempo de compilación.

Patrón de constructor .

Piense en la relación entre stringy StringBuilder. Puede obtener esto para sus propias clases. Me gusta implementarlo como una clase anidada, por lo que la clase Ctiene una clase relacionada C.Builder. También me gusta una interfaz fluida en el constructor. Si se hace bien, puede obtener una sintaxis como esta:

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

Tengo un script de PowerShell que me permite generar el código del constructor para hacer todo esto, donde la entrada se ve así:

class C {
    field I X
    field string Y
}

Entonces puedo generar en tiempo de compilación. partiallas clases me permiten extender tanto la clase principal como el constructor sin modificar el código generado.

Refactorización "Introducir objeto de parámetro" . Consulte el catálogo de refactorización . La idea es que tome algunos de los parámetros que está pasando y los ponga en un nuevo tipo, y luego pase una instancia de ese tipo. Si hace esto sin pensar, terminará de nuevo donde comenzó:

new C(a, b, c, d);

se convierte en

new C(new D(a, b, c, d));

Sin embargo, este enfoque tiene el mayor potencial para tener un impacto positivo en su código. Entonces, continúe siguiendo estos pasos:

  1. Busque subconjuntos de parámetros que tengan sentido juntos. El simple hecho de agrupar sin pensar todos los parámetros de una función no significa mucho; el objetivo es tener agrupaciones que tengan sentido. Sabrá que lo hizo bien cuando el nombre del nuevo tipo sea obvio.

  2. Busque otros lugares donde estos valores se usen juntos y use el nuevo tipo allí también. Lo más probable es que, cuando haya encontrado un buen tipo nuevo para un conjunto de valores que ya usa en todas partes, ese nuevo tipo también tendrá sentido en todos esos lugares.

  3. Busque la funcionalidad que está en el código existente, pero pertenece al nuevo tipo.

Por ejemplo, tal vez vea un código que se parece a:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

Usted podría tomar las minSpeedy los maxSpeedparámetros y los puso en un nuevo tipo:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

Esto es mejor, pero para aprovechar realmente el nuevo tipo, mueva las comparaciones al nuevo tipo:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

Y ahora estamos llegando a algo: la implementación de SpeedIsAcceptable()now dice lo que quieres decir, y tienes una clase útil y reutilizable. (El siguiente paso obvio es hacer que SpeedRangeen a Range<Speed>.)

Como puede ver, Introducir objeto de parámetro fue un buen comienzo, pero su valor real fue que nos ayudó a descubrir un tipo útil que faltaba en nuestro modelo.

Jay Bazuzi
fuente
4
Sugeriría probar primero "Introducir objeto de parámetro", y solo recurrir a las otras opciones si no puede encontrar un buen objeto de parámetro para crear.
Douglas Leeder
4
excelente respuesta. si ha mencionado la explicación de la refactorización antes de los azúcares sintácticos de c #, esto habría sido votado más alto en mi humilde opinión.
rpattabi
10
¡Oh! +1 para "Sabrá que lo hizo bien cuando el nombre del nuevo tipo sea obvio".
Sean McMillan
20

Si es un constructor, especialmente si hay múltiples variantes sobrecargadas, debería mirar el patrón Builder:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

Si es un método normal, debe pensar en las relaciones entre los valores que se pasan y quizás crear un objeto de transferencia.

kdgregory
fuente
Excelente respuesta. Quizás incluso más relevante que la respuesta de "poner los parámetros en una clase" que dieron todos (incluido yo).
Wouter Lievens
1
Probablemente sea una mala idea hacer que su clase sea mutable solo para evitar pasar demasiados parámetros al constructor.
Programador fuera de la ley
@outlaw: si la mutabilidad es un problema, puede implementar fácilmente la semántica de "ejecutar una vez". Sin embargo, una gran cantidad de parámetros de ctor a menudo indica la necesidad de configuración (o, como otros han señalado, una clase que intenta hacer demasiadas cosas). (cont)
kdgregory
Si bien puede externalizar la configuración, en muchos casos eso es innecesario, especialmente si está impulsado por el estado del programa o es estándar para un programa determinado (piense en analizadores XML, que pueden reconocer el espacio de nombres, validar con diferentes herramientas, etc.).
kdgregory
Me gusta el patrón del constructor, pero separo mis tipos de constructor inmutables y mutables, como string / StringBuilder, pero uso clases anidadas: Foo / Foo.Builder. Tengo un script de PowerShell para generar el código para hacer esto para clases de datos simples.
Jay Bazuzi
10

La respuesta clásica a esto es usar una clase para encapsular algunos o todos los parámetros. En teoría, eso suena genial, pero yo soy el tipo de persona que crea clases para conceptos que tienen significado en el dominio, por lo que no siempre es fácil aplicar este consejo.

Por ejemplo, en lugar de:

driver.connect(host, user, pass)

Podrías usar

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV

Wouter Lievens
fuente
5
Definitivamente me gustaría más la primera pieza de código. Estoy de acuerdo en que hay un cierto límite, por encima del cual el número r de parámetros se vuelve feo, pero para mi gusto, 3 sería aceptable.
blabla999
10

Esto se cita del libro de Fowler y Beck: "Refactoring"

Lista larga de parámetros

En nuestros primeros días de programación, nos enseñaron a pasar como parámetros todo lo que necesita una rutina. Esto era comprensible porque la alternativa eran los datos globales, y los datos globales son malos y usualmente dolorosos. Los objetos cambian esta situación porque si no tienes algo que necesitas, siempre puedes pedirle a otro objeto que te lo consiga. Por lo tanto, con los objetos no se pasa todo lo que necesita el método; en su lugar, pasa lo suficiente para que el método pueda llegar a todo lo que necesita. Mucho de lo que necesita un método está disponible en la clase de host del método. En los programas orientados a objetos, las listas de parámetros tienden a ser mucho más pequeñas que en los programas tradicionales. Esto es bueno porque las listas de parámetros largas son difíciles de entender, porque se vuelven inconsistentes y difíciles de usar. y porque los está cambiando para siempre a medida que necesita más datos. La mayoría de los cambios se eliminan al pasar objetos porque es mucho más probable que necesite realizar solo un par de solicitudes para obtener un nuevo dato. Use Reemplazar parámetro con método cuando pueda obtener los datos en un parámetro haciendo una solicitud de un objeto que ya conoce. Este objeto puede ser un campo o puede ser otro parámetro. Use Preserve Whole Object para tomar un montón de datos extraídos de un objeto y reemplazarlo con el objeto en sí. Si tiene varios elementos de datos sin un objeto lógico, utilice Introducir objeto de parámetro. Hay una excepción importante para realizar estos cambios. Esto es cuando explícitamente no desea crear una dependencia del objeto llamado al objeto más grande. En esos casos, es razonable desempaquetar datos y enviarlos como parámetros, pero preste atención al dolor que esto implica. Si la lista de parámetros es demasiado larga o cambia con demasiada frecuencia, debe reconsiderar su estructura de dependencia.

Youssef
fuente
7

No quiero sonar como un sabio, pero también debe verificar para asegurarse de que los datos que está pasando realmente se transmitan: Pasar cosas a un constructor (o método para el caso) huele un poco a poco énfasis en el comportamiento de un objeto.

No me malinterpretes: los métodos y los constructores a veces tendrán muchos parámetros. Pero cuando se encuentre, intente considerar encapsular datos con comportamiento lugar.

Este tipo de olor (ya que estamos hablando de refactorización, esta horrible palabra parece apropiada ...) también podría detectarse para objetos que tienen muchas (léase: cualquiera) propiedades o getters / setters.

Daren Thomas
fuente
7

Cuando veo listas de parámetros largas, mi primera pregunta es si esta función u objeto está haciendo demasiado. Considerar:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

Por supuesto, este ejemplo es deliberadamente ridículo, pero he visto muchos programas reales con ejemplos solo un poco menos ridículos, donde una clase se usa para contener muchas cosas apenas relacionadas o no relacionadas, aparentemente solo porque el mismo programa de llamada necesita ambos o porque el El programador pensó en ambos al mismo tiempo. A veces, la solución fácil es simplemente dividir la clase en varias partes, cada una de las cuales hace lo suyo.

Un poco más complicado es cuando una clase realmente necesita lidiar con múltiples cosas lógicas, como tanto el pedido de un cliente como la información general sobre el cliente. En estos casos, cree una clase para el cliente y una clase para el pedido, y permítales hablar entre ellos según sea necesario. Entonces en lugar de:

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

Nosotros podríamos tener:

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

Aunque, por supuesto, prefiero las funciones que toman solo 1, 2 o 3 parámetros, a veces tenemos que aceptar que, de manera realista, esta función requiere mucho y que el número en sí mismo no crea complejidad. Por ejemplo:

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

Sí, es un montón de campos, pero probablemente todo lo que vamos a hacer con ellos es guardarlos en un registro de base de datos o lanzarlos en una pantalla o algo así. Realmente no hay mucho procesamiento aquí.

Cuando mis listas de parámetros se hacen largas, prefiero si puedo dar a los campos diferentes tipos de datos. Como cuando veo una función como:

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

Y luego lo veo llamado con:

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

Me preocupo. Mirando la llamada, no está del todo claro qué significan todos estos números, códigos y banderas crípticos. Esto es solo pedir errores. Un programador puede confundirse fácilmente sobre el orden de los parámetros y accidentalmente cambiar dos, y si son del mismo tipo de datos, el compilador simplemente los aceptará. Preferiría tener una firma donde todas estas cosas sean enumeraciones, por lo que una llamada pasa en cosas como Type.ACTIVE en lugar de "A" y CreditWatch.NO en lugar de "false", etc.

Arrendajo
fuente
5

Si algunos de los parámetros del constructor son opcionales, tiene sentido usar un constructor, que obtendría los parámetros requeridos en el constructor, y tendría métodos para los opcionales, devolviendo el constructor, para usarse así:

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

Los detalles de esto se describen en Effective Java, 2nd Ed., P. 11. Para los argumentos del método, el mismo libro (p. 189) describe tres enfoques para acortar las listas de parámetros:

  • Divida el método en varios métodos que requieran menos argumentos
  • Cree clases de miembros auxiliares estáticos para representar grupos de parámetros, es decir, pasar a en DinoDonkeylugar de dinoydonkey
  • Si los parámetros son opcionales, el constructor anterior se puede adoptar para los métodos, definiendo un objeto para todos los parámetros, estableciendo los requeridos y luego llamando a algún método de ejecución en él.
Fabian Steeg
fuente
4

Usaría el constructor predeterminado y los establecedores de propiedades. C # 3.0 tiene una buena sintaxis para hacer esto automágicamente.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

La mejora del código viene al simplificar el constructor y no tener que admitir múltiples métodos para admitir varias combinaciones. La sintaxis de "llamar" sigue siendo un poco "prolija", pero en realidad no es peor que llamar a los establecedores de propiedades manualmente.

tvanfosson
fuente
2
Esto permitiría que exista un objeto t new Shniz (). Una buena implementación de OO buscaría minimizar la posibilidad de que los objetos existan en estado incompleto.
AnthonyWJones
En general, cualquier idioma con una sintaxis nativa de hash / diccionario viene con un sustituto adecuado para los parámetros con nombre (que son geniales y, a menudo, lo que estas situaciones requieren, pero por alguna razón el único idioma popular que los admite es el peor del planeta). .
caos
4

No ha proporcionado suficiente información para garantizar una buena respuesta. Una lista larga de parámetros no es intrínsecamente mala.

Shniz (foo, bar, baz, quux, fred, wilma, barney, dino, burro)

podría interpretarse como:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

En este caso, es mucho mejor crear una clase para encapsular los parámetros porque le da significado a los diferentes parámetros de una manera que el compilador puede verificar y también hace que el código sea más fácil de leer visualmente. También hace que sea más fácil de leer y refactorizar más adelante.

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

Alternativamente, si tuvieras:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

Este es un caso muy diferente porque todos los objetos son diferentes (y no es probable que se confundan). Estuvo de acuerdo en que si todos los objetos son necesarios y todos son diferentes, tiene poco sentido crear una clase de parámetro.

Además, ¿algunos parámetros son opcionales? ¿Hay anulaciones de métodos (el mismo nombre de método, pero diferentes firmas de método)? Todos estos tipos de detalles son importantes en cuanto a cuál es la mejor respuesta.

* Una bolsa de propiedades también puede ser útil, pero no específicamente mejor dado que no se proporcionan antecedentes.

Como puede ver, hay más de una respuesta correcta a esta pregunta. Elige tu opción.

Robert Paulson
fuente
3

Puede intentar agrupar su parámetro en múltiples estructuras / clases significativas (si es posible).

Julien Hoarau
fuente
2

En general, me inclinaría hacia el enfoque de estructuras; presumiblemente, la mayoría de estos parámetros están relacionados de alguna manera y representan el estado de algún elemento que es relevante para su método.

Si el conjunto de parámetros no se puede convertir en un objeto significativo, probablemente sea una señal de que se Shnizestá haciendo demasiado, y la refactorización debería implicar dividir el método en preocupaciones separadas.

Andrzej Doyle
fuente
2

Puede cambiar la complejidad por líneas de código fuente. Si el método en sí hace demasiado (cuchillo suizo), intente reducir a la mitad sus tareas creando otro método. Si el método es simple solo que necesita demasiados parámetros, entonces los llamados objetos de parámetro son el camino a seguir.

Karl
fuente
2

Si su idioma lo admite, utilice parámetros con nombre y haga tantos opcionales (con valores predeterminados razonables) como sea posible.

user54650
fuente
1

Creo que el método que describiste es el camino a seguir. Cuando encuentro un método con muchos parámetros y / o uno que probablemente necesite más en el futuro, generalmente creo un objeto ShnizParams para pasar, como usted describe.

Sopa instantánea
fuente
1

¿Qué tal no configurarlo todo a la vez en los constructores sino hacerlo a través de propiedades / establecedores ? He visto algunas clases de .NET que utilizan este enfoque, comoProcess class:

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();
Gant
fuente
3
C # 3 en realidad tiene una sintaxis para hacer esto fácilmente: inicializadores de objetos.
Daren Thomas
1

Estoy de acuerdo con el enfoque de mover los parámetros a un objeto de parámetro (estructura). Sin embargo, en lugar de simplemente pegarlos todos en un objeto, revise si otras funciones usan grupos similares de parámetros. Un objeto de parámetro es más valioso si se usa con múltiples funciones en las que espera que ese conjunto de parámetros cambie consistentemente en esas funciones. Es posible que solo coloque algunos de los parámetros en el nuevo objeto de parámetro.

Frank Schwieterman
fuente
1

Si tiene tantos parámetros, es probable que el método esté haciendo demasiado, así que aborde esto primero dividiendo el método en varios métodos más pequeños. Si aún tiene demasiados parámetros después de esto, intente agrupar los argumentos o convertir algunos de los parámetros en miembros de instancia.

Prefiera clases / métodos pequeños sobre grandes. Recuerde el principio de responsabilidad única.

Brian Rasmussen
fuente
El problema con los miembros y las propiedades de la instancia es que 1) deben poder escribirse, 2) es posible que no se establezcan. En el caso de un constructor, hay ciertos campos que quiero asegurarme de que se llenen antes de que se permita que exista una instancia.
recursivo
@recursive - No estoy de acuerdo con que los campos / propiedades siempre tengan que ser escribibles. Para las clases pequeñas, hay muchas ocasiones en las que los miembros de solo lectura tienen sentido.
Brian Rasmussen
1

Los argumentos con nombre son una buena opción (suponiendo un lenguaje que los admita) para eliminar la ambigüedad de listas de parámetros largas (¡o incluso cortas!) Y al mismo tiempo permitir (en el caso de los constructores) que las propiedades de la clase sean inmutables sin imponer un requisito para permitir que exista. en un estado parcialmente construido.

La otra opción que buscaría al hacer este tipo de refactorización serían grupos de parámetros relacionados que podrían manejarse mejor como un objeto independiente. Usando la clase Rectangle de una respuesta anterior como ejemplo, el constructor que toma parámetros para x, y, alto y ancho podría factorizar xey en un objeto Point, lo que le permite pasar tres parámetros al constructor de Rectangle. O vaya un poco más allá y conviértalo en dos parámetros (UpperLeftPoint, LowerRightPoint), pero eso sería una refactorización más radical.

Dave Sherohman
fuente
0

Depende del tipo de argumentos que tenga, pero si son muchos valores / opciones booleanos, ¿tal vez podría usar un indicador de enumeración?

Scottm
fuente
0

Creo que ese problema está profundamente ligado al dominio del problema que estás tratando de resolver con la clase.

En algunos casos, un constructor de 7 parámetros puede indicar una jerarquía de clases incorrecta: en ese caso, la estructura / clase auxiliar sugerida anteriormente suele ser un buen enfoque, pero también tiende a terminar con un montón de estructuras que son solo bolsas de propiedades y no hagas nada útil. El constructor de 8 argumentos también puede indicar que su clase es demasiado genérica / demasiado polivalente, por lo que necesita muchas opciones para ser realmente útil. En ese caso, puede refactorizar la clase o implementar constructores estáticos que oculten los constructores complejos reales: por ejemplo. Shniz.NewBaz (foo, bar) podría realmente llamar al constructor real pasando los parámetros correctos.

axel_c
fuente
0

Una consideración es ¿cuál de los valores sería de solo lectura una vez que se crea el objeto?

Las propiedades de escritura pública tal vez podrían asignarse después de la construcción.

¿De dónde vienen en última instancia los valores? Quizás algunos valores son verdaderamente externos, mientras que otros son realmente de alguna configuración o datos globales que mantiene la biblioteca.

En este caso, podría ocultar el constructor del uso externo y proporcionarle una función Create. La función de creación toma los valores verdaderamente externos y construye el objeto, luego usa los accesos que solo están disponibles en la biblioteca para completar la creación del objeto.

Sería realmente extraño tener un objeto que requiera 7 o más parámetros para darle al objeto un estado completo y que todos sean realmente externos por naturaleza.

AnthonyWJones
fuente
0

Cuando una clase tiene un constructor que acepta demasiados argumentos, suele ser una señal de que tiene demasiadas responsabilidades. Probablemente se pueda dividir en clases separadas que cooperen para brindar las mismas funcionalidades.

En caso de que realmente necesite tantos argumentos para un constructor, el patrón Builder puede ayudarlo. El objetivo es seguir pasando todos los argumentos al constructor, por lo que su estado se inicializa desde el principio y aún puede hacer que la clase sea inmutable si es necesario.

Vea abajo :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
Guillaume
fuente