¿Por qué las variables estáticas se consideran malas?

635

Soy un programador de Java que es nuevo en el mundo corporativo. Recientemente desarrollé una aplicación usando Groovy y Java. Todo el código que escribí usaba una buena cantidad de estadísticas. El lote técnico superior me pidió que redujera la cantidad de estadísticas utilizadas. He buscado en Google casi lo mismo, y encuentro que muchos programadores están bastante en contra del uso de variables estáticas.

Encuentro que las variables estáticas son más convenientes de usar. Y supongo que también son eficientes (corríjame si me equivoco), porque si tuviera que hacer 10,000 llamadas a una función dentro de una clase, me encantaría hacer que el método sea estático y usarlo directamente Class.methodCall()en lugar de abarrotando la memoria con 10,000 instancias de la clase, ¿verdad?

Además, las estadísticas reducen las interdependencias en las otras partes del código. Pueden actuar como titulares perfectos del estado. Además de esto, encuentro que las estadísticas están ampliamente implementadas en algunos idiomas como Smalltalk y Scala . Entonces, ¿por qué esta opresión por la estática prevalece entre los programadores (especialmente en el mundo de Java)?

PD: corríjame si mis suposiciones sobre la estática son incorrectas.

Vamsi Emani
fuente
43
Solo por decirlo, no hay variables estáticas o métodos en Smalltalk o Scala, exactamente porque los métodos y variables estáticas están en contra de los principios OOP.
Maurício Linhares
87
Al menos una declaración que haces es bastante curiosa: "las estadísticas reducen las interdependencias en las otras partes del código". En general, aprietan las dependencias. El código donde se realiza la llamada está vinculado muy estrechamente al código llamado. Sin abstracción entre, dependencia directa.
Arne Deutsch
11
Buena pregunta ... más de un programador.SE Preguntas Tho?
WernerCD
26
Su segundo párrafo trata sobre un tema completamente diferente, a saber, los métodos estáticos .
Paul
8
La programación funcional también desaprueba el estado global también. Si alguna vez (y debería ) entrar en FP algún día, prepárese para deshacerse de la noción de estado global.
nuevo123456

Respuestas:

689

Las variables estáticas representan el estado global. Es difícil razonar y probar: si creo una nueva instancia de un objeto, puedo razonar sobre su nuevo estado dentro de las pruebas. Si uso código que usa variables estáticas, podría estar en cualquier estado, y cualquier cosa podría estar modificándolo.

Podría continuar durante bastante tiempo, pero el concepto más amplio en el que pensar es que cuanto más limitado sea el alcance de algo, más fácil será razonar. Somos buenos para pensar en cosas pequeñas, pero es difícil razonar sobre el estado de un sistema de un millón de líneas si no hay modularidad. Esto se aplica a todo tipo de cosas, por cierto, no solo a las variables estáticas.

Jon Skeet
fuente
57
Eso últimamente parece ser un argumento, ya sea que el código sea comprobable o no. Es un razonamiento bastante defectuoso. El argumento debe ser 'buen diseño', y generalmente un buen diseño es comprobable. Pero no al revés: "No puedo probarlo porque debe ser un mal diseño". Sin embargo, no me malinterpretes, estoy de acuerdo con tu publicación en general.
M Platvoet
144
@M Platvoet: Diría que dada la opción entre dos diseños igualmente válidos, el comprobable es superior. Ser comprobable ciertamente no equivale a estar bien diseñado, pero rara vez me he encontrado con buenos diseños no comprobables, y creo que son lo suficientemente raros como para no tener ningún problema en hacer de la comprobabilidad un indicador contribuyente de propósito general hacia un buen diseño.
Jon Skeet
99
@M Platvoet: la capacidad de prueba afecta tanto la mantenibilidad como la confiabilidad, y consideraría esos factores principales en la calidad del diseño. No son los únicos factores, sin duda, pero en mi humilde opinión, el costo de cualquier código dado es una combinación de ciclos de máquina, ciclos de desarrollador y ciclos de usuario. La capacidad de prueba golpea a dos de esos tres.
Justin Morgan
55
@M Platvoet: la capacidad de prueba también tiende a afectar la reutilización, ya que una clase desacoplada es generalmente más fácil de reutilizar.
TrueWill
13
M Platvoet - No estoy de acuerdo con tu primer comentario aquí. Creo que si algo no se puede probar, entonces ES un mal diseño; porque si no puedo probarlo, no puedo saber si funciona. ¿Compraría un automóvil si el vendedor le dijera "El diseño de este modelo evita que se pruebe, así que no sé si realmente funciona"? La capacidad de prueba es tan crucial para el software (como para los automóviles), que el diseño competente EXIGE que se incluya.
Dawood ibn Kareem
277

No está muy orientado a objetos: una razón por la cual algunas personas podrían considerar "estáticas" es que son contrarias al paradigma orientado a objetos . En particular, viola el principio de que los datos están encapsulados en objetos (que pueden extenderse, ocultar información, etc.). La estática, en la forma en que está describiendo su uso, es esencialmente usarlos como una variable global para evitar tratar problemas como el alcance. Sin embargo, las variables globales son una de las características definitorias del paradigma de programación de procedimiento o imperativo, no una característica del código orientado a objetos "bueno". Esto no quiere decir que el paradigma de procedimiento sea malo, pero tengo la impresión de que su supervisor espera que esté escribiendo "buen código orientado a objetos" y realmente desea escribir "

Hay muchos gotchyas en Java cuando comienzas a usar estadísticas que no siempre son obvias de inmediato. Por ejemplo, si tiene dos copias de su programa ejecutándose en la misma máquina virtual, ¿compartirán el valor de la variable estática y se meterán entre sí? ¿O qué sucede cuando amplías la clase, puedes anular el miembro estático? ¿Su máquina virtual se está quedando sin memoria porque tiene un número increíble de estadísticas y esa memoria no se puede recuperar para otros objetos de instancia necesarios?

