ventajas que principios objetos mutables inmutables inmutabilidad ejemplos dios derecho clases java oop

java - que - Teniendo en cuenta la encapsulación de objetos, ¿deberían los getters devolver una propiedad inmutable?



que es inmutabilidad en derecho (11)

Cuando un getter devuelve una propiedad, como devolver una List de otros objetos relacionados, ¿esa lista y sus objetos deben ser inmutables para evitar el código fuera de la clase, cambiando el estado de esos objetos, sin que el principal objeto padre lo sepa?

Por ejemplo, si un objeto de Contact tiene un getDetails getter, que devuelve una List de objetos ContactDetails , entonces cualquier código que llame a ese getter:

  1. puede eliminar objetos ContactDetail de esa lista sin que el objeto Contact sepa.
  2. puede cambiar cada objeto ContactDetail sin que el objeto Contact conozca.

Entonces, ¿qué deberíamos hacer aquí? ¿Deberíamos confiar en el código de llamada y devolver objetos fácilmente modificables, o hacer lo difícil y hacer una clase inmutable para cada clase mutable?


Creo que descubrirá que es muy raro que cada gettable sea inmutable.

Lo que podría hacer es disparar eventos cuando se cambia una propiedad dentro de tales objetos. No es una solución perfecta tampoco.

La documentación es probablemente la solución más pragmática;)


Cuando estaba comenzando todavía estaba bajo la influencia de HIDE YOUR DATA OO PRINCIPALS LOL. Me sentaba y reflexionaba sobre lo que sucedería si alguien cambiara el estado de uno de los objetos expuestos por una propiedad. ¿Debo hacer que lean solo para personas que llaman desde el exterior? ¿No debería exponerlos para nada?

Las colecciones sacaron estas ansiedades al extremo. Quiero decir, ¡alguien podría quitar todos los objetos de la colección mientras no miro!

Eventualmente me di cuenta de que si los objetos tienen dependencias tan apretadas en sus propiedades externas visibles y sus tipos que, si alguien los toca en un lugar malo, se dispara, su arquitectura es defectuosa.

Existen razones válidas para hacer que sus propiedades externas sean de solo lectura y sus tipos inmutables. Pero ese es el caso de la esquina, no el típico, imho.


Depende del contexto, realmente. Pero, en general, sí, uno debe escribir como código defensivo como sea posible (devolviendo copias de matriz , devolviendo envoltorios de solo lectura alrededor de colecciones , etc.). En cualquier caso, debe estar claramente documentado .


Depende del contexto. Si la lista está destinada a ser mutable, no tiene sentido agilizar la API de la clase principal con métodos para modificarla cuando List tiene una API perfectamente buena propia.

Sin embargo, si la clase principal no puede hacer frente a las mutaciones, tendrá que devolver una lista inmutable, y las entradas de la lista también pueden necesitar ser inmutables.

Sin embargo, no olvide que puede devolver una implementación de Lista personalizada que sepa cómo responder de forma segura a las solicitudes de mutación, ya sea mediante el disparo de eventos o mediante la realización de cualquier acción requerida directamente. De hecho, este es un ejemplo clásico de un buen momento para usar una clase interna.


En el caso particular de una Colección, Lista, Conjunto o Mapa en Java, es fácil devolver una vista inmutable a la clase usando return Collections.unmodifiableList(list);

Por supuesto, si es posible que los datos de respaldo sigan siendo modificados, entonces necesita hacer una copia completa de la lista.


Es una cuestión de si debe estar "a la defensiva" en su código. Si usted es el (único) usuario de su clase y confía en usted mismo, entonces, por supuesto, no necesita la inmutabilidad. Sin embargo, si este código necesita funcionar sin importar qué, o si no confía en su usuario, haga que todo lo que se externalice sea inmutable.

Dicho eso, la mayoría de las propiedades que creo son mutables. Un usuario ocasional estropea esto, pero nuevamente es su culpa, ya que está claramente documentado que la mutación no debería ocurrir a través de objetos mutables recibidos a través de getters.


Si tiene el control del código de llamada, entonces lo más importante es que la elección que haga esté bien documentada en todos los lugares correctos.


