Pasar todo el objeto frente a pasar la propiedad del objeto

7 minutos de lectura

Avatar de usuario de Lucas
Lucas

Estoy pensando en la solución para mi aplicación. Esta es la situación: tengo una clase con un método que toma ObjectA como parámetro de entrada y llama a varios métodos pequeños. Cada uno de estos métodos necesita algunas partes del ObjectA (no se superponen, es decir, method1() necesidades ObjectA.field1 y ObjectA.field2, method2() necesidades ObjectA.field3 etcétera…)

Dadas las buenas prácticas generales de código y el rendimiento, ¿es mejor pasar ObjectA a cada uno de estos métodos para que puedan extraer el valor que necesitan por su cuenta o es mejor simplemente pasarles valores? Quiero decir:

method1(ObjectA);
method2(ObjectA);

o

method1(Object1.getField1(), ObjectA.getField2());
method2(ObjectA.getField3());

  • Creo que la primera opción es mejor … El mundo exterior no necesita saber qué está haciendo su función internamente con el objeto … (es decir, a qué campos está accediendo).

    – La mente perdida

    15/01/2014 a las 11:30


  • Es totalmente en sus necesidades. Si no desea que el otro método modifique los campos de su objeto, no pase la referencia del objeto. Simplemente pase los valores requeridos. De lo contrario, es mejor pasar la referencia del objeto y usar los valores de él cuando sea necesario en lugar de agregar tantos parámetros en la llamada al método.

    – Raúl

    15 de enero de 2014 a las 11:31

  • Es una buena práctica hacer que el código sea lo más simple de entender y claro posible. En este caso, sospecho que no hay diferencia de rendimiento o es demasiado pequeña para medirla.

    – Peter Lawrey

    15 de enero de 2014 a las 11:37

avatar de usuario de christopher
cristóbal

Tenga en cuenta que, con su código, en realidad no está aprobando ObjectA. Es decir, está pasando el tipo de referencia a ObjectApor lo que en una nota de rendimiento la diferencia entre pasar un String referencia de objeto y ObjectA la referencia al objeto sería insignificante.

La forma en que lo escribiría

Pasaría todo el objeto, si el método es pertinente para la clase. Mi razonamiento para esto es dividir el conocimiento de la clase tanto como sea posible. Lo que quiero decir con esto es lo siguiente.

public void doSomethingRelatedToTheClass(String param)
{
    // Do something with param.
}

Mi primera crítica aquí es que este método asume que la entrada es el campo correcto. Mi segundo, es que ahora, la clase que llama a este código necesita saber un poco más sobre este método, porque tiene que llamarlo así:

doSomethingRelatedToTheClass(myObject.getValue());

Y lo que esto significa es que, si encuentras que otro miembro de ObjectA funciona mejor dentro de este método, o desea acceder a otros miembros de ObjectAy tu cambias doSomething() para reflejar este cambio, también debe cambiar la llamada al método, para:

doSomethingRelatedToTheClass(myObject.getOtherValue(), myObject.getValue());

Entonces, al pasar el objeto completo, abstrae ese detalle y el método puede manejarlo; a saber:

doSomethingRelatedToTheClass(myObject); // Doesn't need to know what you do with it.

public void doSomethingRelatedToTheClass(ObjectA object)
{
    String val = object.getValue();

    String otherVal = object.getOtherValue();
}

Cuando un cambio en una clase resulta en un cambio en otras clases, esto es un Anti-patrón llamado Cirugía de escopeta.

Editar

Tuve la oportunidad de revisar mi respuesta aquí y modifiqué ligeramente mi respuesta original porque creo que no es la mejor solución para todo situaciones Como antes, si un método está relacionado específicamente con una clase, entonces la creación de instancias de esa clase (o más preferiblemente, su superclase o interfaz implementada)[s]) debe ser el parámetro.

El momento en que este no es el caso es cuando la funcionalidad puede ser genérica. Un ejemplo de una función genérica podría ser:

public String[] findNouns(String sentence);

En este caso, encontrar los sustantivos en una oración podría ser apropiado para muchos casos de uso, y no solo para los casos de uso que ha definido. Como tal, pasar el valor es el único enfoque sensato porque, de lo contrario, junta dos piezas de lógica que no tienen una relación directa. El hallazgo de sustantivos y el objeto arbitrario que ha definido.

