language-agnostic encapsulation

language agnostic - Miembros privados vs. miembros públicos en la práctica(¿qué tan importante es la encapsulación?)



language-agnostic encapsulation (22)

Adapte la herramienta al trabajo ... Hace poco vi un código como este en mi base de código actual:

private static class SomeSmallDataStructure { public int someField; public String someOtherField; }

Y luego esta clase se usó internamente para pasar fácilmente varios valores de datos. No siempre tiene sentido, pero si solo tiene DATA, sin métodos, y no lo está exponiendo a los clientes, creo que es un patrón bastante útil.

El uso más reciente que tuve de esto fue una página JSP donde tenía una tabla de datos que se muestra, definida en la parte superior declarativamente. Entonces, inicialmente estaba en matrices múltiples, una matriz por campo de datos ... esto terminó en el código que era bastante difícil de recorrer, con campos que no estaban uno al lado del otro en la definición que se mostrarían juntos ... así que creé un simple clase como la anterior que lo uniría ... el resultado fue un código REALMENTE legible, mucho más que antes.

Moraleja ... a veces deberías considerar alternativas "aceptadas malas" si pueden hacer que el código sea más simple y fácil de leer, siempre y cuando lo pienses detenidamente y consideres las consecuencias ... no aceptes ciegamente TODO lo que escuches.

Dicho esto ... getters y setters públicos es prácticamente equivalente a los campos públicos ... al menos esencialmente (hay un poco más de flexibilidad, pero sigue siendo un mal patrón para aplicar a TODOS los campos que tenga).

Incluso las bibliotecas estándar de Java tienen algunos casos de campos públicos .

Una de las mayores ventajas de la programación orientada a objetos es la encapsulación, y una de las "verdades" que hemos aprendido (o, al menos, me han enseñado) es que los miembros siempre deben hacerse privados y disponibles a través del acceso y el mutador. métodos, lo que garantiza la capacidad de verificar y validar los cambios.

Sin embargo, siento curiosidad por la importancia que esto tiene en la práctica. En particular, si tiene un miembro más complicado (como una colección), puede ser muy tentador simplemente publicitarlo en lugar de crear varios métodos para obtener las claves de la colección, agregar / eliminar elementos de la colección, etc.

¿Sigues la regla en general? ¿Su respuesta cambia dependiendo de si es un código escrito para usted o para ser usado por otros? ¿Hay razones más sutiles que me estoy perdiendo para esta ofuscación?


Baso mi decisión en la profundidad del Código dentro de un módulo. Si estoy escribiendo código que es interno a un módulo, y no interactúa con el mundo exterior, no encapsulo tanto las cosas en privado porque esto afecta el rendimiento de mi programador (qué tan rápido puedo escribir y reescribir mi código).

Pero para los objetos que sirven como interfaz del módulo con el código de usuario, entonces cumplo estrictos patrones de privacidad.


C # Properties ''simula'' los campos públicos. Se ve muy bien y la sintaxis realmente se acelera creando esos métodos get / set


Ciertamente hace la diferencia si escribe código interno o código para ser utilizado por otra persona (o incluso por usted mismo, pero como una unidad contenida). Cualquier código que se va a usar externamente debe tener una interfaz bien definida / documentada que usted Querré cambiar lo menos posible.

Para el código interno, dependiendo de la dificultad, puede encontrar que es menos trabajo hacer las cosas de la manera más sencilla ahora, y pagar un poco de penalidad más adelante. Por supuesto, la ley de Murphy asegurará que la ganancia a corto plazo se borrará muchas veces al tener que realizar cambios de gran alcance más adelante en donde necesitó cambiar los elementos internos de una clase que no logró encapsular.


Como alguien tenía que mantener el código de hace muchos años trabajado por muchas personas en el pasado, es muy claro para mí que si un atributo de miembro se hace público, eventualmente se abuse de él. Incluso he escuchado a personas que están en desacuerdo con la idea de acceso y mutadores, ya que aún no está cumpliendo con el propósito de la encapsulación, que es "ocultar el funcionamiento interno de una clase". Obviamente es un tema controvertido, pero mi opinión sería "hacer que cada miembro sea una variable privada, pensar principalmente en lo que tiene que hacer la clase (métodos) en lugar de cómo va a permitir que las personas cambien las variables internas".


