¿Cuál es un problema preciso al permitir getters?

15

No estoy buscando una opinión sobre la semántica, sino simplemente un caso en el que tener captadores utilizados de manera sensata es un impedimento real. Tal vez me arroja a una espiral interminable de confiar en ellos, tal vez la alternativa es más limpia y maneja los captadores automáticamente, etc. Algo concreto.

Escuché todos los argumentos, escuché que son malos porque te obligan a tratar los objetos como fuentes de datos, que violan el "estado puro" de un objeto de "no dar demasiado, pero prepárate para aceptar mucho ".

Pero no hay ninguna razón sensata para explicar por qué getDataes malo, de hecho, algunas personas argumentaron que se trata mucho de la semántica, los captadores como buenos por sí mismos getX, pero para mí no los nombran , esto es al menos divertido .

¿Qué es una cosa, sin opiniones, que se romperá si uso captadores con sensatez y para datos que claramente la integridad del objeto no se rompe si lo saca?

Por supuesto, permitir un captador para una cadena que se usa para cifrar algo es más que tonto, pero estoy hablando de datos que su sistema necesita para funcionar. Tal vez sus datos se extraen de un Providerobjeto, pero, aún así, el objeto aún necesita permitir Providerque se haga $provider[$object]->getData, no hay forma de evitarlo.


Por qué pregunto: Para mí, los captadores, cuando se usan con sensatez y en datos que se tratan como "seguros" son enviados por Dios, el 99% de mis captadores se usan para identificar el objeto, como, en el código Object, what is your name? Object, what is your identifier?, pregunto . cualquiera que trabaje con un objeto debería saber estas cosas acerca de un objeto, porque casi todo lo relacionado con la programación es identidad y, ¿quién más sabe mejor qué es el objeto en sí? Así que no veo ningún problema real a menos que seas un purista.

He examinado todas las preguntas de StackOverflow sobre "por qué los captadores / setters" son malos y, aunque estoy de acuerdo en que los setters son realmente malos en el 99% de los casos, los getters no tienen que ser tratados de la misma manera solo porque riman.

Un setter comprometerá la identidad de su objeto y hará que sea muy difícil depurar quién está cambiando los datos, pero un getter no está haciendo nada.

coolpasta
fuente
2
Mi consejo es olvidar la mayor parte de la teoría OO. Práctica. Escriba mucho código y luego manténgalo. Aprenderá mucho, mucho, mucho más sobre lo que funciona bien y lo que no funciona haciendo algo y volviendo a ello unos meses más tarde.
jpmc26
1
@ jpmc26 ¿Qué pasa con la madre de ... Nunca me di cuenta de que ninguno de nosotros realmente hace POO correctamente, siempre he tenido esta noción de encapsulación estrictamente relacionada con los campos de un objeto, pero el objeto en sí es un estado y está siendo Compartido libremente. Genuinamente, todos están haciendo mal la POO, entonces, o más bien, la POO no es posible si el paradigma son los objetos. Utilizo OOP estrictamente para expresar cómo funciona un sistema y nunca lo traté como el Santo Grial, ni como SÓLIDO. Son solo herramientas que uso principalmente para definir un concepto razonablemente bien expandido (a través de la implementación) de mi concepto. ¿Alguna lectura sobre si esto es correcto?
coolpasta
2
--cont, cuanto más escribo, más me doy cuenta de que la programación se trata de poder abstraer tanto como sea necesario para poder razonar, a otros, y también a usted acerca de su base de código. Por supuesto, el aspecto técnico de esto es asegurarse de hacer las verificaciones adecuadas / establecer las reglas correctas. En verdad, quien logre hacer que otros entiendan mejor su código gana. Asegúrese de que tenga sentido para cualquiera que lo lea, luego asegúrese de que no falle segmentando adecuadamente los objetos con interfaces, comprobaciones y cambie a otros paradigmas cuando sea más fácil razonar con ellos: sea flexible.
coolpasta
2
Esto me parece un hombre de paja. No creo que ningún desarrollador serio que realmente escriba código utilizable pueda argumentar que usar getters de manera sensata es un problema. La pregunta implica que los captadores pueden usarse con sensatez, lo que implica que los captadores pueden ser sensibles. ¿Estás buscando a alguien que diga que no hay un uso razonable de los captadores? Estarás esperando un rato.
JimmyJames
2
@coolpasta Tienes razón. Realmente no me importa si es pura POO. Solo mejor. Dame una opción entre el código roto que puede ser entendido por los humanos frente al código perfecto para la CPU que es indescifrable y tomaré el código amigable para los humanos cada vez. OOP es ÚNICAMENTE útil para mí cuando reduce la cantidad de cosas en las que tengo que pensar en cualquier momento. Esa es la única razón por la que me gusta sobre el procedimiento directo. Sin eso, solo me estás haciendo saltar a través de múltiples archivos de código fuente sin ninguna razón.
candied_orange