Vida útil del objeto: además, las estadísticas tienen una vida útil que coincide con todo el tiempo de ejecución del programa. Esto significa que, incluso una vez que haya terminado de usar su clase, la memoria de todas esas variables estáticas no se puede recolectar basura. Si, por ejemplo, hizo que sus variables no sean estáticas, y en su función main () hizo una sola instancia de su clase, y luego le pidió a su clase que ejecutara una función particular 10,000 veces, una vez que se hicieron esas 10,000 llamadas , y elimina sus referencias a la instancia única, todas sus variables estáticas podrían ser recolectadas y reutilizadas.

Evita cierta reutilización: además, los métodos estáticos no se pueden usar para implementar una interfaz, por lo que los métodos estáticos pueden evitar que ciertas características orientadas a objetos sean utilizables.

Otras opciones: Si su principal preocupación es la eficiencia, puede haber otras formas mejores de resolver el problema de la velocidad que considerar solo la ventaja de que la invocación suele ser más rápida que la creación. Considere si los modificadores transitorios o volátiles son necesarios en cualquier lugar. Para preservar la capacidad de estar en línea, un método podría marcarse como final en lugar de estático. Los parámetros del método y otras variables se pueden marcar como finales para permitir ciertas optimizaciones del compilador basadas en suposiciones sobre qué puede cambiar esas variables. Un objeto de instancia podría reutilizarse varias veces en lugar de crear una nueva instancia cada vez. Puede haber interruptores de optimización del compilador que deberían estar activados para la aplicación en general. Quizás, el diseño debería configurarse de modo que las 10,000 ejecuciones puedan ser multiproceso y aprovechar los núcleos de múltiples procesadores. Si la portabilidad no es

Si por alguna razón no desea múltiples copias de un objeto, el patrón de diseño singleton, tiene ventajas sobre los objetos estáticos, como la seguridad de subprocesos (suponiendo que su singleton esté bien codificado), permitiendo una inicialización diferida, garantizando que el objeto se haya inicializado correctamente cuando se usa, subclases, ventajas en la prueba y refactorización de su código, sin mencionar que si en algún momento cambia de opinión acerca de querer solo una instancia de un objeto, es MUCHO más fácil eliminar el código para evitar instancias duplicadas que refactorizar todo el código de variable estática para usar variables de instancia. He tenido que hacer eso antes, no es divertido, y terminas teniendo que editar muchas más clases, lo que aumenta el riesgo de introducir nuevos errores ... mucho mejor para configurar las cosas "bien" la primera vez, incluso si parece que tiene sus desventajas. Para mi, el reprocesamiento requerido en caso de que decida en el futuro que necesita copias múltiples de algo es probablemente una de las razones más convincentes para usar la estática lo menos posible. Y por lo tanto, también estaría en desacuerdo con su afirmación de que las estadísticas reducen las interdependencias, creo que terminará con un código que está más acoplado si tiene muchas estadísticas a las que se puede acceder directamente, en lugar de un objeto que "sabe cómo hacerlo algo "en sí mismo.

Jessica Brown
fuente
11
Me gusta su respuesta, creo que se enfoca en las compensaciones correctas para considerar alrededor de las estadísticas en lugar de algunas de las pistas falsas como la concurrencia y el alcance. Y +1 para singletons, una mejor pregunta realmente podría haber sido cuándo usar variables / métodos estáticos vs singletons ...
studgeek
2
Aunque el singleton en sí mismo puede ser seguro para subprocesos (por ejemplo, mediante el uso de synchronizedmétodos), no significa que el código de llamada esté libre de condiciones de carrera con respecto al estado de singleton.
André Caron
8
Además, las estadísticas no están en contra del paradigma OOP. Muchos fanáticos de OOP te dirán que la clase es un objeto, y el método estático es un método del objeto de clase, en lugar de sus instancias. Este fenómeno está menos presente en Java. Otros lenguajes, como Python, le permiten usar clases como variables y puede acceder a métodos estáticos como métodos de ese objeto.
André Caron
44
La última línea del tercer párrafo debería leer, todas sus variables no estáticas , si no me equivoco.
Steve
1
Object Lifetime, es un punto muy importante que mencionó @jessica.
Abhidemon
93

Mal es un término subjetivo.

No controlas las estadísticas en términos de creación y destrucción. Viven a instancias del programa de carga y descarga.

Dado que las estadísticas viven en un espacio, todos los hilos que deseen usarlas deben pasar por el control de acceso que debe administrar. Esto significa que los programas están más acoplados y este cambio es más difícil de imaginar y administrar (como dice J Skeet). Esto lleva a problemas de aislamiento del impacto del cambio y, por lo tanto, afecta la forma en que se administran las pruebas.

Estos son los dos problemas principales que tengo con ellos.

Sangha Preet
fuente
59

No. Los estados globales no son malvados per se. Pero tenemos que ver su código para ver si lo usó correctamente. Es muy posible que un novato abuse de los estados globales; al igual que abusaría de todas las características del lenguaje.

Los estados globales son una necesidad absoluta. No podemos evitar los estados globales. No podemos evitar el razonamiento sobre los estados globales. - Si nos interesa entender nuestra semántica de aplicación.

Las personas que intentan deshacerse de los estados globales por el bien, inevitablemente terminan con un sistema mucho más complejo, y los estados globales todavía están allí, disfrazados de manera inteligente / idiota bajo muchas capas de indirecciones; y todavía tenemos que razonar sobre los estados globales, después de desenvolver todas las indirecciones.

Al igual que las personas de Spring que declaran generosamente estados globales en xml y piensan que de alguna manera es superior.