Cuando hago que los objetos tengan sentido, son más fáciles de usar y más fáciles de mantener .

Por ejemplo: Person.Hand.Grab (howquick, howmuch);

El truco consiste en no pensar en los miembros como valores simples, sino como objetos en sí mismos.


Depende. Este es uno de esos temas que deben decidirse pragmáticamente.

Supongamos que tengo una clase para representar un punto. Podría tener getters y setters para las coordenadas X e Y, o podría simplemente hacerlas públicas y permitir el acceso de lectura / escritura a los datos. En mi opinión, esto está bien porque la clase está actuando como una estructura glorificada, una recopilación de datos con algunas funciones útiles adjuntas.

Sin embargo, hay muchas circunstancias en las que no desea proporcionar acceso completo a sus datos internos y depende de los métodos proporcionados por la clase para interactuar con el objeto. Un ejemplo sería una solicitud y respuesta HTTP. En este caso, es una mala idea permitir que alguien envíe algo por el cable; debe procesarse y formatearse según los métodos de clase. En este caso, la clase se concibe como un objeto real y no como un simple almacén de datos.

Realmente se reduce a si los verbos (métodos) controlan la estructura o si los datos sí lo hacen.


En la práctica, siempre sigo solo una regla, la regla "no se ajusta a todos".

La encapsulación y su importancia es un producto de su proyecto. ¿Qué objeto accederá a su interfaz? ¿Cómo lo usarán? ¿Importará si tienen derechos de acceso innecesarios para los miembros? esas preguntas y cosas similares que debe hacerse al trabajar en cada implementación del proyecto.


Específicamente para su ejemplo de uso de una colección que usted devolvería, parece posible que la implementación de dicha colección pueda cambiar (a diferencia de las variables de miembros más simples) haciendo que la utilidad de la encapsulación sea más alta.

Habiendo dicho eso, me gusta la forma en que Python lo maneja. Las variables de los miembros son públicas por defecto. Si desea ocultarlos o agregar validación, se proporcionan técnicas, pero se consideran casos especiales.


Hay una preocupación práctica aquí que no está siendo abordada por la mayoría de las respuestas existentes. La encapsulación y la exposición de interfaces limpias y seguras al código externo siempre es excelente, pero es mucho más importante cuando el código que está escribiendo está destinado a ser consumido por una base de "usuario" espacialmente y / o temporalmente grande. Lo que quiero decir es que si planeas que alguien (incluso tú) mantenga el código en el futuro, o si estás escribiendo un módulo que interactuará con el código de más de un puñado de otros desarrolladores, debes pensar mucho más. cuidadosamente que si está escribiendo un código que es único o totalmente escrito por usted.

Honestamente, sé lo que es una práctica de ingeniería de software miserable, pero a menudo haré que todo sea público al principio, lo que hace que las cosas sean ligeramente más rápidas de recordar y escribir, luego agrega la encapsulación como tiene sentido. En la actualidad, las herramientas de refactorización en los IDE más populares hacen que el enfoque que utilice (agregue la encapsulación frente a eliminarlo) sea mucho menos relevante de lo que solía ser.


La encapsulación es importante cuando al menos uno de estos tiene:

  1. Cualquier persona que no sea usted va a usar su clase (o romperán sus invariantes porque no leen la documentación).
  2. Cualquier persona que no lea la documentación va a utilizar su clase (o romperán sus invariantes cuidadosamente documentadas). Tenga en cuenta que esta categoría incluye usted-dos años a partir de ahora.
  3. En algún momento en el futuro, alguien va a heredar de su clase (porque tal vez sea necesario tomar una acción adicional cuando cambie el valor de un campo, por lo que tiene que haber un sistema).