Respuestas:

22

No se puede escribir un buen código sin captadores.

La razón por la cual no es porque los captadores no rompen la encapsulación, lo hacen. No es porque los captadores no tienten a las personas a no molestarse en seguir OOP, lo que les haría poner métodos con los datos sobre los que actúan. Ellas hacen. No, necesitas captadores debido a los límites.

Las ideas de encapsulación y métodos de mantenimiento junto con los datos sobre los que actúan simplemente no funcionan cuando te topas con un límite que te impide mover un método y te obliga a mover datos.

Es realmente así de simple. Si usa getters cuando no hay límite, terminará sin objetos reales. Todo comienza a tender a lo procesal. Que funciona tan bien como siempre.

La verdadera OOP no es algo que pueda difundir en todas partes. Solo funciona dentro de esos límites.

Esos límites no son muy delgados. Tienen código en ellos. Ese código no puede ser OOP. No puede ser funcional tampoco. No, este código tiene nuestros ideales despojados de él para que pueda lidiar con la dura realidad.

Michael Fetters llamó a este código fascia después de ese tejido conectivo blanco que mantiene juntas secciones de una naranja.

Esta es una forma maravillosa de pensarlo. Explica por qué está bien tener ambos tipos de código en la misma base de código. Sin esta perspectiva, muchos programadores nuevos se aferran a sus ideales con fuerza, luego se les rompe el corazón y abandonan estos ideales cuando alcanzan su primer límite.

Los ideales solo funcionan en su lugar apropiado. No renuncies a ellos solo porque no funcionan en todas partes. Úsalos donde trabajan. Ese lugar es la parte jugosa que protege la fascia.

Un ejemplo simple de un límite es una colección. Esto contiene algo y no tiene idea de qué es. ¿Cómo podría un diseñador de colección posiblemente mover la funcionalidad de comportamiento del objeto retenido a la colección cuando no tiene idea de lo que va a retener? No puedes Estás contra un límite. Por eso las colecciones tienen captadores.

Ahora, si lo supiera, podría mover ese comportamiento y evitar el estado de movimiento. Cuando lo sepas, deberías. Simplemente no siempre lo sabes.

Algunas personas simplemente llaman a esto ser pragmático. Y es. Pero es bueno saber por qué tenemos que ser pragmáticos.


Usted ha expresado que no quiere escuchar argumentos semánticos y parece estar abogando por poner "captadores sensibles" en todas partes. Estás pidiendo que se desafíe esta idea. Creo que puedo mostrar que la idea tiene problemas con la forma en que la ha enmarcado. Pero también creo que sé de dónde vienes porque he estado allí.

Si quieres captadores en todas partes, mira Python. No hay una palabra clave privada. Sin embargo, Python funciona bien. ¿Cómo? Usan un truco semántico. Nombran todo lo que debe ser privado con un guión bajo destacado. Incluso puede leerlo siempre que asuma la responsabilidad de hacerlo. "Todos somos adultos aquí", dicen a menudo.

Entonces, ¿cuál es la diferencia entre eso y simplemente poner getters en todo en Java o C #? Lo siento pero es semántica. La convención de subrayado de Pythons le indica claramente que está hurgando detrás de la puerta de los empleados. Golpea a los captadores en todo y pierdes esa señal. Con la reflexión, de todos modos, podría haberse despojado de lo privado y aún no haber perdido la señal semántica. Simplemente no hay un argumento estructural que hacer aquí.

Entonces, lo que nos queda es el trabajo de decidir dónde colgar el letrero de "solo empleados". ¿Qué se debe considerar privado? Lo llaman "captadores sensibles". Como he dicho, la mejor justificación para un captador es un límite que nos aleja de nuestros ideales. Eso no debería resultar en captadores de todo. Cuando da como resultado un captador, debe considerar mover el comportamiento aún más hacia la parte jugosa donde puede protegerlo.