En resumen

  • Si el método es una lógica relacionada con el objeto, pase el objeto

  • Si el método no tiene nada que ver con el objeto, y el objeto solo lo usa como una función de utilidad, entonces pase el valor y nombre la función de forma genérica.

  • Un problema que puede surgir es que al pasar todo el ObjectA a la definición de la función, cada función de prueba que llama a la API necesita instanciar un Fake ObjectA con todos los campos, incluso si la función solo usa un subconjunto de esos campos. Luego, si agrega un campo en el ObjectA class, deberá cambiar todos estos objetos falsos incluso si la implementación realmente no ha cambiado. Esto se puede evitar usando fábricas para stubs, pero requiere aún más código.

    –Luca Molteni

    7 de marzo de 2016 a las 9:29


  • otra cosa es que en mi proyecto actual veo mucho código heredado que pasa demasiados datos. Si se necesita una propiedad, y está pasando un objeto completo en caso de que necesite otra propiedad algún día… YAGNI

    – kiedysktos

    5 de enero de 2018 a las 10:11


  • Bueno, ¿esa función (que supongo que vive fuera de la clase) hace algo exclusivamente para esa clase? ¿O es una funcionalidad genérica? ¿Podría ser una funcionalidad genérica?

    – cristobal

    5 de enero de 2018 a las 13:15

  • Otra opción que encuentro una mejora de la referencia del objeto es crear un tipo para él, como un objeto de valor simple con solo los atributos que necesita. Entonces la función sabe lo suficiente y ya no depende del objeto principal.

    – Damián Roche

    1 de agosto de 2022 a las 10:56


avatar de usuario de n4rzul
n4rzul

Examinemos un escenario. Ahora bien, este puede o no ser su escenario, pero ilustra un punto.

Digamos que field1 y field2 en su caso son dos números enteros y method1 los suma y devuelve el resultado.

Si pasa los objetos, ese método solo puede sumar esos dos campos. El método ahora también está fuertemente acoplado con esos objetos.

Por otro lado, si pasa solo los campos, los dos enteros en este caso su método se vuelve más genérico. Ahora puede sumar 2 enteros arbitrarios sin importar en qué objetos se encuentren.

Sin embargo, en general, siempre exponga la menor cantidad posible de sus objetos a otros métodos y clases. Esto promueve el acoplamiento flojo.

Excepciones

AS maaartinus señala que si, por ejemplo, campo1 y campo2 fueran Puntos y el método1 calculara la distancia entre esos dos puntos, entonces tendría que estar de acuerdo en que pasar dos Puntos sería mejor que pasar 2 pares de enteros xy (4 parámetros)

Espero que esto ayude

Yo diría que depende. Un método puede ser más claro y más general si opera sobre los argumentos en lugar de requerir un objeto completo. A veces tienes los argumentos listos (p. ej., x y y) y tendría que agregarlos primero en, por ejemplo, un Point para poder llamar al método. A veces tienes un objeto diferente no relacionado (por ejemplo, algunos ImmutablePointobviamente no se extiende java.awt.Point) y tendría que extraer las coordenadas y crear un objeto para pasar.

Por lo general, si el objeto pasado es la abstracción adecuada, pasarlo como un todo es el camino a seguir. No es una cuestión de rendimiento, se trata de legibilidad y mantenibilidad. Véase también el Ley de Deméter lo que puede conducir a una menor dependencia del objeto pasado.

Avatar de usuario de Nitin Pandey
Nitin Pandey

Como han dicho otros, depende, pero en mi experiencia, pasar objetos completos hace que el código sea más difícil de leer y mantener.

Consideremos este método getUserDetails(User user) que se basa en algunos métodos como getUserAddress(User user) getUserFamilyInfo(User user) etc., que además pueden conectarse a diferentes fuentes de datos para obtener la información.

No hay manera fácil de saber que getUserFamilyInfo solo necesita userId o necesita userId y lastName o algo más de user cuando se pasa todo el objeto. Hace que sea difícil comprender las dependencias entre diferentes servicios y hacer cualquier refactorización.

Prefiero pasar argumentos individuales si el conteo es inferior a 3 o crear un dto si se requieren varias propiedades de un objeto variable grande.

¿Ha sido útil esta solución?