A menudo me encuentro luchando por decidir cuál de estas dos formas usar cuando necesito usar datos comunes a través de algunos métodos en mis clases. ¿Cuál sería una mejor opción?
En esta opción, puedo crear una variable de instancia para evitar la necesidad de tener que declarar variables adicionales y también para evitar definir los parámetros del método, pero puede que no esté tan claro dónde se instancian / modifican esas variables:
public class MyClass {
private int var1;
MyClass(){
doSomething();
doSomethingElse();
doMoreStuff();
}
private void doSomething(){
var1 = 2;
}
private void doSomethingElse(){
int var2 = var1 + 1;
}
private void doMoreStuff(){
int var3 = var1 - 1;
}
}
¿O simplemente instanciar variables locales y pasarlas como argumentos?
public class MyClass {
MyClass(){
int var1 = doSomething();
doSomethingElse(var1);
doMoreStuff(var1);
}
private int doSomething(){
int var = 2;
return var;
}
private void doSomethingElse(int var){
int var2 = var + 1;
}
private void doMoreStuff(int var){
int var3 = var - 1;
}
}
Si la respuesta es que ambos son correctos, ¿cuál se ve / usa con más frecuencia? Además, si puede proporcionar ventajas / desventajas adicionales para cada opción, sería muy valioso.
java
coding-style
carlossierra
fuente
fuente
Respuestas:
Me sorprende que esto no haya mencionado aún ...
Depende de si
var1
es realmente parte del estado de su objeto .Asume que ambos enfoques son correctos y que es solo una cuestión de estilo. Está usted equivocado.
Esto es completamente acerca de cómo modelar adecuadamente.
Del mismo modo,
private
existen métodos de instancia para mutar el estado de su objeto . Si eso no es lo que está haciendo su método, entonces debería serloprivate static
.fuente
transient
no tiene nada que ver con esto, porquetransient
se trata de un estado persistente , como en partes del objeto que se guardan cuando haces algo como serializar el objeto. Por ejemplo, una tienda de respaldo de ArrayList estransient
a pesar de que es completamente crucial para el estado de ArrayList, porque cuando se serializa una ArrayList, solo desea guardar la parte de la tienda de respaldo que contiene elementos de ArrayList reales, no el espacio libre al final reservado para más adiciones de elementos.var1
es necesario para un par de métodos, pero no es parte del estadoMyClass
, podría ser el momento de ponervar1
esos métodos en otra clase para ser utilizadosMyClass
.No sé cuál es más frecuente, pero siempre haría lo último. Comunica más claramente el flujo de datos y la vida útil, y no llena cada instancia de su clase con un campo cuya única vida útil relevante es durante la inicialización. Diría que lo primero es confuso y hace que la revisión del código sea mucho más difícil, porque tengo que considerar la posibilidad de que cualquier método pueda modificarse
var1
.fuente
Debe reducir el alcance de sus variables tanto como sea posible (y razonable). No solo en métodos, sino en general.
Para su pregunta, eso significa que depende de si la variable es parte del estado del objeto. En caso afirmativo, está bien usarlo en ese ámbito, es decir, todo el objeto. En este caso ve con la primera opción. Si no, elija la segunda opción, ya que reduce la visibilidad de las variables y, por lo tanto, la complejidad general.
fuente
Hay otro estilo: use un contexto / estado.
Hay varios beneficios para este enfoque. Los objetos de estado pueden cambiar independientemente del objeto, por ejemplo, dando mucho margen de maniobra para el futuro.
Este es un patrón que también funciona bien en un sistema distribuido / servidor donde se deben preservar algunos detalles en las llamadas. Puede almacenar detalles de usuario, conexiones de base de datos, etc. en el
state
objeto.fuente
Se trata de efectos secundarios.
Al preguntar si
var1
es parte del estado se pierde el punto de esta pregunta. Claro que sivar1
debe persistir, tiene que ser una instancia. Cualquiera de los dos enfoques puede funcionar si se necesita persistencia o no.El enfoque de efectos secundarios
Algunas variables de instancia solo se utilizan para comunicarse entre métodos privados de llamada a llamada. Este tipo de variable de instancia se puede refactorizar fuera de existencia, pero no tiene que ser así. A veces las cosas son más claras con ellos. Pero esto no está exento de riesgos.
Está dejando una variable fuera de su alcance porque se usa en dos ámbitos privados diferentes. No porque sea necesario en el ámbito en el que lo está colocando. Esto puede ser confuso. ¡Los "globales son malos!" nivel de confusión Esto puede funcionar pero simplemente no escalará bien. Solo funciona en los pequeños. No hay grandes objetos. No largas cadenas de herencia. No provoques un efecto yo yo .
El enfoque funcional
Ahora, incluso si
var1
debe persistir, nada dice que debe usar if para cada valor transitorio que pueda tomar antes de que alcance el estado que desea conservar entre las llamadas públicas. Eso significa que aún puede establecer unavar1
instancia utilizando nada más que métodos más funcionales.Entonces, como parte del estado o no, aún puede usar cualquiera de los enfoques.
En estos ejemplos, 'var1' está tan encapsulado que nada más que su depurador sabe que existe. Supongo que lo hiciste deliberadamente porque no quieres sesgarnos. Afortunadamente no me importa cuál.
El riesgo de efectos secundarios.
Dicho esto, sé de dónde viene tu pregunta. He trabajado bajo desgraciada yo yo 'ing herencia que muta una variable de instancia en múltiples niveles en varios métodos y se han ido ardilla tratando de seguirlo. Este es el riesgo.
Este es el dolor que me lleva a un enfoque más funcional. Un método puede documentar sus dependencias y resultados en su firma. Este es un enfoque poderoso y claro. También le permite cambiar lo que pasa el método privado haciéndolo más reutilizable dentro de la clase.
La ventaja de los efectos secundarios
También es limitante. Las funciones puras no tienen efectos secundarios. Eso puede ser algo bueno, pero no está orientado a objetos. Una gran parte de la orientación a objetos es la capacidad de referirse a un contexto fuera del método. Hacer eso sin fugas de glóbulos por todas partes aquí y allá es la fuerza de OOP. Tengo la flexibilidad de un global pero está muy bien contenido en la clase. Puedo llamar a un método y mutar cada variable de instancia a la vez si lo deseo. Si hago eso, estoy obligado a dar al menos al método un nombre que aclare lo que está haciendo para que la gente no se sorprenda cuando eso suceda. Los comentarios también pueden ayudar. Algunas veces estos comentarios se formalizan como "condiciones de publicación".
La desventaja de los métodos privados funcionales
El enfoque funcional aclara algunas dependencias. A menos que esté en un lenguaje funcional puro, no puede descartar dependencias ocultas. No sabe, mirando solo una firma de métodos, que no le está ocultando un efecto secundario en el resto de su código. Simplemente no lo haces.
Publicaciones condicionales
Si usted y todos los demás miembros del equipo documentan de manera confiable los efectos secundarios (condiciones previas / posteriores) en los comentarios, la ganancia del enfoque funcional es mucho menor. Sí, lo sé, sigue soñando.
Conclusión
Personalmente, tiendo a métodos privados funcionales en cualquier caso si puedo, pero honestamente es principalmente porque esos comentarios de efectos secundarios condicionales previos / posteriores no causan errores de compilación cuando están desactualizados o cuando los métodos se llaman fuera de servicio. A menos que realmente necesite la flexibilidad de los efectos secundarios, preferiría saber que las cosas funcionan.
fuente
var1
como una variable de estado cambiando los paradigmas. El alcance de la clase NO es solo donde se almacena el estado. También es un alcance cerrado. Eso significa que hay dos posibles motivaciones para poner una variable a nivel de clase. Puede afirmar que hacerlo solo por el alcance que lo abarca y no por el estado es malo, pero yo digo que es una compensación con el arity, que también es malo. Sé esto porque he tenido que mantener el código que hace esto. Algunos se hicieron bien. Algo fue una pesadilla. La línea entre los dos no es estado. Es legibilidad.La primera variante parece no intuitiva y potencialmente peligrosa para mí (imagine por cualquier razón que alguien hace público su método privado).
Prefiero crear instancias de sus variables en la construcción de la clase, o pasarlas como argumentos. Este último le ofrece la opción de utilizar expresiones idiomáticas funcionales y no depender del estado del objeto que lo contiene.
fuente
Ya hay respuestas que hablan sobre el estado del objeto y cuándo se prefiere el segundo método. Solo quiero agregar un caso de uso común para el primer patrón.
El primer patrón es perfectamente válido cuando todo lo que hace su clase es que encapsula un algoritmo . Un caso de uso para esto es que si escribiera el algoritmo en un solo método, sería demasiado grande. Entonces lo divide en métodos más pequeños, lo convierte en una clase y hace que los submétodos sean privados.
Ahora, pasar todo el estado del algoritmo a través de parámetros puede ser tedioso, por lo que debe usar los campos privados. También está de acuerdo con la regla del primer párrafo porque es básicamente un estado de la instancia. Solo debe tener en cuenta y documentar adecuadamente que el algoritmo no es reentrante si utiliza campos privados para esto . Esto no debería ser un problema la mayor parte del tiempo, pero posiblemente puede morderte.
fuente
Probemos un ejemplo que hace algo. Perdóname ya que esto es JavaScript, no Java. El punto debería ser el mismo.
Visite https://blockly-games.appspot.com/pond-duck?lang=en , haga clic en la pestaña javascript y pegue esto:
Debes notar eso
dir
,wid
ydis
no se pasan mucho. También debe notar que el código en la función quelock()
devuelve se parece mucho al pseudocódigo. Bueno, es el código real. Sin embargo, es muy fácil de leer. Podríamos agregar pases y asignaciones, pero eso agregaría un desorden que nunca verías en un pseudocódigo.Si desea argumentar que no hacer la asignación y pasar está bien porque esos tres vars son de estado persistente, considere un rediseño que asigne
dir
un valor aleatorio a cada ciclo. No es persistente ahora, ¿verdad?Claro, ahora podríamos
dir
reducir el alcance ahora, pero no sin estar obligados a abarrotar nuestro pseudo código similar con pasar y configurar.Entonces, no, el estado no es la razón por la que decide usar o no usar los efectos secundarios en lugar de pasar y regresar. Tampoco los efectos secundarios solo significan que su código es ilegible. No obtienes los beneficios del código funcional puro . Pero bien hecho, con buenos nombres, en realidad pueden ser agradables de leer.
Eso no quiere decir que no puedan convertirse en un espagueti como una pesadilla. Pero entonces, ¿qué no puede?
Feliz caza de patos.
fuente