Viniendo de un fondo OOP (Java), estoy aprendiendo Scala por mi cuenta. Si bien puedo ver fácilmente las ventajas de usar objetos inmutables individualmente, me cuesta ver cómo se puede diseñar una aplicación completa como esa. Daré un ejemplo:
Digamos que tengo objetos que representan "materiales" y sus propiedades (estoy diseñando un juego, así que realmente tengo ese problema), como el agua y el hielo. Tendría un "gerente" que posee todas esas instancias de materiales. Una propiedad sería el punto de congelación y fusión, y a qué se congela o derrite el material.
[EDITAR] Todas las instancias materiales son "singleton", algo así como una enumeración de Java.
Quiero "agua" para decir que se congela a "hielo" a 0 ° C, y "hielo" para decir que se derrite a "agua" a 1 ° C. Pero si el agua y el hielo son inmutables, no pueden obtener una referencia el uno al otro como parámetros del constructor, porque uno de ellos debe crearse primero, y ese no podría obtener una referencia al otro que aún no existe como parámetro del constructor. Podría resolver esto dándoles a ambos una referencia al gerente para que puedan consultarlo para encontrar la otra instancia material que necesitan cada vez que se les solicite sus propiedades de congelación / fusión, pero luego obtengo el mismo problema entre el gerente y los materiales, que necesitan una referencia entre sí, pero solo se puede proporcionar en el constructor de uno de ellos, por lo que el administrador o el material no pueden ser inmutables.
¿Simplemente no hay forma de evitar este problema, o necesito usar técnicas de programación "funcionales", o algún otro patrón para resolverlo?
fuente
h2o
materialRespuestas:
La solución es hacer trampa un poco. Específicamente:
Cree A, pero deje su referencia a B sin inicializar (ya que B aún no existe).
Crea B y haz que apunte a A.
Actualice A para que apunte a B. No actualice A ni B después de esto.
Esto se puede hacer explícitamente (ejemplo en C ++):
o implícitamente (ejemplo en Haskell):
El ejemplo de Haskell utiliza una evaluación perezosa para lograr la ilusión de valores inmutables mutuamente dependientes. Los valores comienzan como:
a
yb
son formas válidas de cabeza normal independientemente. Cada contra puede construirse sin necesitar el valor final de la otra variable. Cuando se evalúa el thunk, apuntará a los mismosb
puntos de datos .Por lo tanto, si desea que dos valores inmutables se apunten entre sí, debe actualizar el primero después de construir el segundo o utilizar un mecanismo de nivel superior para hacer lo mismo.
En su ejemplo particular, podría expresarlo en Haskell como:
Sin embargo, estoy esquivando el problema. Me imagino que en un enfoque orientado a objetos, donde
setTemperature
se adjunta un método al resultado de cadaMaterial
constructor, tendría que hacer que los constructores se apuntaran entre sí. Si los constructores se tratan como valores inmutables, puede utilizar el enfoque descrito anteriormente.fuente
En su ejemplo, está aplicando una transformación a un objeto, por lo que usaría algo como un
ApplyTransform()
método que devuelve un enBlockBase
lugar de intentar cambiar el objeto actual.Por ejemplo, para cambiar un IceBlock a un WaterBlock aplicando algo de calor, llamaría algo así como
y el
IceBlock.ApplyTemperature()
método se vería así:fuente
BlockList.WaterBlock
lugar de crear un nuevo bloque?BlockList
es solo unastatic
clase que es responsable de las instancias individuales de cada bloque, por lo que no es necesario crear una instancia deBlockList
(Estoy acostumbrado a C #)Otra forma de romper el ciclo es separar las preocupaciones del material y la transmutación, en un lenguaje inventado:
fuente
Si va a usar un lenguaje funcional, y quiere darse cuenta de los beneficios de la inmutabilidad, entonces debe abordar el problema con eso en mente. Está intentando definir un tipo de objeto "hielo" o "agua" que pueda soportar un rango de temperaturas; para soportar la inmutabilidad, entonces necesitaría crear un nuevo objeto cada vez que cambie la temperatura, lo cual es un desperdicio. Así que trate de hacer que los conceptos de tipo de bloque y temperatura sean más independientes. No sé Scala (está en mi lista de aprendizaje :-)), pero tomando prestado de Joey Adams Answer en Haskell , sugiero algo como:
o tal vez:
(Nota: no he intentado ejecutar esto, y mi Haskell está un poco oxidado). Ahora, la lógica de transición está separada del tipo de material, por lo que no desperdicia tanta memoria y (en mi opinión) es bastante un poco más funcionalmente orientado.
fuente