Solía ​​devolver una versión de solo lectura de la lista, o al menos, una copia. Pero cada objeto contenido en la lista debe ser editable, a menos que sean inmutables por diseño.


Su primer imperativo debe ser seguir la Ley de Demeter o ''Diga, no pregunte''; decirle a la instancia del objeto qué hacer, por ejemplo

contact.print( printer ) ; // or contact.show( new Dialog() ) ; // or contactList.findByName( searchName ).print( printer ) ;

El código orientado a objetos le dice a los objetos que hagan cosas. El código de procedimiento obtiene información y luego actúa sobre esa información. Pedirle a un objeto que revele los detalles de su interior rompe la encapsulación, es un código de procedimiento, no es una programación de OO sonora y como Will ya ha dicho que es un diseño defectuoso.

Si sigue el enfoque de la Ley de Demeter, cualquier cambio en el estado de un objeto se produce a través de su interfaz definida, por lo tanto, los efectos secundarios son conocidos y controlados. Tu problema desaparece


Primero que nada, los setters y getters son una indicación de mal OO. Generalmente, la idea de OO es pedirle al objeto que haga algo por usted. Establecer y obtener es lo opuesto. Sun debería haberse dado cuenta de alguna otra forma de implementar beans Java para que la gente no recogiera este patrón y pensara que era "Correcto".

En segundo lugar, cada objeto que tenga debe ser un mundo en sí mismo; en general, si va a usar setters y getters, debe devolver objetos independientes bastante seguros. Esos objetos pueden o no ser inmutables porque son solo objetos de primera clase. La otra posibilidad es que devuelvan tipos nativos que son siempre inmutables. Por lo tanto, decir "si setters y getters devuelven algo inmutable" no tiene demasiado sentido.

En cuanto a hacer objetos inmutables, virtualmente siempre debes hacer que los miembros dentro de tu objeto sean definitivos a menos que tengas una razón fuerte para no hacerlo (Final debería haber sido el predeterminado, "mutable" debería ser una palabra clave que anule ese valor predeterminado). Esto implica que siempre que sea posible, los objetos serán inmutables.

En cuanto a cosas cuasi-objetos predefinidas que podrías pasar, te recomiendo que envuelvas cosas como colecciones y grupos de valores que van juntos en sus propias clases con sus propios métodos. Prácticamente nunca paso una colección desprotegida simplemente porque no está dando ninguna guía / ayuda sobre cómo se usa cuando el uso de un objeto bien diseñado debería ser obvio. La seguridad también es un factor, ya que permitir que alguien acceda a una colección dentro de su clase hace que sea casi imposible garantizar que la clase siempre sea válida.


Joshua Bloch en su excelente libro "Effective Java" dice que SIEMPRE debe hacer copias defensivas cuando devuelve algo como esto. Eso puede ser un poco extremo, especialmente si los objetos ContactDetails no son clonables, pero siempre es la manera más segura. En caso de duda, siempre favorezca la seguridad del código sobre el rendimiento, a menos que los perfiles hayan demostrado que la clonación es un cuello de botella de rendimiento real.

En realidad, hay varios niveles de protección que puede agregar. Simplemente puede devolver el miembro, que esencialmente está dando a cualquier otra clase acceso a las partes internas de su clase. Muy inseguro, pero en justicia, ampliamente hecho. También le causará problemas más adelante si desea cambiar las partes internas para que los detalles de contacto se almacenen en un conjunto. Puede devolver una lista recién creada con referencias a los mismos objetos en la lista interna. Esto es más seguro: otra clase no puede eliminar o agregar a la lista, pero puede modificar los objetos existentes. En tercer lugar, devuelve una lista recién creada con copias de los objetos ContactDetails. Esa es la manera segura, pero puede ser costosa.

Haría esto de una mejor manera. No devuelva una lista en absoluto; en su lugar, devuelva un iterador sobre una lista . De esta forma, no es necesario crear una nueva lista (List tiene un método para obtener un iterador), pero la clase externa no puede modificar la lista. Todavía puede modificar los elementos, a menos que escriba su propio iterador que clone los elementos según sea necesario. Si luego cambia a usar otra colección internamente, aún puede devolver un iterador, por lo que no se necesitan cambios externos.