Esta separación ha dado lugar a unos pocos términos. Un objeto de transferencia de datos o DTO no tiene comportamiento. Los únicos métodos son getters y algunas veces setters, a veces un constructor. Este nombre es desafortunado porque no es un objeto verdadero en absoluto. Los captadores y establecedores son realmente solo códigos de depuración que le dan un lugar para establecer un punto de interrupción. Si no fuera por esa necesidad, solo serían un montón de campos públicos. En C ++ solíamos llamarlos structs. La única diferencia que tenían de una clase de C ++ era que estaban predeterminados en público.

Los DTO son buenos porque puedes arrojarlos sobre un muro de límites y mantener tus otros métodos de forma segura en un objeto de comportamiento agradable y jugoso. Un verdadero objeto. Sin getters para violar es encapsulación. Mis objetos de comportamiento pueden comer DTO al usarlos como objetos de parámetro . A veces tengo que hacer una copia defensiva para evitar un estado mutable compartido . No esparzo DTOs mutables dentro de la parte jugosa dentro del límite. Los encapsulo. Los escondo Y cuando finalmente me encuentro con un nuevo límite, giro un nuevo DTO y lo tiro sobre la pared, lo que lo convierte en un problema para otra persona.

Pero desea proporcionar captadores que expresen su identidad. Bueno, felicidades, has encontrado un límite. Las entidades tienen una identidad que va más allá de su referencia. Es decir, más allá de su dirección de memoria. Por lo tanto, debe almacenarse en algún lugar. Y algo tiene que ser capaz de referirse a esto por su identidad. Un captador que expresa identidad es perfectamente razonable. Una pila de código que usa ese captador para tomar decisiones que la Entidad podría haber tomado no lo es.

Al final, no es la existencia de captadores lo que está mal. Son mucho mejores que los campos públicos. Lo malo es cuando se usan para fingir que estás orientado a objetos cuando no lo estás. Los captadores son buenos. Ser orientado a objetos es bueno. Los captadores no están orientados a objetos. Use getters para forjar un lugar seguro para orientarse a objetos.

naranja confitada
fuente
Esto es exactamente por lo que pregunté y no estoy seguro de si mi conversación es visible con Robert, también estaba tratando de descubrir un caso específico en el que claramente me duele usar "captadores sensibles". Yo, por mi parte, no estoy de acuerdo con los setters porque al menos para mí, es muy difícil atribuir la propiedad a una acción como esta, solía usar setters en todo y cuando me di cuenta de que incluso 1 setter, cuando se ve afectado por demasiados objetos mata yo ... tuve que reescribir todo. El uso sensible de los captadores, después de pruebas exhaustivas durante tanto tiempo, no tiene inconvenientes si no eres un purista.
coolpasta
1
@coolpasta puede crear objetos propietarios de datos. Los objetos propietarios de datos son objetos de datos, pero están diseñados y nombrados de modo que (claramente) poseen los datos. Por supuesto, estos objetos tendrán captadores.
rwong
1
@rwong ¿Qué quieres decir con clearly? Si los datos me pasan de algo, por valor , claramente, mi objeto ahora posee los datos, pero por semántica, todavía no lo hace, en este punto, simplemente obtuvo los datos de otra persona. Luego debe realizar operaciones para transformar esos datos, haciéndolos suyos, en el momento en que se tocan los datos, debe realizar cambios semánticos: ¿Recibió los datos como $data_from_requesty ahora los operó? Nómbralo $changed_data. ¿Desea exponer estos datos a otros? Cree un captador getChangedRequestData, entonces, la propiedad está claramente establecida. ¿O es eso?
coolpasta
1
"No se puede escribir un buen código sin captadores". Esto es completamente incorrecto, como lo es la noción de que los límites inherentemente necesitan captadores. Esto no es pragmatismo, solo pereza. Es bastante más fácil simplemente arrojar datos sobre la cerca y terminar con eso, pensar en casos de uso y diseñar una buena API de comportamiento es difícil.
Robert Bräutigam
2
Excelente respuesta!
cmaster - reinstalar a monica
10