Si es solo para mí, y lo utilizo en pocos lugares, y no voy a heredar de él, y el cambio de campos no invalidará ninguna invariante que la clase asuma, solo entonces ocasionalmente haré público un campo.


Mi tendencia es intentar que todo sea privado si es posible. Esto mantiene los límites de los objetos lo más claramente definidos posible y mantiene los objetos lo más desacoplados posible. Me gusta porque cuando tengo que reescribir un objeto que fallé en la primera (¿segunda, quinta?) Vez, mantiene el daño contenido en un número menor de objetos.

Si acoplas los objetos lo suficientemente bien, puede ser más sencillo combinarlos en un solo objeto. Si relaja las restricciones de acoplamiento lo suficiente, vuelve a la programación estructurada.

Puede ser que si encuentra que muchos de sus objetos son solo funciones accesorias, debe reconsiderar las divisiones de objetos. Si no está haciendo ninguna acción sobre esos datos, puede pertenecer como parte de otro objeto.

Por supuesto, si está escribiendo algo así como una biblioteca, quiere una interfaz lo más clara y nítida posible para que otros puedan programar en contra de ella.


Sí, la encapsulación importa. Exponer la implementación subyacente hace (al menos) dos cosas incorrectas:

  1. Mezcla las responsabilidades. Las personas que llaman no deberían necesitar o querer comprender la implementación subyacente. Solo deberían querer que la clase haga su trabajo. Al exponer la implementación subyacente, su clase no está haciendo su trabajo. En cambio, solo está cargando la responsabilidad sobre la persona que llama.
  2. Lo ata a la implementación subyacente. Una vez que expone la implementación subyacente, está ligado a ella. Si le dice a las personas que llaman, por ejemplo, hay una colección debajo, no puede cambiar fácilmente la colección para una nueva implementación.

Estos (y otros) problemas se aplican independientemente de si da acceso directo a la implementación subyacente o simplemente duplica todos los métodos subyacentes. Debería estar exponiendo la implementación necesaria, y nada más. Mantener la implementación privada hace que el sistema en general sea más fácil de mantener.


Se trata de controlar lo que las personas pueden hacer con lo que les das. Cuanto más controlas, más suposiciones puedes hacer.

Además, teóricamente puede cambiar la implementación subyacente o algo así, pero ya que en su mayor parte es:

private Foo foo; public Foo getFoo() {} public void setFoo(Foo foo) {}

Es un poco difícil de justificar.


Sigo las reglas sobre esto casi todo el tiempo. Hay cuatro escenarios para mí: básicamente, la regla en sí misma y varias excepciones (todas con influencia de Java):

  1. Utilizable por cualquier cosa fuera de la clase actual, accesible a través de getters / setters
  2. El uso interno a la clase suele estar precedido por ''esto'' para dejar en claro que no es un parámetro de método
  3. Algo destinado a mantenerse extremadamente pequeño, como un objeto de transporte, básicamente una toma directa de atributos; todo público
  4. Necesario para ser no privado para la extensión de algún tipo

Tiendo a seguir la regla bastante estrictamente, incluso cuando es solo mi propio código. Realmente me gustan las propiedades en C # por esa razón. Hace que sea realmente fácil controlar qué valores se le otorgan, pero aún así puede usarlos como variables. O haga que el conjunto sea privado y público, etc.


Yo diría que esta pregunta confunde el concepto de encapsulación con ''ocultar información''
(Esto no es crítico, ya que parece coincidir con una interpretación común de la noción de ''encapsulación'')

Sin embargo, para mí, ''encapsulación'' es:

  • el proceso de reagrupación de varios elementos en un contenedor
  • el contenedor mismo reagrupando los artículos

Supongamos que está diseñando un sistema de contribuyentes. Para cada contribuyente, puede encapsular la noción de niño en

  • una lista de niños que representan a los niños
  • un mapa de a tiene en cuenta a los niños de diferentes padres
  • un objeto Children (not Child) que proporcionaría la información necesaria (como el número total de niños)

