2011-01-21 20 views
20

¿Debo hacer que mis clases sean inmutables siempre que sea posible?¿Los objetos inmutables son una buena práctica?

Una vez leí el libro "Effective Java" de Joshua Bloch y me recomendó que todos los objetos comerciales fueran inmutables por varias razones. (por ejemplo, seguridad de la rosca) ¿Esto también se aplica a C#?

¿Tratas de hacer que tus objetos sean inmutables, por lo que tienes menos problemas cuando trabajas con ellos? ¿O no vale la pena la inconveniencia que tiene para crearlos?

+0

Lamentablemente, la respuesta a esta pregunta es "depende". ¿De qué depende? Una gran cantidad de factores, la mayoría de los cuales son locales para su proyecto y no de naturaleza general. –

+0

El contexto es la clave. –

Respuesta

20

El inmutable Eric Lippert ha escrito un whole series of blog posts sobre el tema. La primera parte es here.

Citando del earlier post que vincula a:

LADO: estructuras de datos inmutables son el camino del futuro en C#. Es mucho más fácil razonar sobre una estructura de datos si sabes que nunca cambiará. Dado que no se pueden modificar, son automáticamente seguros para el hilo. Como no se pueden modificar, puede mantener una pila de "instantáneas" pasadas de la estructura, y de repente las implementaciones de deshacer-rehacer se vuelven triviales. En el lado negativo, tienden a masticar la memoria, pero bueno, para eso se inventó la recolección de basura, así que no te preocupes.

+1

Hm ... ¿Es inmutable como rasgo personal algo positivo o negativo? ;) – sisve

+8

Dudo mucho que Eric sea inmutable. Por un lado, estoy bastante seguro de que se pueden observar cambios en la edad. –

+3

Solo si uno considera la proyección tridimensional de su yo cuatridimensional en el tiempo actual. – Thomas

4

Esto va a ser más de una respuesta de tipo opinión, pero ...

Me parece que la facilidad de comprensión de un programa, es decir, el mantenimiento y la depuración de dicha solicitud, es inversamente proporcional a la cantidad de stateful transiciones que ocurren durante el procesamiento de cada componente. Mientras menos estado tenga que cargar en mi cabeza, más atención puedo prestar a la lógica dentro de los algoritmos tal como está escrita.

1

Los objetos inmutables son la característica central de la programación funcional; tiene sus propias ventajas y desventajas. (Por ejemplo, las listas enlazadas son prácticamente imposibles de ser inmutables, pero los objetos inmutables hacen que el paralelismo sea pan comido). Así que, como se señaló un comentario en su publicación, la respuesta es "depende".

+0

Las listas de enlaces individuales son bastante triviales para hacerlas inmutables. Listas doblemente enlazadas, por otro lado ... –

1

Por la parte superior de mi cabeza, no puedo pensar en una razón para objetos inmutables haciendo que el código de seguridad sea de alguna manera "mejor".

Si quiero que un objeto sea seguro para hilos, le pondré un candado o haré una copia y actualizaré la referencia una vez que haya terminado de trabajar en ella. Normalmente no querría un objeto nuevo por cada pequeño cambio.

Para mí, las cadenas inmutables crean más dolores de cabeza para el enhebrado de lo que ayuda.

De hecho, me salí de mi camino para hacer un "ToUpper" "in-place" utilizando el código inseguro isntead del construido en String.ToUpper(). Funciona aproximadamente 4 veces más rápido y consume 1/2 memoria pico.

+0

_Has puesto un código de bloqueo para asegurar que es seguro para subprocesos, pero ¿puedes garantizar que todos los demás desarrolladores de tu equipo harán lo mismo? ¿También puede garantizar que no olvidará un bloqueo alrededor de un objeto que puede compartirse entre hilos? ¿Puedes predecir todos los objetos que puedan ser compartidos por hilos? Solo porque no puedas ver un problema no significa que no exista. –

0

Otro buen beneficio de las estructuras inmutables es que puede almacenar instancias de ellas en caché localmente y reutilizarlas en varios subprocesos sin temor a comportamientos inesperados, como sería el caso si fueran mutables.

Por ejemplo, supongamos que está utilizando un servicio de caché externo como memcached o Velocity o algún otro servicio de tablas distribuidas igualmente simplista. Podrías usar la biblioteca cliente de C# y llamarla suficientemente buena. Sin embargo, eso es un desperdicio de recursos dado un contexto efímero como un escenario de solicitud web.Lo que realmente desea es extraer cada objeto del caché una vez y solo una vez en su contexto.

La forma más segura de realizar este trabajo es colocar una tabla hash local en su proceso frente al proveedor de la memoria caché. En la primera solicitud de la clave de caché, debe desplegar la secuencia de bytes serializados que representa el objeto que desea usar y almacenar esa secuencia de bytes en su tabla hash local. En solicitudes posteriores para la misma clave de caché, simplemente busque la secuencia de bytes en la tabla hash local y deserialice el objeto a una nueva instancia para cada solicitud. Esto es para evitar múltiples viajes redundantes al nodo del servidor de caché para la misma información que supuestamente no ha cambiado durante la vida útil de su contexto.

Con estructuras inmutables, puede deserializar la secuencia de bytes solo una vez en la primera solicitud y salirse con la tarea de almacenar la instancia deserializada en la tabla hash en lugar de la secuencia de bytes y simplemente compartir esa única instancia inmutable de su objeto. Obviamente, esto reduce las penalizaciones de deserialización que pueden sumarse rápidamente si el código de consumo está escrito de tal manera que no importa cuántas llamadas haga al proveedor de almacenamiento en caché, suponiendo que el caché es más rápido que consultar el almacén de datos subyacente.

Quizás esta sea una respuesta más subjetiva, pero es un problema específico que se puede resolver de manera única mediante el uso de estructuras inmutables, así que pensé que era relevante compartir.

+0

La volatilidad de sus datos en caché es una preocupación aquí, pero se supone que su "contexto" o "unidad de trabajo" es de tan corta vida que no tiene sentido volver a consultar constantemente sus datos desde su caché nodos o su almacén de datos subyacente. Tirarlo una vez debería ser suficiente para mantener una visión coherente de los datos para la corta vida útil de la unidad de trabajo que se debe realizar para atender una única solicitud, por ejemplo. Sin embargo, codificar la lógica de su negocio para garantizar que cada dato sea consultado solo una vez es un problema difícil de resolver y complica su lógica con la mecánica. –

Cuestiones relacionadas