@ Jon Skeet if I create a new instance of an objectahora tiene dos razones para razonar: el estado dentro del objeto y el estado del entorno que aloja el objeto.

irreputable
fuente
10
"Tengo dos razones para razonar". No si hago que mi prueba solo dependa del estado del objeto. Lo que es más fácil, el estado menos global que tengo.
DJClayworth
2
La inyección de dependencia no tiene nada que ver con el estado global o la visibilidad global, incluso el contenedor en sí no es global. En comparación con el código "normal", la única cosa adicional que un objeto administrado por contenedor es visible es el contenedor mismo. De hecho, DI se usa muy comúnmente para evitar el patrón Singleton.
Floegipoky
31

Hay 2 problemas principales con las variables estáticas:

  • Seguridad de subprocesos: los recursos estáticos, por definición, no son seguros para subprocesos
  • Implicidad de código: no sabe cuándo se instancia una variable estática y si se instanciará antes que otra variable estática
sternr
fuente
Creo que Jon Skeet se refería al mismo comentario que publicaste.
RG-3
13
No entiendo el punto de seguridad de subprocesos, creo que nada es seguro para subprocesos a menos que lo haga. Esto no parece estar relacionado con cosas estáticas, corrígeme si me falta algo.
Zmaster
1
@Zmaster: si bien es cierto que la seguridad de subprocesos no es un problema exclusivo de las variables estáticas, porque, por su definición, deben llamarse desde y por diferentes contextos, son más
afines
2
@sternr Entiendo lo que quiere decir, evento si "contextos diferentes" no es necesariamente igual a "hilos diferentes". Pero es cierto que la seguridad de los hilos a menudo debe tenerse en cuenta con los recursos estáticos. Deberías considerar aclarar la oración.
Zmaster
Hay usos válidos seguros para subprocesos de recursos estáticos, por ejemplo. Logger final estático privado LOG = Logger.getLogger (Foo.class); AtomicInteger final estático privado x = nuevo AtomicInteger (0); Según tengo entendido, el cargador de clases garantiza asignaciones estáticas de recursos como este. La instancia de Logger es o no segura para subprocesos independientemente de dónde le asigne el puntero. Mantener el estado en estática probablemente no sea una buena idea, pero no hay razón para que no sea seguro para subprocesos.
teknopaul
29

Si está utilizando la palabra clave 'estática' sin la palabra clave 'final', esto debería ser una señal para considerar cuidadosamente su diseño. Incluso la presencia de un 'final' no es un pase libre, ya que un objeto final estático mutable puede ser igual de peligroso.

Estimaría en algún lugar alrededor del 85% de las veces que veo un 'estático' sin un 'final', es INCORRECTO. A menudo, encontraré soluciones extrañas para enmascarar u ocultar estos problemas.

Por favor no cree mutables estáticas. Especialmente colecciones. En general, las Colecciones deben inicializarse cuando se inicializa su objeto contenedor y deben diseñarse de modo que se restablezcan u olviden cuando se olvida su objeto contenedor.

El uso de estática puede crear errores muy sutiles que causarán dolor a los ingenieros. Lo sé, porque he creado y cazado estos errores.

Si desea más detalles, siga leyendo ...

¿Por qué no usar estadísticas?

Hay muchos problemas con la estática, incluida la escritura y ejecución de pruebas, así como errores sutiles que no son obvios de inmediato.

El código que se basa en objetos estáticos no se puede probar fácilmente en la unidad, y las estadísticas no se pueden burlar fácilmente (por lo general).

Si usa estadísticas, no es posible intercambiar la implementación de la clase para probar componentes de nivel superior. Por ejemplo, imagine un CustomerDAO estático que devuelve los objetos del Cliente que carga de la base de datos. Ahora tengo una clase CustomerFilter, que necesita acceder a algunos objetos de Customer. Si CustomerDAO es estático, no puedo escribir una prueba para CustomerFilter sin inicializar primero mi base de datos y completar información útil.

Y la población e inicialización de la base de datos lleva mucho tiempo. Y en mi experiencia, su marco de inicialización de DB cambiará con el tiempo, lo que significa que los datos se transformarán y las pruebas pueden romperse. Es decir, imagine que el Cliente 1 solía ser un VIP, pero el marco de inicialización de DB cambió, y ahora el Cliente 1 ya no es VIP, pero su prueba estaba codificada para cargar el Cliente 1 ...

