Un punto importante aún no mencionado es que el hecho de que el estado de un objeto sea mutable hace posible que la identidad del objeto que encapsula ese estado sea inmutable.
Muchos programas están diseñados para modelar cosas del mundo real que son inherentemente mutables. Suponga que a las 12:51 am, alguna variable AllTrucks
contiene una referencia al objeto # 451, que es la raíz de una estructura de datos que indica qué carga está contenida en todos los camiones de una flota en ese momento (12:51 am), y alguna variable BobsTruck
puede usarse para obtener una referencia al objeto # 24601 que apunta a un objeto que indica qué carga está contenida en el camión de Bob en ese momento (12:51 am). A las 12:52 a.m., algunos camiones (incluido el de Bob) se cargan y descargan, y las estructuras de datos se actualizan para que AllTrucks
ahora contengan una referencia a una estructura de datos que indique que la carga está en todos los camiones a las 12:52 a.m.
¿Qué debería pasar BobsTruck
?
Si la propiedad de 'carga' de cada objeto de camión es inmutable, entonces el objeto # 24601 representará para siempre el estado que tenía el camión de Bob a las 12:51 am. Si BobsTruck
contiene una referencia directa al objeto # 24601, a menos que el código que actualiza AllTrucks
también se actualice BobsTruck
, dejará de representar el estado actual del camión de Bob. Tenga en cuenta además que, a menos que BobsTruck
se almacene en alguna forma de objeto mutable, la única forma en que el código que actualiza AllTrucks
podría actualizarlo sería si el código se programara explícitamente para hacerlo.
Si se quiere poder usar BobsTruck
para observar el estado del camión de Bob mientras se mantienen todos los objetos inmutables, se podría tener BobsTruck
una función inmutable que, dado el valor que AllTrucks
tiene o tuvo en un momento determinado, generará el estado del camión de Bob en ese momento. Uno podría incluso tener un par de funciones inmutables, una de las cuales sería la anterior y la otra aceptaría una referencia a un estado de flota y un nuevo estado de camión, y devolvería una referencia a un nuevo estado de flota que coincidía con el viejo, excepto que la camioneta de Bob tendría el nuevo estado.
Desafortunadamente, tener que usar esa función cada vez que se quiere acceder al estado del camión de Bob podría ser bastante molesto y engorroso. Un enfoque alternativo sería decir que el objeto # 24601 representará siempre y para siempre (siempre y cuando alguien tenga una referencia a él) representará el estado actual del camión de Bob. El código que querrá acceder repetidamente al estado actual del camión de Bob no tendría que ejecutar alguna función que requiera mucho tiempo cada vez; simplemente podría hacer una función de búsqueda una vez para descubrir que el objeto # 24601 es el camión de Bob, y luego simplemente acceder a ese objeto cada vez que quiera ver el estado actual del camión de Bob.
Tenga en cuenta que el enfoque funcional no está exento de ventajas en un entorno de subproceso único, o en un entorno de subprocesos múltiples donde los subprocesos en su mayoría solo estarán observando datos en lugar de cambiarlos. Cualquier hilo de observación que copia la referencia de objeto contenida enAllTrucks
y luego examina los estados de los camiones representados, de este modo verá el estado de todos los camiones a partir del momento en que tomó la referencia. Cada vez que un hilo de observador quiere ver datos más nuevos, simplemente puede volver a tomar la referencia. Por otro lado, tener todo el estado de la flota representado por un solo objeto inmutable impediría la posibilidad de que dos subprocesos actualicen diferentes camiones simultáneamente, ya que cada subproceso si se deja en sus propios dispositivos produciría un nuevo objeto de "estado de flota" que incluía El nuevo estado de su camión y los viejos estados de cada uno. Se puede asegurar la corrección si cada subproceso se utiliza CompareExchange
para actualizar AllTrucks
solo si no ha cambiado y responde a un errorCompareExchange
regenerando su objeto de estado y reintentando la operación, pero si más de un hilo intenta una operación de escritura simultánea, el rendimiento generalmente será peor que si toda la escritura se hiciera en un solo hilo; cuantos más hilos intenten tales operaciones simultáneas, peor será el rendimiento.
Si los objetos individuales del camión son mutables pero tienen identidades inmutables , el escenario de subprocesos múltiples se vuelve más limpio. Solo se puede permitir que un hilo opere a la vez en un camión determinado, pero los hilos que operan en camiones diferentes podrían hacerlo sin interferencia. Si bien hay formas en que uno podría emular tal comportamiento incluso cuando usa objetos inmutables (por ejemplo, uno podría definir los objetos "AllTrucks" de modo que establecer el estado del camión que pertenece a XXX a SSS simplemente requeriría generar un objeto que dijera "A partir de [Tiempo], el estado del camión que pertenece a [XXX] ahora es [SSS]; el estado de todo lo demás es [Valor anterior de AllTrucks] ". Generar un objeto de este tipo sería lo suficientemente rápido que incluso en presencia de contención, unCompareExchange
el bucle no llevaría mucho tiempo. Por otro lado, el uso de dicha estructura de datos aumentaría sustancialmente la cantidad de tiempo requerida para encontrar el camión de una persona en particular. El uso de objetos mutables con identidades inmutables evita ese problema.