Aquí tiene tres tipos diferentes de encapsulaciones, 2 representadas por contenedor de bajo nivel (lista o mapa), una representada por un objeto.

Al tomar esas decisiones, no lo haces

  • hacer que la encapsulación sea pública o protegida o privada: esa opción de ''ocultar información'' aún está por hacerse
  • hacer una abstracción completa (necesita refinar los atributos del objeto Children y puede decidir crear un objeto Child, que mantendría solo las informaciones relevantes desde el punto de vista de un sistema de contribuyentes)
    La abstracción es el proceso de elegir qué atributos del objeto son relevantes para su sistema y cuáles deben ignorarse por completo.

Entonces mi punto es:
Esa pregunta puede haber sido titulada:
Miembros privados vs. miembros públicos en la práctica (¿cuán importante es ocultar la información ?)

Solo mis 2 centavos, sin embargo. Respeto perfectamente que uno pueda considerar la encapsulación como un proceso que incluye la decisión de "ocultar información".

Sin embargo, siempre trato de diferenciar ''abstracción'' - ''encapsulación'' - ''ocultación de información o visibilidad''.


@VonC

Es posible que encuentre una lectura interesante en la Organización Internacional para la Estandarización, "Modelo de Referencia de Procesamiento Distribuido Abierto". Define: "Encapsulación: la propiedad de que la información contenida en un objeto es accesible solo a través de interacciones en las interfaces admitidas por el objeto".

Intenté argumentar que el ocultamiento de información es una parte fundamental de esta definición aquí: http://www.edmundkirwan.com/encap/s2.html

Saludos,

Ed.


Encuentro que un montón de getters y setters son un código que huele que la estructura del programa no está bien diseñada. Debería mirar el código que usa esos getters y setters, y buscar funcionalidades que realmente deberían ser parte de la clase. En la mayoría de los casos, los campos de una clase deben ser detalles privados de implementación y solo los métodos de esa clase pueden manipularlos.

Tener tanto getters como setters equivale a que el campo sea público (cuando los getters y setters son triviales / generados automáticamente). A veces puede ser mejor simplemente declarar los campos como públicos, para que el código sea más simple, a menos que necesite polimorfismo o un marco requiera métodos get / set (y no puede cambiar el marco).

Pero también hay casos en los que tener getters y setters es un buen patrón. Un ejemplo:

Cuando creo la GUI de una aplicación, trato de mantener el comportamiento de la GUI en una clase (FooModel) para que pueda probarse en una unidad fácilmente, y tener la visualización de la GUI en otra clase (FooView) que se puede probar solo de forma manual La vista y el modelo se unen con un simple código de pegamento; cuando el usuario cambia el valor del campo x , la vista llama a setX(String) en el modelo, lo que a su vez puede generar un evento que alguna otra parte del modelo ha cambiado, y la vista obtendrá los valores actualizados del modelo con getters.

En un proyecto, hay un modelo de GUI que tiene 15 getters y setters, de los cuales solo 3 métodos get son triviales (de modo que el IDE podría generarlos). Todos los demás contienen alguna funcionalidad o expresiones no triviales, como las siguientes:

public boolean isEmployeeStatusEnabled() { return pinCodeValidation.equals(PinCodeValidation.VALID); } public EmployeeStatus getEmployeeStatus() { Employee employee; if (isEmployeeStatusEnabled() && (employee = getSelectedEmployee()) != null) { return employee.getStatus(); } return null; } public void setEmployeeStatus(EmployeeStatus status) { getSelectedEmployee().changeStatusTo(status, getPinCode()); fireComponentStateChanged(); }


Tenga en cuenta la semántica de invocar métodos en un objeto. Una invocación a un método es una abstracción de muy alto nivel que se puede implementar desde el compilador o el sistema de tiempo de ejecución de diferentes maneras.

Si el objeto cuyo método está invocando existe en el mismo mapa de proceso / memoria, un compilador o VM podría optimizar un método para acceder directamente al miembro de datos. Por otro lado, si el objeto vive en otro nodo en un sistema distribuido, entonces no hay manera de que pueda acceder directamente a sus miembros de datos internos, pero aún puede invocar sus métodos para enviarle un mensaje.