Un mejor enfoque es crear una instancia de CustomerDAO y pasarlo al CustomerFilter cuando se construye. (Un enfoque aún mejor sería usar Spring u otro marco de Inversión de Control.

Una vez que haga esto, puede simular o eliminar rápidamente un DAO alternativo en su CustomerFilterTest, lo que le permite tener más control sobre la prueba,

Sin el DAO estático, la prueba será más rápida (sin inicialización de db) y más confiable (porque no fallará cuando cambie el código de inicialización de db). Por ejemplo, en este caso, asegurarse de que el Cliente 1 sea y siempre será un VIP, en lo que respecta a la prueba.

Ejecutando pruebas

Las estadísticas causan un problema real cuando se ejecutan conjuntos de pruebas unitarias juntas (por ejemplo, con su servidor de Integración Continua). Imagine un mapa estático de objetos de Socket de red que permanece abierto de una prueba a otra. La primera prueba podría abrir un Socket en el puerto 8080, pero se olvidó de borrar el Mapa cuando la prueba se derribó. Ahora, cuando se inicia una segunda prueba, es probable que se bloquee cuando intenta crear un nuevo Socket para el puerto 8080, ya que el puerto todavía está ocupado. Imagine también que las referencias de Socket en su Colección estática no se eliminan, y (con la excepción de WeakHashMap) nunca son elegibles para la recolección de basura, lo que causa una pérdida de memoria.

Este es un ejemplo demasiado generalizado, pero en sistemas grandes, este problema ocurre TODO EL TIEMPO. La gente no piensa en las pruebas unitarias que inician y detienen su software repetidamente en la misma JVM, pero es una buena prueba del diseño de su software, y si tiene aspiraciones de alta disponibilidad, es algo que debe tener en cuenta.

Estos problemas a menudo surgen con objetos de marco, por ejemplo, sus capas de acceso a la base de datos, almacenamiento en caché, mensajería y registro. Si está utilizando Java EE o algunos de los mejores frameworks, probablemente manejen mucho de esto por usted, pero si como yo está tratando con un sistema heredado, es posible que tenga muchos frameworks personalizados para acceder a estas capas.

Si la configuración del sistema que se aplica a estos componentes del marco cambia entre las pruebas unitarias, y el marco de prueba unitaria no destruye y reconstruye los componentes, estos cambios no pueden tener efecto, y cuando una prueba se basa en esos cambios, fallarán .

Incluso los componentes no marco están sujetos a este problema. Imagine un mapa estático llamado OpenOrders. Escribe una prueba que crea algunas órdenes abiertas y comprueba para asegurarse de que todas estén en el estado correcto, luego finaliza la prueba. Otro desarrollador escribe una segunda prueba que coloca los pedidos que necesita en el mapa de OpenOrders, luego afirma que el número de pedidos es exacto. Ejecutadas individualmente, ambas pruebas pasarían, pero cuando se ejecutan juntas en una suite, fallarán.

Peor aún, la falla puede basarse en el orden en que se ejecutaron las pruebas.

En este caso, al evitar la estática, se evita el riesgo de persistencia de datos en las instancias de prueba, lo que garantiza una mejor confiabilidad de la prueba.

Errores sutiles

Si trabaja en un entorno de alta disponibilidad, o en cualquier lugar donde los subprocesos puedan iniciarse y detenerse, la misma preocupación mencionada anteriormente con los conjuntos de pruebas unitarias puede aplicarse cuando su código también se ejecuta en producción.

Cuando se trata de hilos, en lugar de usar un objeto estático para almacenar datos, es mejor usar un objeto inicializado durante la fase de inicio del hilo. De esta manera, cada vez que se inicia el subproceso, se crea una nueva instancia del objeto (con una configuración potencialmente nueva) y evita que los datos de una instancia del subproceso se filtren a la siguiente instancia.

Cuando un hilo muere, un objeto estático no se restablece ni se recolecta basura. Imagine que tiene un hilo llamado "EmailCustomers", y cuando se inicia, completa una colección de cadenas estática con una lista de direcciones de correo electrónico, luego comienza a enviar cada una de las direcciones. Digamos que el hilo se interrumpe o cancela de alguna manera, por lo que su marco de alta disponibilidad reinicia el hilo. Luego, cuando se inicia el hilo, vuelve a cargar la lista de clientes. Pero debido a que la colección es estática, podría retener la lista de direcciones de correo electrónico de la colección anterior. Ahora algunos clientes pueden recibir correos electrónicos duplicados.

Un aparte: final estático

El uso de "static final" es efectivamente el equivalente de Java de un C #define, aunque existen diferencias de implementación técnica. AC / C ++ #define es intercambiado fuera del código por el preprocesador, antes de la compilación. Un "final estático" de Java terminará en la memoria residente en la pila. De esa manera, es más similar a una variable de "constante estática" en C ++ que a una #define.

Resumen

Espero que esto ayude a explicar algunas razones básicas por las que las estadísticas son problemáticas. Si está utilizando un marco Java moderno como Java EE o Spring, etc., es posible que no encuentre muchas de estas situaciones, pero si está trabajando con un gran cuerpo de código heredado, pueden volverse mucho más frecuentes.

JBCP
fuente
15

Como nadie * lo ha mencionado: concurrencia. Las variables estáticas pueden sorprenderlo si tiene múltiples hilos que leen y escriben en la variable estática. Esto es común en aplicaciones web (por ejemplo, ASP.NET) y puede causar algunos errores bastante enloquecedores. Por ejemplo, si tiene una variable estática que una página actualiza y dos personas solicitan la página "casi al mismo tiempo", un usuario puede obtener el resultado esperado por el otro, o peor.

Las estadísticas reducen las interdependencias en las otras partes del código. Pueden actuar como titulares perfectos del estado

Espero que estés preparado para usar cerraduras y lidiar con la contención.

* En realidad, Preet Sangha lo mencionó.

Justin M. Keyes
fuente
55
Las variables de instancia no tienen ventajas de seguridad de subprocesos sobre las estadísticas, todas son variables desprotegidas. En cambio, todo se reduce a cómo protege el código que accede a esas variables.
studgeek
2
No hice esa afirmación del todo, pero en aras de la discusión: la separación es una forma de protección. Los estados del hilo están separados; estado global no lo es . Una variable de instancia no necesita protección a menos que se comparta explícitamente entre subprocesos; Una variable estática siempre es compartida por todos los hilos en el proceso.
Justin M. Keyes
Desearía que las variables estáticas de hilo fueran más un concepto de primera clase, ya que pueden ser muy útiles para hacer que la información esté disponible de forma segura para una llamada de subrutina envuelta sin tener que pasar esa información a través de cada capa de envoltura. Por ejemplo, si un objeto tenía métodos para representarlo en el contexto gráfico actual del hilo, y había métodos para guardar / restaurar el contexto gráfico actual, usarlos a menudo podría ser más limpio que tener que pasar el contexto gráfico a través de cada llamada a método.
supercat
15

Resumiendo algunas ventajas y desventajas básicas del uso de métodos estáticos en Java:

Ventajas:

  1. Accesible globalmente, es decir, no vinculado con ninguna instancia de objeto en particular.
  2. Una instancia por JVM.
  3. Se puede acceder utilizando el nombre de la clase (no se requiere ningún objeto).
  4. Contiene un valor único aplicable a todas las instancias.
  5. Se carga al iniciar JVM y muere cuando JVM se apaga.
  6. No modifican el estado del objeto.

Desventajas

  1. Los miembros estáticos siempre son parte de la memoria, estén o no en uso.
  2. No puede controlar la creación y destrucción de variables estáticas. Útilmente, se crearon al cargar el programa y se destruyeron cuando se descargó el programa (o cuando JVM se apaga).
  3. Puede hacer que el hilo estático sea seguro mediante la sincronización, pero necesita algunos esfuerzos adicionales.
  4. Si un hilo cambia el valor de una variable estática que posiblemente puede romper la funcionalidad de otros hilos.
  5. Debe saber "estático" antes de usarlo.
  6. No puede anular los métodos estáticos.
  7. La serialización no funciona bien con ellos.
  8. No participan en el polimorfismo de tiempo de ejecución.
  9. Hay un problema de memoria (hasta cierto punto, pero no mucho, supongo) si se usa una gran cantidad de variables / métodos estáticos. Porque no se recolectarán basura hasta que finalice el programa.
  10. Los métodos estáticos también son difíciles de probar.
Affy
fuente
Las desventajas 6, 7, 8 y 10 son las desventajas de los lenguajes / marcos utilizados, y no las desventajas de las variables estáticas en general. Las desventajas 1, 4 y 5 también existen para otras soluciones, como algunos patrones únicos proporcionados por algún marco. (No voté a la respuesta, porque estoy de acuerdo con el resto y es una buena colección.)
Peter - Restablece a Monica
13

si tuviera que hacer 10,000 llamadas a una función dentro de una clase, me complacería hacer que el método sea estático y usar un class.methodCall () directo en lugar de saturar la memoria con 10,000 instancias de la clase, ¿verdad?

Debe equilibrar la necesidad de encapsular datos en un objeto con un estado, en comparación con la necesidad de simplemente calcular el resultado de una función en algunos datos.

Además, las estadísticas reducen las interdependencias en las otras partes del código.

También lo hace la encapsulación. En aplicaciones grandes, las estadísticas tienden a producir código de espagueti y no permiten fácilmente la refactorización o prueba.

Las otras respuestas también brindan buenas razones contra el uso excesivo de estadísticas.

Jérôme Verstrynge
fuente
13

Las variables estáticas generalmente se consideran malas porque representan un estado global y, por lo tanto, son mucho más difíciles de razonar. En particular, rompen los supuestos de la programación orientada a objetos. En la programación orientada a objetos, cada objeto tiene su propio estado, representado por variables de instancia (no estáticas). Las variables estáticas representan estados entre instancias que pueden ser mucho más difíciles de probar en unidades. Esto se debe principalmente a que es más difícil aislar los cambios en las variables estáticas en una sola prueba.

Dicho esto, es importante hacer una distinción entre las variables estáticas regulares (generalmente consideradas malas) y las variables estáticas finales (constantes AKA; no tan malas).

Jack Edmonds
fuente
44
"Las variables estáticas representan el estado en todas las clases" ... ¿Creo que quiere decir "las variables estáticas representan el estado en todas las instancias"? +1 para "constantes AKA estáticas finales, no tan mal". Como el valor no puede cambiar, cualquier cosa que dependa de él en un momento dado no puede cambiar implícitamente su comportamiento en un momento posterior; el valor es el mismo.
Jared Updike
"Las variables estáticas representan el estado en todas las instancias" es una forma mucho mejor de expresarlo. He editado mi respuesta.
Jack Edmonds
9

En mi opinión, casi nunca se trata de rendimiento, se trata de diseño. No considero que el uso de métodos estáticos sea incorrecto en comparación con el uso de variables estáticas (pero supongo que en realidad estás hablando de llamadas a métodos).

Se trata simplemente de cómo aislar la lógica y darle un buen lugar. A veces eso justifica el uso de métodos estáticos, de los cuales java.lang.Mathes un buen ejemplo. Creo que cuando nombras la mayoría de tus clases XxxUtilo Xxxhelperes mejor que reconsideres tu diseño.

M Platvoet
fuente
3
Los métodos estáticos libres de efectos secundarios puros están perfectamente bien IMO. Pero el estado mutable global rara vez lo es e interpreto que el OP habla del estado global.
CodesInChaos
1
@CodeInChaos totalmente de acuerdo. Creo que el OP no es del todo claro sobre la diferencia entre métodos estáticos y vars.
M Platvoet
8

Acabo de resumir algunos de los puntos hechos en las respuestas. Si encuentra algo malo, no dude en corregirlo.

Escalado: tenemos exactamente una instancia de una variable estática por JVM. Supongamos que estamos desarrollando un sistema de administración de bibliotecas y decidimos poner el nombre del libro como una variable estática, ya que solo hay uno por libro. Pero si el sistema crece y estamos usando múltiples JVM, ¿entonces no tenemos una manera de averiguar con qué libro estamos tratando?

Seguridad de subprocesos: tanto la variable de instancia como la variable estática deben controlarse cuando se usan en entornos de subprocesos múltiples. Pero en el caso de una variable de instancia, no necesita protección a menos que se comparta explícitamente entre subprocesos, pero en el caso de una variable estática, siempre se comparte entre todos los subprocesos del proceso.

Pruebas: aunque el diseño comprobable no equivale a un buen diseño, pero rara vez observaremos un buen diseño que no sea comprobable. Como las variables estáticas representan el estado global y se hace muy difícil probarlas.

Razonamiento sobre el estado: si creo una nueva instancia de una clase, podemos razonar sobre el estado de esta instancia, pero si tiene variables estáticas, podría estar en cualquier estado. ¿Por qué? Debido a que es posible que la variable estática haya sido modificada por alguna instancia diferente, ya que la variable estática se comparte entre las instancias.

Serialización: la serialización tampoco funciona bien con ellos.

Creación y destrucción: la creación y destrucción de variables estáticas no se pueden controlar. Por lo general, se crean y destruyen en el momento de carga y descarga del programa. Significa que son malos para la administración de la memoria y también suman el tiempo de inicialización al inicio.

Pero, ¿y si realmente los necesitamos?

Pero a veces podemos tener una necesidad genuina de ellos. Si realmente sentimos la necesidad de muchas variables estáticas que se comparten en la aplicación, entonces una opción es utilizar el patrón de diseño Singleton que tendrá todas estas variables. O podemos crear algún objeto que tenga estas variables estáticas y se pueda pasar.

Además, si la variable estática se marca como final, se convierte en una constante y el valor que se le asigna una vez no se puede cambiar. Significa que nos salvará de todos los problemas que enfrentamos debido a su mutabilidad.

akhil_mittal
fuente
7

Me parece que está preguntando sobre variables estáticas, pero también señala métodos estáticos en sus ejemplos.

Las variables estáticas no son malas: tienen su adopción como variables globales como constantes en la mayoría de los casos combinadas con el modificador final, pero como se dice, no las uses en exceso.

Métodos estáticos, también conocido como método de utilidad. Por lo general, no es una mala práctica usarlos, pero la principal preocupación es que podrían obstruir pruebas.

Como ejemplo de un gran proyecto de Java que usa muchas estadísticas y lo hace correctamente, ¡mira Play! marco . También hay discusión respecto en SO.

Las variables / métodos estáticos combinados con la importación estática también se usan ampliamente en bibliotecas que facilitan la programación declarativa en java como: hacerlo fácil o Hamcrest . No sería posible sin muchas variables y métodos estáticos.

Por lo tanto, las variables estáticas (y los métodos) son buenos, ¡pero úselos sabiamente!

cetnar
fuente
6

Lo más importante es que las variables estáticas crean problemas con la seguridad de los datos (en cualquier momento cambiado, cualquiera puede cambiar, acceso directo sin objeto, etc.)

Para más información lea esto Gracias.

Anuj Patel
fuente
6

Se puede sugerir que en la mayoría de los casos en los que usa una variable estática, realmente desea usar el patrón singleton .

El problema con los estados globales es que a veces lo que tiene sentido como global en un contexto más simple, debe ser un poco más flexible en un contexto práctico, y aquí es donde el patrón singleton se vuelve útil.

Charles Goodwin
fuente
5

Otra razón más: fragilidad.

Si tiene una clase, la mayoría de las personas esperan poder crearla y usarla a voluntad.

Puede documentar que no es el caso, o protegerse contra él (patrón único / de fábrica), pero eso es un trabajo adicional y, por lo tanto, un costo adicional. Incluso entonces, en una gran empresa, es probable que alguien intente usar su clase en algún momento sin prestar atención a todos los comentarios agradables o la fábrica.

Si usa muchas variables estáticas, eso se romperá. Los errores son caros.

Entre una mejora de rendimiento de .0001% y la robustez para cambiar por parte de desarrolladores potencialmente desorientados, en muchos casos, la robustez es la buena opción.

ptyx
fuente
4

Encuentro que las variables estáticas son más convenientes de usar. Y supongo que también son eficientes (corríjame si me equivoco) porque si tuviera que hacer 10,000 llamadas a una función dentro de una clase, me complacería hacer que el método sea estático y usar una clase directa. en lugar de saturar la memoria con 10.000 instancias de la clase, ¿verdad?

Veo lo que piensas, pero un patrón Singleton simple hará lo mismo sin tener que instanciar 10 000 objetos.

Se pueden usar métodos estáticos, pero solo para funciones que están relacionadas con el dominio del objeto y que no necesitan o usan propiedades internas del objeto.

ex:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}
Cygnusx1
fuente
Un singleton clásico (es decir, uno al que se accede Class.Instance) es apenas mejor que una variable estática. Es un poco más comprobable, pero aún mucho peor que los diseños en los que se crea una sola instancia en lugar de crear su código suponiendo que solo haya una.
CodesInChaos
¡No estoy seguro de entender tu comentario! Estaba respondiendo al OP sobre lo que dijo en cursiva sobre instancias de 10 000 objetos. No entiendo por qué compara un singleton y una variable estática? ¡Lo que entiendo de lo que escribiste es que Singleton es un mal diseño ...! Supongo que no te entiendo, ya que Spring Framework
crea
Un singleton clásico (que tiene Class.Instance) que lleva un estado mutable es un mal diseño de la OMI. En ese caso, prefiero un diseño donde obtengo los singletons que necesito usar pasados ​​como parámetro a la clase que los usa (generalmente con la ayuda de DI). Los singletons clásicos lógicamente inmutables están bien en mi opinión.
CodesInChaos
@ Cygnusx1 En caso de que no estuviera claro por qué un Singleton de clase (un singleton donde la clase asegura que es una copia única) no era fácilmente comprobable, vincula estrechamente la existencia de la clase al ciclo de vida del programa. Para probarlo, debe cumplir con el inicio y apagado del programa, que a menudo tiene efectos secundarios sin importancia para probar la clase. Si fue efectivamente como singleton (una copia en el programa, pero de lo contrario no se aplicaría), podría crear varias copias en el momento de la prueba sin el programa, verificando que el comportamiento en toda la clase sea como debería ser para cada escenario de prueba.
Edwin Buck
4

