language-agnostic oop immutability setter getter

language agnostic - Allen Holub escribió "Nunca deberías usar las funciones get/set", ¿está en lo correcto?



language-agnostic oop (13)

(Tenga en cuenta que estoy llegando a esto desde un ángulo de "propiedad" de .NET)

Bueno, simplemente - no estoy de acuerdo con él; él hace un gran alboroto sobre el tipo de retorno de las propiedades que es algo malo porque puede romper su código de llamada, pero exactamente el mismo argumento se aplicaría a los argumentos del método. Y si no puedes usar los métodos tampoco?

De acuerdo, los argumentos del método podrían cambiarse a medida que se ampliaran las conversiones, pero ... por qué ... Además, tenga en cuenta que en C # la palabra clave var podría mitigar una gran parte de este dolor percibido.

Los usuarios no son un detalle de implementación; ellos son el API / contrato público. Sí, si rompes el contracft tienes problemas. ¿Cuándo se convirtió eso en una sorpresa? Del mismo modo, no es raro que los usuarios no sean triviales, es decir, no solo envuelven campos; realizan cálculos, verificaciones lógicas, notificaciones, etc. Y permiten abstracciones de estado basadas en interfaz. Ah, y polimorfismo, etc.

Re la naturaleza detallada de los usuarios (p3? 4?) - en C #: public int Foo {get; private set;} public int Foo {get; private set;} - trabajo hecho.

En definitiva, todo el código es un medio para expresar nuestra intención al compilador. Las propiedades me permiten hacer eso de una forma polimórfica, extensible, contraíble, extensible y basada en contratos, gracias. ¿Por qué tengo que "arreglar" esto?

Esta pregunta ya tiene una respuesta aquí:

Allen Holub escribió lo siguiente,

No puedes tener un programa sin un acoplamiento. No obstante, puede minimizar el acoplamiento considerablemente siguiendo servilmente los preceptos OO (orientados a objetos) (lo más importante es que la implementación de un objeto debe estar completamente oculta de los objetos que lo utilizan). Por ejemplo, las variables de instancia de un objeto (campos de miembro que no son constantes), siempre deben ser privadas. Período. Sin excepciones. Nunca. Lo digo en serio. (En ocasiones puede usar métodos protegidos de manera efectiva, pero las variables de instancia protegidas son una abominación).

Lo cual suena razonable, pero luego continúa diciendo:

Nunca debe usar las funciones get / set por la misma razón: son formas demasiado complicadas de hacer que un campo sea público (aunque las funciones de acceso que devuelven objetos completos en lugar de un valor de tipo básico son razonables en situaciones donde el objeto devuelto la clase es una abstracción clave en el diseño).

Lo cual, francamente, me parece una locura.

Entiendo el principio de ocultación de información, pero sin accesoadores y mutadores no podrías usar Java beans en absoluto. No sé cómo seguiría un diseño de MVC sin acceso en el modelo, ya que el modelo no puede ser responsable de mostrar la vista.

Sin embargo, soy un programador más joven y aprendo más sobre Diseño Orientado a Objetos todos los días. Quizás alguien con más experiencia pueda opinar sobre este tema.

Artículos de Allen Holub como referencia

Preguntas relacionadas:


Creo que lo que Allen Holub intentó decir, reformulado en this artículo, es el siguiente.

Getters y setters pueden ser útiles para las variables que específicamente desea encapsular, pero no tiene que usarlas para todas las variables. De hecho, usarlos para todas las variables es desagradable.

Los programadores de problemas tienen, y Allen Holub tenía razón al señalarlo, es que a veces usan getters / setters para todas las variables. Y el propósito de la encapsulación se pierde.


Creo que los getters y setters solo deben usarse para variables a las que se necesita acceder o cambiar fuera de una clase. Dicho esto, no creo que las variables deban ser públicas a menos que sean estáticas. Esto se debe a que hacer públicas las variables que no son estáticas puede hacer que se modifiquen indeseablemente. Digamos que tienes un desarrollador que está usando descuidadamente variables públicas. A continuación, accede a una variable de otra clase y sin querer, la cambia. Ahora tiene un error en su software como resultado de este contratiempo. Es por eso que creo en el uso adecuado de getters y setters, pero no los necesita para cada variable privada o protegida.


Cuando se me presentan ideas como esta, me gusta echar un vistazo a las bibliotecas y marcos que uso y que me gusta usar.

Por ejemplo, aunque algunos estarán en desacuerdo, me gusta la API estándar de Java. También me gusta el Spring Framework. Al observar las clases en estas bibliotecas, notará que muy raramente hay setters y getters que están ahí solo para exponer alguna variable interna. Hay métodos llamados getX, pero eso no significa que sea un getter en el sentido convencional.