Getters viola el Principio de Hollywood ("No nos llames, te llamaremos")

El Principio de Hollywood (también conocido como Inversión de control) establece que no se llama al código de la biblioteca para hacer las cosas; más bien, el marco llama a tu código. Debido a que el marco controla las cosas, no es necesario transmitir su estado interno a sus clientes. No necesitas saberlo.

En su forma más insidiosa, violar el Principio de Hollywood significa que está utilizando un captador para obtener información sobre el estado de una clase y luego tomar decisiones sobre qué métodos recurrir a esa clase en función del valor que obtenga. Es violación de la encapsulación en su máxima expresión.

Usar un getter implica que necesitas ese valor, cuando en realidad no lo necesitas .

Es posible que realmente necesite esa mejora de rendimiento

En casos extremos de objetos livianos que deben tener el máximo rendimiento posible, es posible (aunque extremadamente improbable) que no pueda pagar la penalización de rendimiento muy pequeña que impone un getter. Esto no sucederá el 99.9 por ciento de las veces.

Robert Harvey
fuente
1
Entiendo y volaré con tu verdad, que no necesito saber. . Pero he llegado a un lugar donde necesito. Tengo un Generatorobjeto que recorre todos mis Itemsobjetos, luego llama getNamea cada uno Itempara hacer algo más. ¿Cuál es el problema con esto? Luego, a cambio, Generatorescupe cadenas formateadas. Esto está dentro de mi marco, para lo cual tengo una API además que las personas pueden usar para ejecutar lo que proporcionan los usuarios pero sin tocar el marco.
coolpasta
33
What is the issue with this?Ninguno que pueda ver. Eso es esencialmente lo que hace una mapfunción. Pero esa no es la pregunta que hiciste. Básicamente, usted preguntó "¿Hay alguna condición bajo la cual un captador pueda ser desaconsejable"? Respondí con dos, pero eso no significa que abandones a los setters por completo.
Robert Harvey
1
Le estás haciendo esa pregunta al tipo equivocado. Soy pragmatista; Hago lo que mejor se adapta a mis programas específicos, y no pongo mucha importancia en los "principios" a menos que sirvan para mis propósitos.
Robert Harvey
2
@ jpmc26: Marcos. No bibliotecas
Robert Harvey
2
Un marco es un intento de convertir un lenguaje de propósito general en un dominio específico. Una biblioteca es una colección de algoritmos reutilizables. Cualquiera de los dos le pedirá que dependa de ello. Depende de usted si desea codificar ya sea la dependencia o hacer que la dependencia sea fácilmente sustituible.
candied_orange
2

Detalles de implementación de fugas de Getters y abstracción de ruptura

Considerar public int millisecondsSince1970()

Sus clientes pensarán "oh, esto es un int" y habrá un millón de llamadas asumiendo que es un int , haciendo una comparación matemática de fechas enteras, etc. Cuando se dé cuenta de que necesita mucho tiempo , habrá mucho de código heredado con problemas. En un mundo Java, agregarías @deprecateda la API, todos lo ignorarán y estarás atrapado manteniendo código obsoleto y defectuoso. :-( (¡Supongo que otros idiomas son similares!)

En este caso en particular, no estoy seguro de cuál podría ser una mejor opción, y la respuesta @candiedorange es completamente acertada, hay muchas ocasiones en las que necesita captadores, pero este ejemplo ilustra las desventajas. Cada captador público tiende a "encerrarlo" en una determinada implementación. Úselos si realmente necesita "cruzar los límites", pero úselos lo menos posible y con cuidado y previsión.

user949300
fuente
Esto es cierto, pero ¿cuál es la solución de patrón para imponer un tipo / estructura de datos a la variable de un objeto, así como a cualquier cosa que use el captador para esa variable?
coolpasta
1
Este ejemplo ilustra un fenómeno diferente, la obsesión primitiva. Hoy en día, la mayoría de las bibliotecas y marcos tienen clases que representan timepointy timespanrespectivamente. ( epoches un valor particular de timepoint.) En estas clases, proporcionan captadores como getIntor getLongo getDouble. Si las clases que manipulan el tiempo se escriben de manera que (1) deleguen la aritmética relacionada con el tiempo a estos objetos y métodos, y (2) proporcionen acceso a estos objetos de tiempo a través de captadores, entonces el problema se resuelve, sin necesidad de deshacerse de los captadores .
rwong
1
Para resumir, los captadores son parte de la API y, por lo tanto, los captadores deben diseñarse con suficientes consideraciones para todas las consecuencias, incluidas las pasadas, actuales y futuras. Pero eso no lleva a la conclusión de evitar o minimizar el número de captadores. De hecho, si existe una necesidad futura de acceder a una información particular para la que se omitió un captador, habría requerido un cambio de API y un cambio de código en el lado de la biblioteca. Por lo tanto, la decisión de si un getter particular debe implementarse o evitarse debe basarse en el dominio y la intención, no en una razón preventiva.
rwong
1
"millisecondsSince1970" dejó de funcionar para entradas de 32 bits antes de finales de enero de 1970, por lo que dudo que haya mucho código usándolo :-)
gnasher729
1
@rwong Algo correcto, pero usted supone que las bibliotecas preferidas que representan puntos de tiempo son estables. Lo cual no son. Por ejemplo, moment.js
user949300
1