El tema de 'La estática es malvada' es más un problema sobre el estado global. El momento apropiado para que una variable sea estática es si nunca tiene más de un estado; Las herramientas de IE a las que debe poder acceder todo el marco y que siempre devuelven los mismos resultados para las mismas llamadas a métodos nunca son 'malas' que las estadísticas. En cuanto a tu comentario:

Encuentro que las variables estáticas son más convenientes de usar. Y supongo que también son eficientes

Las estadísticas son la opción ideal y eficiente para variables / clases que nunca cambian .

El problema con el estado global es la inconsistencia inherente que puede crear. La documentación sobre las pruebas unitarias a menudo aborda este problema, ya que cada vez que hay un estado global al que pueden acceder más de varios objetos no relacionados, las pruebas unitarias estarán incompletas y no 'granuladas'. Como se menciona en este artículo sobre el estado global y los singletons , si los objetos A y B no están relacionados (ya que en uno no se da referencia expresa a otro), A no debería ser capaz de afectar el estado de B.

Hay algunas excepciones a la prohibición del estado global en un buen código, como el reloj. El tiempo es global y, en cierto sentido, cambia el estado de los objetos sin tener una relación codificada.

Bryan Agee
fuente
"El tiempo es global": existen otras formas de modelar el tiempo en los sistemas informáticos que hacer que sea algo implícito y global que cambie por sí solo. cf. esta encuesta: "Tiempo de modelado en informática: una taxonomía y una encuesta comparativa" @ arxiv.org/abs/0807.4132
Jared Updike
4

