Me está costando mucho diseñar las clases de una manera u otra. He leído que los objetos exponen su comportamiento, no sus datos; por lo tanto, en lugar de usar getter / setters para modificar datos, los métodos de una clase dada deberían ser "verbos" o acciones que operen en el objeto. Por ejemplo, en un objeto 'Cuenta', tendríamos los métodos Withdraw()
y Deposit()
no setAmount()
etc. Ver: ¿Por qué los métodos getter y setter son malos ?
Entonces, por ejemplo, dada una clase de cliente que mantiene mucha información sobre el cliente, por ejemplo, nombre, fecha de nacimiento, teléfono, dirección, etc., ¿cómo se evitaría obtener / establecer para obtener y establecer todos esos atributos? ¿Qué método de tipo 'Comportamiento' se puede escribir para completar todos esos datos?
fuente
name()
enCustomer
es menos clara o más clara, de un método llamadogetName()
.Respuestas:
Como se indicó en bastantes respuestas y comentarios, los DTO son apropiados y útiles en algunas situaciones, especialmente en la transferencia de datos a través de límites (por ejemplo, serialización a JSON para enviar a través de un servicio web). Para el resto de esta respuesta, lo ignoraré más o menos y hablaré sobre las clases de dominio y cómo se pueden diseñar para minimizar (si no eliminar) los captadores y establecedores, y seguir siendo útiles en un proyecto grande. Tampoco hablaré sobre por qué eliminar getters o setters, o cuándo hacerlo, porque esas son preguntas propias.
Como ejemplo, imagina que tu proyecto es un juego de mesa como el ajedrez o el acorazado. Puede tener varias formas de representar esto en una capa de presentación (aplicación de consola, servicio web, GUI, etc.), pero también tiene un dominio central. Una clase que puede tener es
Coordinate
, representando una posición en el tablero. La forma "malvada" de escribirlo sería:(Voy a escribir ejemplos de código en C # en lugar de Java, por brevedad y porque estoy más familiarizado con él. Espero que eso no sea un problema. Los conceptos son los mismos y la traducción debería ser simple).
Eliminar setters: inmutabilidad
Si bien los captadores públicos y los setters son potencialmente problemáticos, los setters son los más "malvados" de los dos. También suelen ser los más fáciles de eliminar. El proceso es simple: establece el valor desde dentro del constructor. Cualquier método que previamente haya mutado el objeto debería devolver un nuevo resultado. Entonces:
Tenga en cuenta que esto no protege contra otros métodos en la clase que muta X e Y. Para ser más estrictamente inmutable, puede usar
readonly
(final
en Java). Pero de cualquier manera, ya sea que haga que sus propiedades sean realmente inmutables o simplemente evite la mutación pública directa a través de los setters, es el truco para eliminar sus setters públicos. En la gran mayoría de las situaciones, esto funciona bien.Eliminando Getters, Parte 1: Diseñando para el comportamiento
Lo anterior está muy bien para los setters, pero en términos de getters, en realidad nos disparamos en el pie incluso antes de comenzar. Nuestro proceso fue pensar qué es una coordenada, los datos que representa, y crear una clase en torno a eso. En cambio, deberíamos haber comenzado con qué comportamiento necesitamos de una coordenada. Este proceso, por cierto, es ayudado por TDD, donde solo extraemos clases como esta una vez que las necesitamos, por lo que comenzamos con el comportamiento deseado y trabajamos desde allí.
Entonces, digamos que el primer lugar donde necesitabas un
Coordinate
detector de colisión: querías verificar si dos piezas ocupan el mismo espacio en el tablero. Aquí está la forma "malvada" (los constructores se omiten por brevedad):Y aquí está el buen camino:
(
IEquatable
implementación abreviada para simplificar). Al diseñar para el comportamiento en lugar de modelar datos, hemos logrado eliminar nuestros captadores.Tenga en cuenta que esto también es relevante para su ejemplo. Puede estar utilizando un ORM, o mostrar información del cliente en un sitio web o algo así, en cuyo caso
Customer
probablemente tendría sentido algún tipo de DTO. Pero el hecho de que su sistema incluya clientes y estén representados en el modelo de datos no significa automáticamente que deba tener unaCustomer
clase en su dominio. Tal vez, a medida que diseñe para el comportamiento, surgirá uno, pero si desea evitar captadores, no cree uno preventivamente.Eliminando Getters, Parte 2: Comportamiento Externo
Entonces, lo anterior es un buen comienzo, pero tarde o temprano probablemente se encontrará con una situación en la que tiene un comportamiento asociado con una clase, que de alguna manera depende del estado de la clase, pero que no pertenece a la clase. Este tipo de comportamiento es lo que generalmente vive en la capa de servicio de su aplicación.
Tomando nuestro
Coordinate
ejemplo, eventualmente querrás representar tu juego para el usuario, y eso podría significar dibujar en la pantalla. Puede, por ejemplo, tener un proyecto de IU queVector2
representa un punto en la pantalla. Pero sería inapropiado que laCoordinate
clase se hiciera cargo de la conversión de una coordenada a un punto en la pantalla, lo que llevaría todo tipo de problemas de presentación a su dominio principal. Lamentablemente, este tipo de situación es inherente al diseño OO.La primera opción , que se elige con mucha frecuencia, es simplemente exponer a los malditos captadores y decirle al diablo con ella. Esto tiene la ventaja de la simplicidad. Pero ya que estamos hablando de evitar a los captadores, digamos por el argumento de rechazar este y ver qué otras opciones hay.
Una segunda opción es agregar algún tipo de
.ToDTO()
método en su clase. Esto, o similar, puede ser necesario de todos modos, por ejemplo, cuando desea guardar el juego, necesita capturar casi todo su estado. Pero la diferencia entre hacer esto por sus servicios y simplemente acceder al getter directamente es más o menos estético. Todavía tiene tanta "maldad".Una tercera opción , que he visto defendida por Zoran Horvat en un par de videos de Pluralsight, es usar una versión modificada del patrón de visitante. Este es un uso y variación bastante inusual del patrón y creo que el kilometraje de las personas variará enormemente en cuanto a si está agregando complejidad sin ganancia real o si es un buen compromiso para la situación. La idea es esencialmente usar el patrón de visitante estándar, pero que los
Visit
métodos tomen el estado que necesitan como parámetros, en lugar de la clase que visitan. Los ejemplos se pueden encontrar aquí .Para nuestro problema, una solución usando este patrón sería:
Como probablemente pueda ver,
_x
ya_y
no están realmente encapsulados. Podríamos extraerlos creando unoIPositionTransformer<Tuple<int,int>>
que simplemente los devuelva directamente. Dependiendo del gusto, puede sentir que esto hace que todo el ejercicio no tenga sentido.Sin embargo, con los captadores públicos, es muy fácil hacer las cosas de manera incorrecta, simplemente extrayendo los datos directamente y usándolos en violación de Tell, Don't Ask . Mientras que usar este patrón en realidad es más simple hacerlo de la manera correcta: cuando desee crear un comportamiento, comenzará automáticamente creando un tipo asociado con él. Las violaciones de TDA serán muy evidentemente malolientes y probablemente requieran trabajar en una solución más simple y mejor. En la práctica, estos puntos hacen que sea mucho más fácil hacerlo de la manera correcta, OO, que la forma "malvada" que estimulan los captadores.
Finalmente , incluso si inicialmente no es obvio, de hecho puede haber formas de exponer suficiente de lo que necesita como comportamiento para evitar la necesidad de exponer el estado. Por ejemplo, utilizando nuestra versión anterior de
Coordinate
cuyo único miembro público esEquals()
(en la práctica necesitaría unaIEquatable
implementación completa ), podría escribir la siguiente clase en su capa de presentación:Resulta, quizás sorprendentemente, que todo el comportamiento que realmente necesitábamos de una coordenada para lograr nuestro objetivo era la verificación de la igualdad. Por supuesto, esta solución se adapta a este problema y hace suposiciones sobre el uso / rendimiento aceptable de la memoria. Es solo un ejemplo que se ajusta a este dominio del problema en particular, en lugar de un plan para una solución general.
Y nuevamente, las opiniones variarán sobre si en la práctica esto es una complejidad innecesaria. En algunos casos, no existe una solución como esta, o puede ser prohibitivamente extraño o complejo, en cuyo caso puede volver a las tres anteriores.
fuente
Customer
clase que requiera poder mutar su número de teléfono? Quizás el número de teléfono del cliente cambia y necesito persistir ese cambio en la base de datos, pero nada de eso es responsabilidad de un objeto de dominio que proporcione comportamiento. Esa es una preocupación de acceso a datos, y probablemente se manejaría con un DTO y, por ejemplo, un repositorio.Customer
datos del objeto de dominio relativamente frescos (en sincronía con la base de datos) es una cuestión de administrar su ciclo de vida, que tampoco es su responsabilidad, y de nuevo probablemente terminaría viviendo en un repositorio o una fábrica o un contenedor de COI o cualquiera que sea la instanciaCustomer
s.La forma más sencilla de evitar los establecedores es entregar los valores al método constructor cuando
new
sube el objeto. Este también es el patrón habitual cuando desea hacer que un objeto sea inmutable. Dicho esto, las cosas no siempre son tan claras en el mundo real.Es cierto que los métodos deben ser sobre el comportamiento. Sin embargo, algunos objetos, como el Cliente, existen principalmente para contener información. Esos son los tipos de objetos que más se benefician de los captadores y colocadores; Si no hubiera necesidad de tales métodos, simplemente los eliminaríamos por completo.
Lecturas adicionales
Cuándo se justifican los captadores y establecedores
fuente
setEvil(null);
Está perfectamente bien tener un objeto que exponga datos en lugar de comportamiento. Simplemente lo llamamos un "objeto de datos". El patrón existe bajo nombres como Data Transfer Object u Value Object. Si el propósito del objeto es retener datos, los captadores y los establecedores son válidos para acceder a los datos.
Entonces, ¿ por qué alguien diría "los métodos getter y setter son malos"? Verá esto mucho: alguien toma una directriz que es perfectamente válida en un contexto específico y luego la elimina para obtener un titular más contundente. Por ejemplo, " favorecer la composición sobre la herencia " es un buen principio, pero pronto alguien eliminará el contexto y escribirá " ¿Por qué se extiende es malo " (oye, mismo autor, qué coincidencia!) O "la herencia es mala y debe ser destruido ".
Si nos fijamos en el contenido del artículo, en realidad tiene algunos puntos válidos, simplemente estira el punto para hacer un titular de click-baity. Por ejemplo, el artículo establece que los detalles de implementación no deben exponerse. Estos son los principios de encapsulación y ocultación de datos que son fundamentales en OO. Sin embargo, un método getter no expone por definición los detalles de implementación. En el caso de un objeto de datos del Cliente , las propiedades de Nombre , Dirección , etc., no son detalles de implementación, sino más bien el propósito completo del objeto y deberían formar parte de la interfaz pública.
Lea la continuación del artículo al que se vincula, para ver cómo sugiere realmente establecer propiedades como 'nombre' y 'salario' en un objeto 'Empleado' sin el uso de los malvados. Resulta que usa un patrón con un 'Exportador' que se completa con métodos llamados agregar Nombre, agregar Salario, que a su vez establece campos del mismo nombre ... Así que al final termina usando exactamente el patrón de establecimiento, solo con un diferente convención de nomenclatura
Esto es como pensar que evitas las trampas de los singletons al renombrarlos como únicos, pero manteniendo la misma implementación.
fuente
Para transformar la
Customer
clase de un objeto de datos, podemos hacernos las siguientes preguntas sobre los campos de datos:¿Cómo queremos usar {campo de datos}? ¿Dónde se usa {campo de datos}? ¿Se puede y se debe mover el uso de {campo de datos} a la clase?
P.ej:
¿Para qué sirve
Customer.Name
?Posibles respuestas, mostrar el nombre en una página web de inicio de sesión, usar el nombre en los correos al cliente.
Lo que lleva a métodos:
¿Para qué sirve
Customer.DOB
?Validar la edad del cliente. Descuentos en el cumpleaños del cliente. Mailings
Dados los comentarios, el objeto de ejemplo
Customer
, tanto como objeto de datos como objeto "real" con sus propias responsabilidades, es demasiado amplio; es decir, tiene demasiadas propiedades / responsabilidades. Lo que conduce a muchos componentes dependiendo deCustomer
(leyendo sus propiedades) oCustomer
dependiendo de muchos componentes. Quizás existan diferentes puntos de vista del cliente, quizás cada uno debería tener su propia clase 1 distinta :El cliente en el contexto de
Account
las transacciones monetarias probablemente solo se use para:Account
s.Este cliente no necesita campos como
DOB
,FavouriteColour
,Tel
, y tal vez ni siquieraAddress
.El cliente en el contexto de un usuario que inicia sesión en un sitio web bancario.
Los campos relevantes son:
FavouriteColour
, que puede venir en forma de temática personalizada;LanguagePreferences
yGreetingName
En lugar de propiedades con captadores y definidores, estos podrían capturarse en un solo método:
El cliente en el contexto de marketing y correo personalizado.
Aquí no confiamos en las propiedades de un objeto de datos, sino que comenzamos por las responsabilidades del objeto; p.ej:
El hecho de que este objeto de cliente tenga una
FavouriteColour
propiedad y / o unaAddress
propiedad se vuelve irrelevante: quizás la implementación utiliza estas propiedades; pero también podría usar algunas técnicas de aprendizaje automático y usar interacciones previas con el cliente para descubrir en qué productos podría estar interesado el cliente.1. Por supuesto, las clases
Customer
yAccount
fueron ejemplos, y para un ejemplo simple o ejercicio de tarea, dividir a este cliente puede ser excesivo, pero con el ejemplo de división, espero demostrar que el método de convertir un objeto de datos en un objeto con Las responsabilidades funcionarán.fuente
Customer.FavoriteColor
?TL; DR
Modelar para el comportamiento es bueno.
Modelar para buenas (!) Abstracciones es mejor.
A veces se requieren objetos de datos.
Comportamiento y abstracción
Hay varias razones para evitar captadores y colocadores. Una es, como notó, evitar los datos de modelado. Esta es en realidad la razón menor. La razón más grande es proporcionar abstracción.
En su ejemplo con la cuenta bancaria que está clara: un
setBalance()
método sería realmente malo porque establecer un saldo no es para lo que debe usarse una cuenta. El comportamiento de la cuenta debe abstraerse de su saldo actual tanto como sea posible. Puede tener en cuenta el saldo al decidir si se debe suspender un retiro, puede dar acceso al saldo actual, pero modificar la interacción con una cuenta bancaria no debería requerir que el usuario calcule el nuevo saldo. Eso es lo que la cuenta debería hacer por sí misma.Incluso un par de
deposit()
ywithdraw()
métodos no es ideal para modelar una cuenta bancaria. Una mejor manera sería proporcionar un solotransfer()
método que tenga en cuenta otra y una cantidad como argumentos. Esto permitiría que la clase de cuenta se asegure trivialmente de que no cree / destruya accidentalmente dinero en su sistema, proporcionaría una abstracción muy útil y en realidad proporcionaría a los usuarios más información porque forzaría el uso de cuentas especiales para dinero ganado / invertido / perdido (ver doble contabilidad ). Por supuesto, no todos los usos de una cuenta necesitan este nivel de abstracción, pero definitivamente vale la pena considerar cuánta abstracción pueden proporcionar sus clases.Tenga en cuenta que proporcionar abstracción y ocultar datos internos no siempre es lo mismo. Casi cualquier aplicación contiene clases que efectivamente son solo datos. Las tuplas, los diccionarios y las matrices son ejemplos frecuentes. No desea ocultar al usuario la coordenada x de un punto. Hay muy poca abstracción que pueda / deba hacer con un punto.
La clase del cliente
Un cliente es ciertamente una entidad en su sistema que debería intentar proporcionar abstracciones útiles. Por ejemplo, probablemente debería estar asociado con un carrito de compras, y la combinación del carrito y el cliente debería permitir comprometer una compra, lo que podría iniciar acciones como enviarle los productos solicitados, cobrarle dinero (teniendo en cuenta su pago seleccionado método), etc.
El problema es que todos los datos que mencionó no solo están asociados con un cliente, sino que todos esos datos también son mutables. El cliente puede mudarse. Pueden cambiar su compañía de tarjeta de crédito. Pueden cambiar su dirección de correo electrónico y número de teléfono. ¡Diablos, incluso pueden cambiar su nombre y / o sexo! Por lo tanto, una clase de cliente con todas las funciones tiene que proporcionar un acceso de modificación total a todos estos elementos de datos.
Aún así, los emisores pueden / deberían proporcionar servicios no triviales: pueden garantizar el formato correcto de direcciones de correo electrónico, verificación de direcciones postales, etc. Asimismo, los "captadores" pueden proporcionar servicios de alto nivel como proporcionar direcciones de correo electrónico en el
Name <[email protected]>
formato utilizando los campos de nombre y la dirección de correo electrónico depositada, o proporcione una dirección postal formateada correctamente, etc. Por supuesto, lo que tiene sentido de esta funcionalidad de alto nivel depende en gran medida de su caso de uso. Puede ser una exageración completa, o puede requerir que otra clase lo haga bien. La elección del nivel de abstracción no es fácil.fuente
Intentando ampliar la respuesta de Kasper, es más fácil despotricar contra y eliminar a los setters. En un argumento bastante vago, de saludo (y con suerte humorístico):
¿Cuándo cambiaría Customer.Name alguna vez?
Raramente. Quizás se casaron. O entró en protección de testigos. Pero en ese caso, también querrá verificar y posiblemente cambiar su residencia, sus familiares y otra información.
¿Cuándo cambiaría DOB alguna vez?
Solo en la creación inicial o en un atornillado de entrada de datos. O si son jugadores de béisbol dominicanos. :-)
Estos campos no deberían ser accesibles con los setters normales y de rutina. Quizás tenga un
Customer.initialEntry()
método, o unCustomer.screwedUpHaveToChange()
método que requiera permisos especiales. Pero no tenga unCustomer.setDOB()
método público .Por lo general, un Cliente se lee desde una base de datos, una API REST, algún XML, lo que sea. Tenga un método
Customer.readFromDB()
o, si es más estricto con respecto a SRP / separación de preocupaciones, tendría un constructor separado, por ejemplo, unCustomerPersister
objeto con unread()
método. Internamente, de alguna manera establecen los campos (prefiero usar el acceso a paquetes o una clase interna, YMMV). Pero, de nuevo, evite los setters públicos.(Addendum as Question ha cambiado algo ...)
Digamos que su aplicación hace un uso intensivo de las bases de datos relacionales. Sería tonto tener
Customer.saveToMYSQL()
oCustomer.readFromMYSQL()
métodos. Eso crea un acoplamiento indeseable a una entidad concreta, no estándar y que probablemente cambie . Por ejemplo, cuando cambia el esquema o cambia a Postgress u Oracle.Sin embargo, la OMI, es perfectamente aceptable para acoplar al cliente a un nivel abstracto ,
ResultSet
. Un objeto auxiliar separado (lo llamaréCustomerDBHelper
, que probablemente sea una subclase deAbstractMySQLHelper
) conoce todas las conexiones complejas a su base de datos, conoce los detalles difíciles de optimización, conoce las tablas, consultas, uniones, etc. (o usa un ORM como Hibernate) para generar el ResultSet. Su objeto habla con elResultSet
, que es un estándar abstracto , que es poco probable que cambie. Cuando cambia la base de datos subyacente, o cambia el esquema, el Cliente no cambia , pero el CustomerDBHelper sí. Si tiene suerte, solo cambia AbstractMySQLHelper, que realiza automáticamente los cambios para Cliente, Comerciante, Envío, etc.De esta manera puede (quizás) evitar o disminuir la necesidad de captadores y establecedores.
Y, el punto principal del artículo de Holub, compara y contrasta lo anterior con lo que sería si usaras getters y setters para todo y cambiaras la base de datos.
Del mismo modo, digamos que usa mucho XML. En mi opinión, está bien acoplar a su Cliente a un estándar abstracto, como Python xml.etree.ElementTree o Java org.w3c.dom.Element . El cliente obtiene y se establece a partir de eso. De nuevo, puede (tal vez) disminuir la necesidad de captadores y establecedores.
fuente
El problema de tener captadores y establecedores puede deberse al hecho de que una clase puede usarse en la lógica de negocios de una manera, pero también puede tener clases auxiliares para serializar / deserializar los datos de una base de datos o archivo u otro almacenamiento persistente.
Debido al hecho de que hay muchas formas de almacenar / recuperar sus datos y desea desacoplar los objetos de datos de la forma en que están almacenados, la encapsulación puede verse "comprometida" haciendo públicos estos miembros o haciéndolos accesibles a través de getters y setters que es casi tan malo como hacerlos públicos.
Hay varias formas de evitar esto. Una forma es hacer que los datos estén disponibles para un "amigo". Aunque la amistad no se hereda, esto puede ser superado por cualquier serializador que solicite la información del amigo, es decir, el serializador base "reenvía" la información.
Su clase podría tener un método genérico "fromMetadata" o "toMetadata". From-metadata construye un objeto, por lo que bien puede ser un constructor. Si es un lenguaje de tipo dinámico, los metadatos son bastante estándar para dicho lenguaje y probablemente es la forma principal de construir tales objetos.
Si su lenguaje es C ++ específicamente, una forma de evitar esto es tener una "estructura" pública de datos y luego que su clase tenga una instancia de esta "estructura" como miembro y, de hecho, todos los datos que va a almacenar / recuperar para ser almacenado en él. Luego puede escribir fácilmente "envoltorios" para leer / escribir sus datos en múltiples formatos.
Si su lenguaje es C # o Java que no tiene "estructuras", puede hacerlo de manera similar, pero su estructura ahora es una clase secundaria. No existe un concepto real de "propiedad" de los datos o constancia, por lo que si proporciona una instancia de la clase que contiene sus datos y todo es público, lo que sea que se retenga puede modificarlo. Podrías "clonarlo" aunque esto puede ser costoso. Alternativamente, podría hacer que esta clase tenga datos privados pero use accesores. Eso brinda a los usuarios de su clase una forma indirecta de acceder a los datos, pero no es la interfaz directa con su clase y es realmente un detalle en el almacenamiento de los datos de la clase, que también es un caso de uso.
fuente
OOP se trata de encapsular y ocultar el comportamiento dentro de los objetos. Los objetos son cajas negras. Esta es una forma de diseñar cosas. El activo es, en muchos casos, uno no necesita conocer el estado interno de otro componente y es mejor no tener que saberlo. Puede aplicar esa idea principalmente con interfaces o dentro de un objeto con visibilidad y teniendo cuidado de que solo los verbos / acciones permitidos estén disponibles para la persona que llama.
Esto funciona bien para algún tipo de problema. Por ejemplo, en las interfaces de usuario para modelar componentes de IU individuales. Cuando interactúa con un cuadro de texto, solo le interesa configurar el texto, obtenerlo o escuchar el evento de cambio de texto. Por lo general, no le interesa saber dónde está el cursor, la fuente utilizada para dibujar el texto o cómo se usa el teclado. La encapsulación proporciona mucho aquí.
Por el contrario, cuando llama a un servicio de red, proporciona una entrada explícita. Por lo general, hay una gramática (como en JSON o XML) y todas las opciones de llamar al servicio no tienen por qué ocultarse. La idea es que puede llamar al servicio de la forma que desee y el formato de datos es público y publicado.
En este caso, o en muchos otros (como el acceso a una base de datos), realmente trabaja con datos compartidos. Como tal, no hay razón para ocultarlo, por el contrario, desea que esté disponible. Puede haber preocupación por el acceso de lectura / escritura o la coherencia de la comprobación de datos, pero en este núcleo, el concepto central si es público.
Para tal requisito de diseño en el que desea evitar la encapsulación y hacer que las cosas sean públicas y, de forma clara, desea evitar los objetos. Lo que realmente necesita son tuplas, estructuras C o su equivalente, no objetos.
Pero también ocurre en lenguajes como Java, lo único que puede modelar es objetos o matrices de objetos. Los objetos pueden contener algunos tipos nativos (int, float ...) pero eso es todo. Pero los objetos también pueden comportarse como una estructura simple con solo campos públicos y todo eso.
Entonces, si modela datos, puede hacerlo solo con campos públicos dentro de objetos porque no necesita más. No utiliza la encapsulación porque no la necesita. Esto se hace de esta manera en muchos idiomas. En Java, históricamente, una rosa estándar donde con getter / setter al menos podría tener control de lectura / escritura (al no agregar setter, por ejemplo) y que las herramientas y el marco utilizando la API de instrospección buscarían métodos getter / setter y los usarían para autocompletar el contenido o mostrar tesis como campos modificables en la interfaz de usuario generada automáticamente.
También existe el argumento de que podría agregar algo de lógica / verificación en el método setter.
En realidad, casi no hay justificación para getter / setters, ya que se usan con mayor frecuencia para modelar datos puros. Los marcos y desarrolladores que usan sus objetos esperan que el captador / definidor no haga nada más que establecer / obtener los campos de todos modos. Efectivamente, no está haciendo más con getter / setter de lo que podría hacerse con los campos públicos.
Pero esos viejos hábitos y viejos hábitos son difíciles de eliminar ... Incluso podría ser amenazado por sus colegas o maestros si no coloca a los captadores / colocadores a ciegas en todas partes si carecen de los antecedentes para comprender mejor qué son y cuáles son no.
Es probable que deba cambiar el idioma para obtener el código repetitivo de getters / setters de estas tesis. (Como C # o lisp). Para mí, los captadores / establecedores son solo otro error de mil millones de dólares ...
fuente
@Getter @Setter class MutablePoint3D {private int x, y, z;}
.Creo que esta pregunta es espinosa porque le preocupan los métodos de comportamiento para poblar datos, pero no veo ninguna indicación de qué comportamiento
Customer
pretende encapsular la clase de objetos.No confunda
Customer
como una clase de objetos con 'Cliente' como usuario / actor que realiza diferentes tareas utilizando su software.Cuando dice que se le da una clase de Cliente que mantiene mucha información sobre el cliente, entonces, en lo que respecta al comportamiento, parece que su clase de Cliente tiene poco que distinguirlo de una roca. A
Rock
puede tener un color, podría darle un nombre, podría tener un campo para almacenar su dirección actual pero no esperamos ningún tipo de comportamiento inteligente de una roca.Del artículo vinculado sobre getters / setters como malvados:
Sin ningún comportamiento definido, referirse a una roca como a
Customer
no cambia el hecho de que es solo un objeto con algunas propiedades que le gustaría rastrear y no importa qué trucos quiera jugar para alejarse de los atrapadores y setters A una roca no le importa si tiene un nombre válido y no se esperaría que una roca supiera si una dirección es válida o no.Su sistema de pedidos podría asociar un
Rock
pedido de compra y, siempre queRock
tenga una dirección definida, alguna parte del sistema puede asegurarse de que un artículo se entregue a una roca.En todos estos casos,
Rock
es solo un objeto de datos y seguirá siendo uno hasta que definamos comportamientos específicos con resultados útiles en lugar de hipotéticos.Prueba esto:
Cuando evita sobrecargar la palabra 'Cliente' con 2 significados potencialmente diferentes, debería facilitar la conceptualización de las cosas.
¿Un
Rock
objeto coloca una Orden o es algo que hace un ser humano haciendo clic en los elementos de la interfaz de usuario para activar acciones en su sistema?fuente
Agrego mis 2 centavos aquí mencionando el enfoque de objetos que hablan SQL .
Este enfoque se basa en la noción de objeto autocontenido. Tiene todos los recursos que necesita para implementar su comportamiento. No es necesario que le digan cómo hacer su trabajo: la solicitud declarativa es suficiente. Y un objeto definitivamente no tiene que contener todos sus datos como propiedades de clase. Realmente no importa, y no debería importar, de dónde provienen.
Hablando de un agregado , la inmutabilidad tampoco es un problema. Digamos que tiene una secuencia de estados que el agregado puede contener: está totalmente bien implementar cada estado como objeto independiente. Probablemente podría ir aún más lejos: chatee con su experto en dominios. Lo más probable es que él o ella no vea este agregado como una entidad unificada. Probablemente cada estado tiene su propio significado, mereciendo su propio objeto.
Finalmente, me gustaría señalar que el proceso de búsqueda de objetos es muy similar con la descomposición del sistema en subsistemas . Ambos se basan en el comportamiento, no en otra cosa.
fuente