Por lo tanto, creo que él tiene un punto, y es esto: cada vez que presiona elegir "Generar getters / setters" en Eclipse (o su IDE de elección), debería dar un paso atrás y preguntarse qué está haciendo. ¿Es realmente apropiado exponer esta representación interna, o estropeé mi diseño en algún momento?


El problema con getters / setters es que intentan falsificar la encapsulación pero en realidad la rompen al exponer sus partes internas. En segundo lugar, están tratando de hacer dos cosas por separado, proporcionar acceso y controlar su estado, y no lo están haciendo muy bien.

Rompe la encapsulación porque cuando llamas a un método get / set, primero necesitas saber el nombre (o tener una buena idea) del campo que deseas cambiar, y segundo, debes saber su tipo, por ejemplo. no podías llamar

setPositionX("some string");

Si conoce el nombre y el tipo del campo, y el colocador es público, entonces cualquiera puede llamar al método como si fuera un campo público de todos modos, es solo una forma más complicada de hacerlo, entonces ¿por qué no simplemente simplificarlo y hacer Es un campo público en primer lugar.

Al permitir el acceso a su estado pero al tratar de controlarlo al mismo tiempo, un método get / set simplemente confunde las cosas y termina siendo una placa de caldera inútil, o engañoso, al no hacer lo que dice al tener side- efectos que el usuario podría no esperar. Si se necesita una verificación de errores, podría llamarse algo así como

public void tryPositionX(int x) throws InvalidParameterException{ if (x >= 0) this.x = x; else throw new InvalidParameterException("Holy Negatives Batman!"); }

o si se necesita un código adicional, podría llamarse un nombre más preciso en función de lo que hace el método completo, por ejemplo.

tryPositionXAndLog(int x) throws InvalidParameterException{ tryPositionX(x); numChanges++; }

En mi humilde opinión necesitando getters / setters para hacer que algo funcione es a menudo un síntoma de mal diseño. Haga uso del principio "diga, no pregunte", o vuelva a pensar por qué un objeto necesita enviar sus datos de estado en primer lugar. Exponer métodos que cambian el comportamiento de un objeto en lugar de su estado. Los beneficios de eso incluyen un mantenimiento más fácil y una mayor extensibilidad.

También mencionas MVC y dices que un modelo no puede ser responsable de su visión, para ese caso Allen Holub da un ejemplo de hacer una capa de abstracción al tener una clase "give-me-a-JComponent-that-representa-your-identity" " lo que dice sería" aislar la forma en que se representan las identidades del resto del sistema ". No tengo la experiencia suficiente para comentar si eso funcionaría o no, pero en la superficie suena una idea decente.


Hice algunos Google adicionales en Allen Holub, parece que tiene su parte de oponentes en la comunidad de Java.

El último es particularmente señalado. El texto del comentario está en cursiva.

Aunque getIdentity comienza con "get", no es un descriptor de acceso porque no solo devuelve un campo. Devuelve un objeto complejo que tiene un comportamiento razonable

Ah, pero espera ... ¿está bien usar accesorios siempre que devuelvas objetos en lugar de tipos primitivos? Ahora esa es una historia diferente, pero es tan tonto para mí. A veces necesitas un objeto, a veces necesitas un tipo primitivo.

Además, noto que Allen ha suavizado radicalmente su posición desde su columna anterior sobre el mismo tema, donde el mantra "Nunca use accesadores" no sufrió una sola excepción. Quizás después de unos años se dio cuenta de que los usuarios de acceso sí tienen un propósito después de todo ...

Tenga en cuenta que no he puesto ningún código de UI en la lógica comercial. Escribí la capa UI en términos de AWT (Abstract Window Toolkit) o ​​Swing, que son ambas capas de abstracción.

Buena persona ¿Qué sucede si está escribiendo su solicitud en SWT? ¿Qué tan "abstracto" es realmente AWT en ese caso? Solo déjalo en la cara: este consejo simplemente te lleva a escribir código de UI en tu lógica de negocios. Qué gran principio. Después de todo, solo han pasado al menos diez años desde que identificamos esta práctica como una de las peores decisiones de diseño que puede tomar en un proyecto.

Mi problema es que un programador novel a veces tropieza con artículos en Internet y les da más crédito de lo que debería. Quizás este es uno de esos casos.