Mi $ .02 es que varias de estas respuestas confunden el problema, en lugar de decir "las estadísticas son malas". Creo que es mejor hablar sobre el alcance y las instancias.

Lo que yo diría es que una estática es una variable de "clase": representa un valor que se comparte en todas las instancias de esa clase. Por lo general, también debe tener un alcance de esa manera (protegido o privado para la clase y sus instancias).

Si planea poner un comportamiento a nivel de clase a su alrededor y exponerlo a otro código, entonces un singleton puede ser una mejor solución para soportar cambios en el futuro (como sugirió @Jessica). Esto se debe a que puede usar interfaces a nivel de instancia / singleton de maneras que no puede usar a nivel de clase, en particular la herencia.

Algunas reflexiones sobre por qué creo que algunos de los aspectos en otras respuestas no son fundamentales para la pregunta ...

La estática no es "global". En Java, el alcance se controla por separado de static / instance.

La concurrencia no es menos peligrosa para las estadísticas que los métodos de instancia. Todavía es un estado que necesita ser protegido. Claro que puede tener 1000 instancias con una variable de instancia cada una y solo una variable estática, pero si el código de acceso no está escrito de manera segura para los subprocesos, todavía está atornillado, puede que le tome un poco más de tiempo darse cuenta. .

La gestión del ciclo de vida es un argumento interesante, pero creo que es menos importante. No veo por qué es más difícil administrar un par de métodos de clase como init () / clear () que la creación y destrucción de una instancia singleton. De hecho, algunos podrían decir que un singleton es un poco más complicado debido a GC.