Creo que la primera clave es recordar que los absolutos siempre están mal.

Considere un programa de libreta de direcciones, que simplemente almacena la información de contacto de las personas. Pero, hagámoslo bien y dividámoslo en capas de controlador / servicio / repositorio.

Habrá una Personclase que contiene datos reales: nombre, dirección, número de teléfono, etc. Addressy también Phoneson clases, por lo que podemos usar el polimorfismo más adelante para admitir esquemas no estadounidenses. Las tres clases son objetos de transferencia de datos , por lo que no serán más que getters y setters. Y eso tiene sentido: no van a tener ninguna lógica en ellos más allá de anular equalsy getHashcode; pedirles que le den una representación de la información completa de una persona no tiene sentido: ¿es para una presentación HTML, una aplicación GUI personalizada, una aplicación de consola, un punto final HTTP, etc.?

Ahí es donde intervienen el controlador, el servicio y el repositorio. Todas esas clases serán lógicas y ligeras, gracias a la inyección de dependencia.

Al controlador se le inyectará un servicio, que expondrá métodos como gety save, los cuales tomarán algunos argumentos razonables (por ejemplo, getpodrían tener anulaciones para buscar por nombre, buscar por código postal o simplemente obtener todo; saveprobablemente tomará un solo Persony guardarlo). ¿Qué captadores tendría el controlador? Ser capaz de obtener el nombre de la clase concreta puede ser útil para iniciar sesión, pero ¿qué estado mantendrá además del estado que se le ha inyectado?

Del mismo modo, la capa de servicio recibirá un depósito inyectado, por lo que en realidad puede obtener y persistir Person, y puede tener alguna "lógica de negocios" (por ejemplo, tal vez vamos a requerir que todos los Personobjetos tengan al menos una dirección o teléfono número). Pero, de nuevo: ¿qué estado tiene ese objeto además de lo que se inyecta? De nuevo, ninguno.

La capa de repositorio es donde las cosas se ponen un poco más interesantes: va a establecer una conexión con una ubicación de almacenamiento, por ejemplo. un archivo, una base de datos SQL o un almacén en memoria. Aquí es tentador agregar un captador para obtener el nombre de archivo o la cadena de conexión SQL, pero ese es el estado que se ha inyectado en la clase. ¿El repositorio debe tener un captador para saber si está o no conectado correctamente a su almacén de datos? Bueno, tal vez, pero ¿de qué sirve esa información fuera de la clase del repositorio en sí? La clase del repositorio en sí debería poder intentar volver a conectarse a su almacén de datos si es necesario, lo que sugiere que la isConnectedpropiedad ya tiene un valor dudoso. Además, una isConnectedpropiedad probablemente va a estar equivocada justo cuando más debería ser correcta: verificarisConnectedantes de intentar obtener / almacenar datos no garantiza que el repositorio todavía esté conectado cuando se realiza la llamada "real", por lo que no elimina la necesidad de manejo de excepciones en algún lugar (hay argumentos sobre dónde debería ir eso, más allá de El alcance de esta pregunta).