Al codificar en las interfaces, puede escribir código que no se preocupa de dónde existe el objeto de destino o cómo se invocan sus métodos o incluso si está escrito en el mismo idioma.

En su ejemplo de un objeto que implementa todos los métodos de una colección, entonces seguramente ese objeto en realidad es una colección. así que tal vez este sería un caso en el que la herencia sería mejor que la encapsulación.


Prefiero mantener a los miembros privados el mayor tiempo posible y solo acceder a ellos a través de getters, incluso desde la misma clase. También trato de evitar setters como un primer borrador para promover objetos de estilo de valor, siempre que sea posible. Trabajando mucho con la inyección de dependencia, a menudo tiene instaladores pero no obtiene, ya que los clientes deberían poder configurar el objeto, pero (otros) no llegan a saber qué está configurado de forma real, ya que este es un detalle de implementación.

Saludos, Ollie


Básicamente, ocultar la información se trata de la claridad del código. Está diseñado para que sea más fácil para otras personas extender su código y evitar que accidentalmente creen errores cuando trabajan con los datos internos de sus clases. Se basa en el principio de que nadie lee comentarios , especialmente los que tienen instrucciones.

Ejemplo: estoy escribiendo código que actualiza una variable, y tengo que estar absolutamente seguro de que la Gui cambia para reflejar el cambio, la forma más fácil es agregar un método de acceso (también conocido como "Setter"), que se llama en lugar de la actualización de los datos se actualiza.

Si publico esos datos, y algo cambia la variable sin pasar por el método Setter (y esto ocurre cada vez que pronuncias una palabra), entonces alguien tendrá que pasar una hora depurando para descubrir por qué las actualizaciones no se muestran. Lo mismo se aplica, en menor medida, a los datos "Getting". Podría poner un comentario en el archivo de encabezado, pero es probable que nadie lo lea hasta que algo vaya terriblemente mal. Hacerlo de forma privada significa que no se puede cometer el error, ya que se mostrará como un error en tiempo de compilación fácilmente localizado, en lugar de un error en tiempo de ejecución.

Por experiencia, los únicos momentos en los que desearía que una variable miembro sea pública, y dejar de lado los métodos Getter y Setter, es si desea dejar absolutamente en claro que cambiarlos no tendrá efectos secundarios; especialmente si la estructura de datos es simple, como una clase que simplemente contiene dos variables como un par.

Esto debería ser una ocurrencia bastante rara, ya que normalmente querría efectos secundarios, y si la estructura de datos que está creando es tan simple que no lo hace (por ejemplo, un emparejamiento), ya habrá uno escrito más eficientemente disponible. en una biblioteca estándar.

Dicho esto, para la mayoría de los programas pequeños que no usan extensiones de un solo uso, como los que obtienes en la universidad, es más una "buena práctica" que cualquier otra cosa, porque recordarás durante el proceso de escribirlos, y entonces tú " llévalos y nunca vuelvas a tocar el código. Además, si está escribiendo una estructura de datos como forma de averiguar cómo almacenan datos en lugar de como código de publicación, entonces hay un buen argumento de que Getters and Setters no ayudará, y se interpondrá en el camino de la experiencia de aprendizaje. .

Solo cuando llegue al lugar de trabajo o un gran proyecto, donde la probabilidad es que su código sea llamado por objetos y estructuras escritas por diferentes personas, es vital hacer que estos "recordatorios" sean fuertes. Si es o no un proyecto de un solo hombre es sorprendentemente irrelevante, por la sencilla razón de que "dentro de seis semanas" es una persona tan diferente como un compañero de trabajo. Y "yo hace seis semanas" a menudo resulta ser flojo.

Un último punto es que algunas personas son muy entusiastas sobre la ocultación de información y se molestarán si sus datos son innecesariamente públicos. Lo mejor es complacerlos.