PD: en términos de Smalltalk, muchos de sus dialectos tienen variables de clase, pero en Smalltalk las clases son en realidad instancias de Metaclass, por lo que realmente son variables en la instancia de Metaclass. Aún así, aplicaría la misma regla general. Si se están utilizando para un estado compartido entre instancias, entonces está bien. Si son compatibles con la funcionalidad pública, debería mirar un Singleton. Suspiro, seguro que extraño a Smalltalk ...

studgeek
fuente
4

Hay dos preguntas principales en tu publicación.

Primero, sobre las variables estáticas. Las variables estáticas son completamente innecesarias y su uso se puede evitar fácilmente. En los idiomas OOP en general, y en Java en particular, los parámetros de función se pasan por referencia, es decir, si pasa un objeto a una función, está pasando un puntero al objeto, por lo que no necesita definir variables estáticas ya que Puede pasar un puntero al objeto a cualquier ámbito que necesite esta información. Incluso si esto implica que llenará su memoria con punteros, esto no representará necesariamente un bajo rendimiento porque los sistemas de paginación de memoria reales están optimizados para manejar esto, y mantendrán en la memoria las páginas referenciadas por los punteros que pasó al nuevo alcance; El uso de variables estáticas puede hacer que el sistema cargue la página de memoria donde se almacenan cuando es necesario acceder (esto sucederá si la página no ha sido accedida durante mucho tiempo). Una buena práctica es reunir todo ese material estático en algunas pequeñas "clases de configuración", esto asegurará que el sistema lo ponga todo en la misma página de memoria.

En segundo lugar, sobre los métodos estáticos. Los métodos estáticos no son tan malos, pero pueden reducir rápidamente el rendimiento. Por ejemplo, piense en un método que compara dos objetos de una clase y devuelve un valor que indica cuál de los objetos es más grande (método de comparación típica), este método puede ser estático o no, pero al invocarlo, la forma no estática será más eficiente ya que tendrá que resolver solo dos referencias (una para cada objeto) frente a las tres referencias que deberán resolver la versión estática del mismo método (una para la clase más dos, una para cada objeto). Pero como digo, esto no es tan malo, si echamos un vistazo a la clase de Matemáticas, podemos encontrar muchas funciones matemáticas definidas como métodos estáticos. Esto es realmente más eficiente que poner todos estos métodos en la clase que define los números,

En conclusión: evite el uso de variables estáticas y encuentre el equilibrio de rendimiento correcto cuando trabaje con métodos estáticos o no estáticos.

PD: Perdón por mi inglés.

jrodriguez
fuente
4

No hay nada malo con las variables estáticas per se. Es solo la sintaxis de Java que está rota. Cada clase Java en realidad define dos estructuras: un objeto singleton que encapsula variables estáticas y una instancia. Definir ambos en el mismo bloque fuente es pura maldad, y da como resultado un código que es difícil de leer. Scala hizo eso bien.

Zdeněk Pavlas
fuente
3

a) Razón sobre los programas.