Las variables públicas tienen sentido cuando la clase no es más que un paquete de datos sin coherencia real, o cuando es realmente realmente elemental (como una clase de puntos). En general, si hay alguna variable en una clase que crees que probablemente no debería ser pública, eso significa que la clase tiene cierta coherencia, y las variables tienen una cierta relación que debe mantenerse, por lo que todas las variables deben ser privadas.

Los captadores y establecedores tienen sentido cuando reflejan algún tipo de idea coherente. En una clase de polígono, por ejemplo, las coordenadas xey de vértices dados tienen un significado fuera del límite de clase. Probablemente tenga sentido tener getters, y probablemente tenga sentido tener setters. En una clase de cuenta bancaria, el saldo probablemente se almacena como una variable privada, y casi seguramente debería tener un getter. Si tiene un setter, necesita tener un log integrado para preservar la auditabilidad.

Hay algunas ventajas de getters y setters sobre variables públicas. Proporcionan cierta separación de interfaz e implementación. El hecho de que un punto tenga una función .getX() no significa que tiene que haber una x, ya que se puede hacer que .getX() y .setX() funcionen bien con coordenadas radiales. Otra es que es posible mantener invariantes de clase, haciendo lo que sea necesario para mantener la clase constante dentro del organismo. Otra es que es posible tener una funcionalidad que se active en un conjunto, como el registro para el saldo de la cuenta bancaria.

Sin embargo, para las clases más abstractas, las variables miembro pierden significado individual, y solo tienen sentido en contexto. No necesita conocer todas las variables internas de una clase de flujo C ++, por ejemplo. Necesita saber cómo introducir y sacar elementos y cómo realizar otras acciones. Si contara con la estructura interna exacta, estaría empantanado en detalle que podría variar arbitrariamente entre compiladores o versiones.

Entonces, yo diría usar variables privadas casi exclusivamente, getters y setters donde tienen un significado real en el comportamiento de los objetos, y no de otra manera.

El hecho de que getters y setters se usen en exceso con frecuencia no significa que sean inútiles.


Lea atentamente ese artículo. Holub está insistiendo en que los getters y setters son un malvado "antipatrón por defecto", un mal hábito en el que nos deslizamos cuando diseñamos un sistema; porque podemos.

El proceso de pensamiento debe estar en línea; ¿Qué hace este objeto? ¿Cuáles son sus responsabilidades? ¿Cuáles son sus comportamientos? ¿Qué sabe? Pensar largo y tendido en estas preguntas lo lleva naturalmente a diseñar clases que expongan la interfaz de nivel más alto posible.

Un auto es un buen ejemplo. Expone una interfaz de alto nivel estandarizada y bien definida. No me setSpeed(60) por setSpeed(60) ... ¿es ese MPH o km / h? Solo acelero, crucero, desacelero. No tengo que pensar en los detalles en setSteeringWheelAngle(getSteeringWheelAngle()+Math.rad(-1.5)) , solo doy turn(-1.5) y los detalles se setSteeringWheelAngle(getSteeringWheelAngle()+Math.rad(-1.5)) bajo el capó .

Todo se reduce a "Usted puede y debe saber para qué se usará cada clase, qué hace, qué representa y expondrá la interfaz de más alto nivel posible que cumpla con esos requisitos. Los getters y setters suelen ser una salida de emergencia, cuando el programador es demasiado perezoso para hacer el análisis requerido para determinar exactamente qué es y qué no es cada clase, y así seguimos por el camino de "puede hacer cualquier cosa". ¡Los captadores y establecedores son malvados!

A veces, los requisitos reales para una clase son desconocidos antes de tiempo. Eso es genial, simplemente copie y use getter / setter antipattern por ahora, pero cuando sepa, a través de la experiencia, para qué se usa la clase, probablemente querrá regresar y limpiar la sucia interfaz de bajo nivel. La refacturación basada en "cosas que desearía saber al escribir el lechón en primer lugar" es parte del curso. No es necesario que sepa todo para comenzar, sino que cuanto más sepa, es probable que se requiera menos reelaboración en el camino.

Esa es la mentalidad que está promoviendo. Getters y setters son una trampa fácil en la que caer.

Sí, los frijoles básicamente requieren getters y setters, pero para mí un frijol es un caso especial. Los frijoles representan sustantivos, cosas, objetos tangibles identificables (si no físicos). No muchos objetos tienen comportamientos automáticos; la mayoría de las veces las cosas son manipuladas por fuerzas externas, incluidos los humanos, para convertirlas en cosas productivas.

daisy.setColor(Color.PINK) tiene mucho sentido. ¿Qué más puedes hacer? ¿Tal vez una fusión mental vulcaniana para hacer que la flor quiera ser rosa? Hmmm?

