Estoy tratando de entender los objetos mutables vs inmutables. El uso de objetos mutables recibe mucha mala prensa (por ejemplo, devolver una serie de cadenas de un método) pero tengo problemas para comprender cuáles son los impactos negativos de esto. ¿Cuáles son las mejores prácticas en torno al uso de objetos mutables? ¿Deberías evitarlos siempre que sea posible?
oop
immutability
mutable
Alex Angas
fuente
fuente
string
es inmutable, al menos en .NET, y creo que también en muchos otros lenguajes modernos.Respuestas:
Bueno, hay algunos aspectos de esto.
Los objetos mutables sin identidad de referencia pueden causar errores en momentos extraños. Por ejemplo, considere un
Person
bean con unequals
método basado en valores :La
Person
instancia se "pierde" en el mapa cuando se usa como clave porque suhashCode
igualdad se basa en valores mutables. Esos valores cambiaron fuera del mapa y todo el hashing se volvió obsoleto. A los teóricos les gusta insistir en este punto, pero en la práctica no he encontrado que sea un gran problema.Otro aspecto es la "razonabilidad" lógica de su código. Este es un término difícil de definir, que abarca todo, desde la legibilidad hasta el flujo. Genéricamente, debería poder ver un fragmento de código y comprender fácilmente lo que hace. Pero más importante que eso, deberías poder convencerte de que hace lo que hace correctamente . Cuando los objetos pueden cambiar independientemente en diferentes "dominios" de código, a veces se hace difícil hacer un seguimiento de qué es dónde y por qué (" acción espeluznante a distancia "). Este es un concepto más difícil de ejemplificar, pero es algo que a menudo se enfrenta en arquitecturas más grandes y complejas.
Finalmente, los objetos mutables son asesinos en situaciones concurrentes. Cada vez que accede a un objeto mutable desde subprocesos separados, debe lidiar con el bloqueo. Esto reduce el rendimiento y hace que su código sea mucho más difícil de mantener. Un sistema suficientemente complicado elimina este problema de forma tan desproporcionada que resulta casi imposible de mantener (incluso para expertos en concurrencia).
Los objetos inmutables (y más particularmente, las colecciones inmutables) evitan todos estos problemas. Una vez que comprenda cómo funcionan, su código se convertirá en algo que es más fácil de leer, más fácil de mantener y menos propenso a fallar de maneras extrañas e impredecibles. Los objetos inmutables son aún más fáciles de probar, debido no solo a su facilidad de imitación, sino también a los patrones de código que tienden a imponer. En resumen, ¡son buenas prácticas por todas partes!
Dicho esto, apenas soy un fanático en este asunto. Algunos problemas simplemente no se modelan bien cuando todo es inmutable. Pero sí creo que debería intentar empujar la mayor parte de su código en esa dirección como sea posible, suponiendo, por supuesto, que está utilizando un lenguaje que hace que esta sea una opinión sostenible (C / C ++ lo hace muy difícil, al igual que Java) . En resumen: las ventajas dependen un poco de su problema, pero preferiría la inmutabilidad.
fuente
Objetos inmutables versus colecciones inmutables
Uno de los puntos más finos en el debate sobre los objetos mutables frente a los inmutables es la posibilidad de extender el concepto de inmutabilidad a las colecciones. Un objeto inmutable es un objeto que a menudo representa una única estructura lógica de datos (por ejemplo, una cadena inmutable). Cuando tiene una referencia a un objeto inmutable, el contenido del objeto no cambiará.
Una colección inmutable es una colección que nunca cambia.
Cuando realizo una operación en una colección mutable, entonces cambio la colección en su lugar, y todas las entidades que tienen referencias a la colección verán el cambio.
Cuando realizo una operación en una colección inmutable, se devuelve una referencia a una nueva colección que refleja el cambio. Todas las entidades que tienen referencias a versiones anteriores de la colección no verán el cambio.
Las implementaciones inteligentes no necesariamente necesitan copiar (clonar) toda la colección para proporcionar esa inmutabilidad. El ejemplo más simple es la pila implementada como una lista vinculada individualmente y las operaciones push / pop. Puede reutilizar todos los nodos de la colección anterior en la nueva colección, agregando solo un nodo para la inserción y sin clonar nodos para el pop. La operación push_tail en una lista vinculada individualmente, por otro lado, no es tan simple o eficiente.
Variables / referencias inmutables frente a variables mutables
Algunos lenguajes funcionales toman el concepto de inmutabilidad para objetar referencias, permitiendo solo una asignación de referencia única.
Facilidad de desarrollo frente a rendimiento
Casi siempre la razón para usar un objeto inmutable es promover una programación libre de efectos secundarios y un razonamiento simple sobre el código (especialmente en un entorno altamente concurrente / paralelo). No tiene que preocuparse de que otra entidad cambie los datos subyacentes si el objeto es inmutable.
El principal inconveniente es el rendimiento. Aquí hay una reseña de una prueba simple que hice en Java comparando algunos objetos inmutables frente a mutables en un problema de juguete.
Los problemas de rendimiento son discutibles en muchas aplicaciones, pero no en todas, razón por la cual muchos paquetes numéricos grandes, como la clase Numpy Array en Python, permiten actualizaciones in situ de matrices grandes. Esto sería importante para las áreas de aplicación que utilizan operaciones de matriz y vectores grandes. Estos grandes problemas de datos paralelos y computacionalmente intensivos logran una gran aceleración al operar en el lugar.
fuente
Consulte esta publicación de blog: http://www.yegor256.com/2014/06/09/objects-should-be-immutable.html . Explica por qué los objetos inmutables son mejores que los mutables. En breve:
fuente
Los objetos inmutables son un concepto muy poderoso. Quitan gran parte de la carga de tratar de mantener objetos / variables consistentes para todos los clientes.
Puede usarlos para objetos no polimórficos de bajo nivel, como una clase CPoint, que se usan principalmente con semántica de valor.
O puede usarlos para interfaces polimórficas de alto nivel, como una función IF que representa una función matemática, que se usa exclusivamente con semántica de objetos.
Mayor ventaja: la inmutabilidad + semántica de objetos + punteros inteligentes hacen que la propiedad del objeto no sea un problema, todos los clientes del objeto tienen su propia copia privada por defecto. Implícitamente, esto también significa un comportamiento determinista en presencia de concurrencia.
Desventaja: cuando se usa con objetos que contienen muchos datos, el consumo de memoria puede convertirse en un problema. Una solución a esto podría ser mantener las operaciones en un objeto simbólico y hacer una evaluación perezosa. Sin embargo, esto puede conducir a cadenas de cálculos simbólicos, que pueden influir negativamente en el rendimiento si la interfaz no está diseñada para acomodar operaciones simbólicas. Algo que definitivamente debe evitarse en este caso es devolver grandes cantidades de memoria de un método. En combinación con operaciones simbólicas encadenadas, esto podría conducir a un consumo masivo de memoria y una degradación del rendimiento.
Así que los objetos inmutables son definitivamente mi forma principal de pensar sobre el diseño orientado a objetos, pero no son un dogma. Resuelven muchos problemas para clientes de objetos, pero también crean muchos, especialmente para los implementadores.
fuente
Debe especificar de qué idioma está hablando. Para lenguajes de bajo nivel como C o C ++, prefiero usar objetos mutables para ahorrar espacio y reducir la pérdida de memoria. En los lenguajes de nivel superior, los objetos inmutables hacen que sea más fácil razonar sobre el comportamiento del código (especialmente el código multiproceso) porque no hay una "acción espeluznante a distancia".
fuente
Un objeto mutable es simplemente un objeto que se puede modificar después de su creación / instancia, frente a un objeto inmutable que no se puede modificar (consulte la página de Wikipedia sobre el tema). Un ejemplo de esto en un lenguaje de programación son las listas y tuplas de Python. Las listas se pueden modificar (p. Ej., Se pueden agregar nuevos elementos después de crearlos) mientras que las tuplas no.
Realmente no creo que haya una respuesta clara sobre cuál es mejor para todas las situaciones. Ambos tienen sus lugares.
fuente
Si un tipo de clase es mutable, una variable de ese tipo de clase puede tener varios significados diferentes. Por ejemplo, suponga que un objeto
foo
tiene un campoint[] arr
y contiene una referencia a unaint[3]
retención de los números {5, 7, 9}. Aunque se conoce el tipo de campo, hay al menos cuatro cosas diferentes que puede representar:Una referencia potencialmente compartida, cuyos titulares solo se preocupan de que encapsule los valores 5, 7 y 9. Si
foo
deseaarr
encapsular valores diferentes, debe reemplazarlo con una matriz diferente que contenga los valores deseados. Si se quiere hacer una copiafoo
, se puede dar a la copia una referenciaarr
o una nueva matriz que contenga los valores {1,2,3}, lo que sea más conveniente.La única referencia, en cualquier parte del universo, a una matriz que encapsula los valores 5, 7 y 9. conjunto de tres ubicaciones de almacenamiento que actualmente contienen los valores 5, 7 y 9; si
foo
quiere que encapsule los valores 5, 8 y 9, puede cambiar el segundo elemento de esa matriz o crear una nueva matriz que contenga los valores 5, 8 y 9 y abandonar el anterior. Tenga en cuenta que si se desea hacer una copiafoo
, se debe reemplazar en la copiaarr
con una referencia a una nueva matriz parafoo.arr
poder permanecer como la única referencia a esa matriz en cualquier parte del universo.Una referencia a una matriz que es propiedad de otro objeto al que la ha expuesto
foo
por alguna razón (por ejemplo, tal vez quierafoo
almacenar algunos datos allí). En este escenario,arr
no encapsula el contenido de la matriz, sino su identidad . Debido a que reemplazararr
con una referencia a una nueva matriz cambiaría totalmente su significado, una copia defoo
debería contener una referencia a la misma matriz.Una referencia a una matriz de la cual
foo
es el único propietario, pero a la cual las referencias son retenidas por otro objeto por alguna razón (por ejemplo, quiere tener el otro objeto para almacenar datos allí, el otro lado del caso anterior). En este escenario,arr
encapsula tanto la identidad de la matriz como sus contenidos. Reemplazararr
con una referencia a una nueva matriz cambiaría totalmente su significado, pero tener unaarr
referencia de clonfoo.arr
violaría la suposición de quefoo
es el único propietario. Por lo tanto, no hay forma de copiarfoo
.En teoría,
int[]
debería ser un buen tipo simple y bien definido, pero tiene cuatro significados muy diferentes. Por el contrario, una referencia a un objeto inmutable (por ejemploString
) generalmente solo tiene un significado. Gran parte del "poder" de los objetos inmutables se deriva de ese hecho.fuente
Las instancias mutables se pasan por referencia.
Las instancias inmutables se pasan por valor.
Ejemplo abstracto. Supongamos que existe un archivo llamado txtfile en mi HDD. Ahora, cuando me pidas txtfile , puedo devolverlo en dos modos:
En el primer modo, devuelto txtfile es un archivo mutable, porque cuando haces cambios en el archivo de acceso directo, también haces cambios en el archivo original. La ventaja de este modo es que cada acceso directo devuelto requiere menos memoria (en RAM o en HDD) y la desventaja es que todos (no solo yo, el propietario) tenemos permisos para modificar el contenido del archivo.
En el segundo modo, txtfile devuelto es un archivo inmutable, porque todos los cambios en el archivo recibido no se refieren al archivo original. La ventaja de este modo es que solo yo (el propietario) puede modificar el archivo original y la desventaja es que cada copia devuelta requiere memoria (en RAM o en HDD).
fuente
Si devuelve referencias de una matriz o cadena, el mundo exterior puede modificar el contenido de ese objeto y, por lo tanto, hacerlo como objeto mutable (modificable).
fuente
Inmutable significa que no se puede cambiar, y mutable significa que puede cambiar.
Los objetos son diferentes a los primitivos en Java. Las primitivas se construyen en tipos (booleanos, int, etc.) y los objetos (clases) son tipos creados por el usuario.
Las primitivas y los objetos pueden ser mutables o inmutables cuando se definen como variables miembro dentro de la implementación de una clase.
Mucha gente piensa que las primitivas y las variables de objeto que tienen un modificador final frente a ellas son inmutables, sin embargo, esto no es exactamente cierto. Así que final casi no significa inmutable para las variables. Vea el ejemplo aquí
http://www.siteconsortium.com/h/D0000F.php .
fuente
Immutable object
- es un estado de objeto del cual no se puede cambiar después de la creación. El objeto es inmutable cuando todos sus campos son inmutablesA salvo de amenazas
La principal ventaja del objeto inmutable es que es un entorno natural para concurrente. El mayor problema en la concurrencia es
shared resource
que se puede cambiar cualquiera de los hilos. Pero si un objeto es inmutable, esread-only
la operación segura para subprocesos. Cualquier modificación de un objeto inmutable original devuelve una copiasin efectos secundarios
Como desarrollador, está completamente seguro de que el estado del objeto inmutable no se puede cambiar desde ningún lugar (a propósito o no)
optimización de compilación
Mejorar el rendimiento
Desventaja:
Copiar un objeto es una operación más pesada que cambiar un objeto mutable, es por eso que tiene una huella de rendimiento
Para crear un
immutable
objeto debes usar:Nivel de idioma. Cada idioma contiene herramientas para ayudarlo. Por ejemplo, Java tiene
final
yprimitives
, Swift tienelet
ystruct
[Acerca de] . El lenguaje define un tipo de variable. Por ejemplo, Java tieneprimitive
yreference
escribe, Swift tienevalue
yreference
escribe [Acerca de] . Para los objetos inmutables es más conveniente escribirprimitives
yvalue
escribir una copia de forma predeterminada. En cuanto alreference
tipo, es más difícil (porque puedes cambiar el estado del objeto) pero es posible. Por ejemplo, puede usar elclone
patrón en un nivel de desarrolladorNivel de desarrollador. Como desarrollador, no debe proporcionar una interfaz para cambiar de estado
fuente