Considere también las pruebas unitarias (está escribiendo pruebas unitarias, ¿verdad?): El servicio no esperará que se inyecte una clase específica, sino que esperará que se inyecte una implementación concreta de una interfaz. Eso permite que la aplicación en su totalidad cambie el lugar donde se almacenan los datos sin tener que hacer nada más que cambiar el repositorio que se inyecta en el servicio. También permite que el servicio se pruebe unitariamente inyectando un repositorio simulado. Esto significa pensar en términos de interfaces en lugar de clases. ¿La interfaz del repositorio expondría algún getter? Es decir, ¿hay algún estado interno que sería: común a todos los repositorios, útil fuera del repositorio y no inyectado en el repositorio? Sería difícil pensar en mí mismo.

TL; DR

En conclusión: aparte de las clases cuyo único propósito es transportar datos, nada más en la pila tiene un lugar para colocar un getter: el estado se inyecta o es irrelevante fuera de la clase trabajadora.

minnmass
fuente
Encuentro esta línea de pensamiento muy similar al debate sobre el uso adecuado de las propiedades de C #. En resumen, se espera que las propiedades (que pueden ser solo getter o getter-setter-pair) mantengan cierto contrato, como "lo que estableces es lo que obtienes", "getter debería estar libre de efectos secundarios", "getter debería evitar lanzar errores", etc. Contrariamente a su conclusión, hay muchos estados de objeto que son beneficiosos para exponerse como propiedades (o getters). Los ejemplos son demasiado numerosos para citarlos aquí.
rwong
2
@rwong Me interesaría conocer algunos ejemplos, digamos para una aplicación "normal" "empresarial" implementada por un equipo. Supongamos también, por simplicidad, que no hay terceros involucrados. Ya sabes, un sistema autónomo, como un calendario, una tienda de mascotas, lo que quieras.
Robert Bräutigam
1

¿Qué es una cosa, sin opiniones, que se romperá si uso captadores con sensatez y para datos que claramente la integridad del objeto no se rompe si lo saca?

Miembros mutables.

Si tiene una colección de cosas en un objeto que expone a través de un captador, posiblemente se exponga a errores asociados con las cosas incorrectas que se agregan a la colección.

Si tiene un objeto mutable que está exponiendo a través de un captador, potencialmente se está abriendo al estado interno de su objeto que se está cambiando de una manera que no espera.

Un ejemplo realmente malo sería un objeto que cuente de 1 a 100 exponiendo su valor Actual como referencia mutable. Esto permite que un objeto de terceros cambie el valor de Current de tal manera que el valor pueda estar fuera de los límites esperados.

Esto es principalmente un problema con la exposición de objetos mutables. Exponer estructuras u objetos inmutables no es un problema en absoluto, ya que cualquier cambio en ellos cambiará una copia en su lugar (generalmente, ya sea por una copia implícita en el caso de una estructura o por una copia explícita en el caso de un objeto inmutable).

Usar una metáfora que pueda hacer que sea más fácil ver la diferencia.

Una flor tiene una serie de cosas que son únicas al respecto. Tiene una Colory tiene una serie de pétalos (NumPetals). Si estoy observando esa flor, en el mundo real, puedo ver claramente su color. Entonces puedo tomar decisiones basadas en ese color. Por ejemplo, si el color es negro, no le des a la novia, pero si el color es rojo, dale a la novia. En nuestro modelo de objetos, el color de la flor estaría expuesto como captador en el objeto de la flor. Mi observación de ese color es importante para las acciones que realizaré, pero no tiene ningún impacto en el objeto de la flor. No debería poder cambiar el color de esa flor. Igualmente, no tiene sentido ocultar la propiedad de color. Una flor generalmente no puede evitar que las personas observen su color.

Si tiño la flor, debería llamar al método ReactToDye (Color dyeColor) en la flor, que cambiará la flor de acuerdo con sus reglas internas. Luego puedo consultar la Colorpropiedad nuevamente y reaccionar ante cualquier cambio después de que se haya llamado al método ReactToDye. Sería un error para mí modificar directamente Colorla flor y si pudiera, entonces la abstracción se ha roto.

Algunas veces (con menos frecuencia, pero con la frecuencia suficiente como para mencionar) los configuradores tienen un diseño OO bastante válido. Si tengo un objeto de Cliente, es bastante válido exponer un setter para su dirección. Si llama a eso setAddress(string address)o ChangeMyAddressBecauseIMoved(string newAddress)simplemente string Address { get; set; }es una cuestión de semántica. El estado interno de ese objeto debe cambiar y la forma adecuada de hacerlo es establecer el estado interno de ese objeto. Incluso si requiero un registro histórico de las direcciones en las que Customerha vivido, puedo usar el setter para cambiar mi estado interno de manera apropiada para hacerlo. En este caso, no tiene sentido Customerque sea inmutable y no hay mejor manera de cambiar una dirección que proporcionar un configurador para que lo haga.