Getters y setters tienen su? Mal? lugar. Es solo que, como todas las cosas buenas de OO, tendemos a abusar de ellas, porque son seguras y familiares, por no mencionar simples, y por lo tanto, podría ser mejor si los noobs no las vieran ni las escucharan, al menos hasta que lo hagan. Dominaba la cosa de la fusión mental.


Los captadores / instaladores públicos son malos si proporcionan acceso a los detalles de implementación. Sin embargo, es razonable proporcionar acceso a las propiedades del objeto y utilizar getters / setters para esto. Por ejemplo, si el automóvil tiene la propiedad de color, es aceptable dejar que los clientes lo "observen" usando un captador. Si algún cliente necesita la posibilidad de volver a colorear un automóvil, la clase puede proporcionar un colocador (sin embargo, el término "recolor" es más claro). Es importante no informar a los clientes cómo se almacenan las propiedades en los objetos, cómo se mantienen, etc.


Los getters y setters se utilizan como poco más que una máscara para hacer pública una variable privada.

No tiene sentido repetir lo que ya dijo Holub, pero el quid de la cuestión es que las clases deben representar el comportamiento y no solo el estado.


No creo que diga nunca utilizar get / set, sino que usar get / set para un campo no es mejor que simplemente hacer que el campo sea público (por ejemplo, cadena pública Name vs. public string Name {get; set;}).

Si se usa get / set, limita el ocultamiento de la información de OO, lo que puede bloquearlo en una mala interfaz.

En el ejemplo anterior, Name es una cadena; ¿y si queremos cambiar el diseño más tarde para agregar varios nombres? La interfaz expuso solo una cadena, por lo que no podemos agregar más sin romper la implementación existente.

Sin embargo, si en lugar de utilizar get / set, inicialmente tenía un método como Add (string name), internamente podría procesar el nombre de manera individual o agregarlo a una lista o no, y externamente llamar al método Add tantas veces como desee agregar más nombres.

El objetivo de OO es diseñar con un nivel de abstracción; no expongas más detalles de los que tienes que hacer.

Lo más probable es que si acaba de envolver un tipo primitivo con un get / set, haya roto este principio.

Por supuesto, esto es si crees en los objetivos OO; Encuentro que la mayoría no lo hace, no realmente, solo usan Objetos como una forma conveniente de agrupar el código funcional.


No tengo ningún problema con que Holub le diga que generalmente debe evitar alterar el estado de un objeto, sino que debe recurrir a métodos integrados (ejecución de comportamientos) para lograr este fin. Como señala Corletk, es sabio pensar mucho sobre el nivel más elevado de abstracción y no solo programar de forma irreflexiva con getters / setters que solo le permiten hacer un end-run alrededor de la encapsulación.

Sin embargo, tengo muchos problemas con cualquiera que te diga que nunca deberías usar "setters" o "nunca" acceder a tipos primitivos. De hecho, el esfuerzo requerido para mantener este nivel de pureza en todos los casos puede y va a terminar causando más complejidad en su código que el uso de propiedades implementadas apropiadamente. Solo debe tener suficiente sentido común para saber cuándo está eludiendo las reglas de ganancia a corto plazo a expensas del dolor a largo plazo.

Holub no confía en ti para saber la diferencia. Creo que saber la diferencia es lo que te hace un profesional.


Ummmm ... nunca ha escuchado sobre el concepto de Encapsulación. Los métodos Getter y Setter se implementan para controlar el acceso a los miembros de una clase. Al hacer que todos los campos sean públicamente visibles ... cualquiera puede escribir los valores que quiera para ellos invalidando por completo todo el objeto.

En caso de que alguien esté un poco confuso sobre el concepto de encapsulación, lee aquí:

Encapsulación (Informática)

... y si son realmente malvados, ¿.NET construiría el concepto de Propiedad en el lenguaje? (Métodos Getter y Setter que simplemente se ven un poco más bonitos)

EDITAR

El artículo menciona la encapsulación:

" Getters y setters pueden ser útiles para las variables que específicamente deseas encapsular, pero no tienes que usarlas para todas las variables. De hecho, usarlas para todas las variables es desagradable.

El uso de este método conducirá a un código extremadamente difícil de mantener a largo plazo. Si descubres a mitad de camino un proyecto que abarca años en que un campo debe ser encapsulado, tendrás que actualizar TODAS LAS REFERENCIAS de ese campo en todas partes de tu software para obtener el beneficio. Parece mucho más inteligente utilizar la encapsulación adecuada por adelantado y salvarte el dolor de cabeza más adelante.