Si tiene un programa de pequeño a mediano, donde se accede a la variable estática Global.foo, la llamada normalmente proviene de la nada: no hay una ruta y, por lo tanto, no hay una línea de tiempo, cómo la variable llega al lugar, donde es usado Ahora, ¿cómo sé quién lo configuró en su valor real? ¿Cómo sé qué sucede si lo modifico ahora? Tengo grep sobre toda la fuente, para recopilar todos los accesos, para saber qué está pasando.

Si sabe cómo lo usa, porque acaba de escribir el código, el problema es invisible, pero si intenta comprender el código extranjero, lo comprenderá.

b) ¿Realmente solo necesitas uno?

Las variables estáticas a menudo evitan que múltiples programas del mismo tipo se ejecuten en la misma JVM con diferentes valores. A menudo no prevé usos, donde más de una instancia de su programa es útil, pero si evoluciona, o si es útil para otros, pueden experimentar situaciones en las que les gustaría iniciar más de una instancia de su programa. .

Solo un código más o menos inútil que no será utilizado por muchas personas durante un tiempo prolongado de manera intensiva podría funcionar bien con variables estáticas.

usuario desconocido
fuente
3

todo (puede :) tiene su propósito, si tiene un montón de subprocesos que necesita compartir / almacenar datos en caché y también toda la memoria accesible (para que no se divida en contextos dentro de una JVM), la estática es la mejor opción

-> por supuesto, puede forzar solo una instancia, pero ¿por qué?
encuentro algunos de los comentarios en este hilo malvados, no las estadísticas;)

tomasb
fuente
3

Las variables estáticas no son buenas ni malas. Representan atributos que describen toda la clase y no una instancia particular. Si necesita tener un contador para todas las instancias de una determinada clase, una variable estática sería el lugar adecuado para mantener el valor.

Los problemas aparecen cuando intenta utilizar variables estáticas para mantener valores relacionados con la instancia.

Andres
fuente
2

Todas las respuestas anteriores muestran por qué las estadísticas son malas. La razón por la que son malvados es porque da la falsa impresión de que está escribiendo código orientado a objetos, cuando en realidad no lo está. Eso es simplemente malvado.

estúpido
fuente
1
¿Pero considerar rígidamente su código para seguir algún paradigma estándar arbitrario realmente mejora el código, o nos estamos quejando de evitar simplemente escribir código que funcione?
Zoey
Sí, lo hace mejor, porque lo hace más manejable en el futuro, más fácil de entender y más explícito.
Blockhead
1
¿Por qué es malo no escribir código OO? ¿Y por qué Bjarne Stroustrup no está de acuerdo con usted? por nombrar solo uno ...
Marqués de Lorne
2
No dije que fuera malo no escribir código OO. Dije que es malo pensar que estás escribiendo un código OO, cuando todo lo que estás haciendo es disfrazar los globales detrás de los métodos y propiedades estáticos. Por favor, vuelva a leer lo que escribí.
Blockhead
2

Aquí hay muchas buenas respuestas que se suman,

Memoria: las variables estáticas están activas mientras el cargador de clases viva [en general hasta que VM muera], pero esto es solo en el caso de objetos / referencias masivas almacenados como estáticos.

Modularización: considere conceptos como IOC, dependenceInjection, proxy, etc. Todos están completamente en contra de implementaciones de acoplamiento / estáticas.

Otros inconvenientes: seguridad del hilo, capacidad de prueba

Sankarganesh Eswaran
fuente
0

Piense que si tiene una aplicación con muchos usuarios y ha definido una forma estática, cada usuario modificará también todas las demás formas de otros usuarios.

seinta
fuente
0

Creo que el uso excesivo de variables globales con palabras clave estáticas también conducirá a una pérdida de memoria en algún momento de la aplicación.

Rahul Anand
fuente
0

Desde mi punto de vista, la staticvariable debe ser solo de lectura de datos o variables creadas por convención .

Por ejemplo, tenemos una interfaz de usuario de algún proyecto, y tenemos una lista de países, idiomas, roles de usuario, etc. Y tenemos clase para organizar estos datos. Estamos absolutamente seguros de que la aplicación no funcionará sin estas listas. así que lo primero que hacemos en la aplicación init es verificar esta lista para ver si hay actualizaciones y obtener esta lista de la API (si es necesario). Por lo tanto, estamos de acuerdo en que estos datos están "siempre" presentes en la aplicación. Prácticamente son datos de solo lectura, por lo que no necesitamos ocuparnos de su estado; al pensar en este caso, realmente no queremos tener muchas instancias de esos datos; este caso parece un candidato perfecto para ser estático .

qiAlex
fuente
0

He jugado mucho con las estadísticas y ¿puedo darle una respuesta ligeramente diferente, o tal vez una forma ligeramente diferente de verlo?

Cuando utilicé estadísticas en una clase (Miembros y métodos, ambos) eventualmente comencé a notar que mi clase es en realidad dos clases que comparten la responsabilidad: existe la parte "Estática", que se parece mucho a un singleton y existe la no Parte estática (una clase normal). Hasta donde sé, siempre puedes separar esas dos clases por completo simplemente seleccionando todas las estadísticas para una clase y no estadísticas para la otra.

Esto solía suceder mucho cuando tenía una colección estática dentro de una clase que contenía instancias de la clase y algunos métodos estáticos para administrar la colección. Una vez que lo piensas, es obvio que tu clase no está haciendo "Solo una cosa", es ser una colección y hacer algo completamente diferente.

Ahora, refactoricemos un poco el problema: si divide su clase en una clase donde todo es estático y otra que es solo una "Clase normal" y se olvida de la "Clase normal", entonces su pregunta se convierte en una clase puramente estática vs Singleton que se aborda en detalle aquí (y probablemente una docena de otras preguntas).

Bill K
fuente