No estoy seguro de quién sugiere que Getters y setters son malos o rompen paradigmas orientados a objetos, pero si lo hacen, probablemente lo estén haciendo en respuesta a una característica específica del lenguaje que utilizan. Tengo la sensación de que esto surgió de la cultura de Java "todo es un objeto" (y posiblemente se extendió a otros lenguajes). El mundo .NET no tiene esta discusión en absoluto. Getters and Setters son una función de lenguaje de primera clase que no solo utilizan las personas que escriben aplicaciones en el idioma, sino también la propia API de idioma.

Debe tener cuidado al usar getters. No desea exponer los datos incorrectos ni desea exponer los datos de manera incorrecta (es decir, objetos mutables, referencias a estructuras que pueden permitir que un consumidor modifique el estado interno). Pero sí desea modelar sus objetos para que los datos se encapsulen de tal manera que sus objetos puedan ser utilizados por consumidores externos y protegidos del mal uso por esos mismos consumidores. A menudo eso requerirá captadores.

Para resumir: Getters y Setters son herramientas válidas de diseño orientado a objetos. No se deben usar con tanta libertad que expongan todos los detalles de la implementación, pero tampoco desea usarlos con tanta moderación que los consumidores de un objeto no puedan usar el objeto de manera eficiente.

Stephen
fuente
-1

Lo que es una cosa, sin opiniones, que se romperá si uso captadores ...

La mantenibilidad se romperá.

En cualquier momento (debería decir casi siempre) ofrece un captador que básicamente regala más de lo que se le pidió. Esto también significa que debe mantener más de lo que se le pidió, por lo tanto, reduce la capacidad de mantenimiento sin ningún motivo real.

Vayamos con el Collectionejemplo de @ candied_orange . Al contrario de lo que escribe Collection , no debería tener un captador. Las colecciones existen por razones muy específicas, sobre todo para iterar sobre todos los elementos. Esta funcionalidad no es una sorpresa para nadie, por lo que debe implementarse en el Collection, en lugar de presionar este caso de uso obvio en el usuario, utilizando ciclos for y getters o cualquier otra cosa.

Para ser justos, algunos límites hacen captadores necesidad. Esto sucede si realmente no sabes para qué se usarán. Por ejemplo, hoy en día puede obtener el seguimiento de pila de la Exceptionclase de Java , porque la gente quería usarlo por todo tipo de razones curiosas que los escritores no pudieron o no pudieron imaginar.

Robert Bräutigam
fuente
1
Contraejemplo. Considere una función que necesita iterar dos Collections simultáneamente, como en (A1, B1), (A2, B2), .... Esto requiere una implementación de "zip". ¿Cómo se implementa "zip" si la función de iteración se implementa en uno de los dos Collection? ¿Cómo se accede al elemento correspondiente en el otro?
rwong
@rwong El Collectiontiene que implementarse zipconsigo mismo, dependiendo de cómo se escriba la biblioteca. Si no es así, lo implementa y envía una solicitud de extracción al creador de la biblioteca. Tenga en cuenta que acepté que algunos límites necesitan getters a veces, mi verdadero problema es que los getters están en todas partes hoy en día.
Robert Bräutigam
En ese caso, eso implica que la biblioteca debe tener captadores en el Collectionobjeto, al menos para su propia implementación de zip. (Es decir, el comprador tiene que tener visibilidad de paquete por lo menos.) Y ahora dependerá del creador de la biblioteca de aceptar su solicitud de extracción ...
rwong
No te entiendo del todo. El Collectionobviamente, poder acceder a su propio estado interno, o para ser más precisos cualquier Collectioninstancia puede acceder el estado interno de cualquier otro. Es del mismo tipo, no se necesitan captadores.
Robert Bräutigam
También te escucho, pero de nuevo, ¿cuál es exactamente la mejor solución para los captadores? Sé que está fuera del alcance de la pregunta.
coolpasta