Por qué NO usar getters y setters | Tell don't ask
HTML-код
- Опубликовано: 28 авг 2024
- 🔥 ¡Aprovecha la oferta del Black Friday de CodelyTV Pro!
⮕ codely.tv/pro/...
---
Material del vídeo: codely.tv/scree...
¿Usas getters y setters? ¿Te suenan los modelos de dominio anémicos? ¿El principio Tell don't ask? ¡En este vídeo aprenderemos más acerca de estos conceptos!
Índice:
0:19 Pregunta: En PHP se accede a atributos de clase públicos, ¿uso getters y setters?
1:17 Principio de programación aplicable a todo lenguaje
2:00 Modelo User con atributos de clase públicos
3:19 Diferencia entre Data Transfer Object (DTO) y modelo de dominio. Qué es un modelo de domino anémico
4:14 Qué problemas tiene usar atributos de clase públicos
4:50 Modelo User como array asociativo/hashmap
6:00 Qué problemas tiene usar arrays asociativos para el modelado de dominio
6:50 Modelo User con atributos de clase privados, getters y setters (modelo anémico)
7:17 Beneficios de usar getters y setters
8:00 Por qué NO usar getters y setters
9:12 Modelo de dominio User con comportamiento
10:47 Qué beneficios tiene usar modelos de dominio ricos (con comportamiento)
13:09 Conclusión: Ni atributos de clase públicos, ni getters y setters. Lo ideal: Lógica de negocio donde corresponda (alta cohesión)
muy bien vídeo! Aún así tengo un par de dudas respecto a como tratarías un objeto con 10 propiedades, y donde una de ellas fuera un value object EmailAddress. Y después me gustaría saber como lo modificarías una vez ya lo tienes creado, ya sea por que recuperas una entity de la base de datos o por que debido a un suceso posterior necesitas actualizarlo antes de persistirlo finalmente en base de datos
Buenas Pablo!
Respecto a la duda que planteabas (perdón por tardar en contestar :P):
* Objeto con 10 propiedades:
** Si esas 10 propiedades son relativas a un mismo concepto, intentaríamos juntarlas en un objeto que represente ese concepto (por ejemplo, si el `EmailAddress` pertenece a un usuario aún no autenticado, podríamos modelar ese concepto en una clase `UnauthenticatedUser`). De esta forma promovemos la cohesión de nuestros modelos de dominio, y evitamos ir pasando esas 10 propiedades por nuestro sistema.
** Si esas 10 propiedades son relativas a conceptos distintos, es posible que la clase que las recibe haga demasiadas cosas ya que necesita de otras dependencias muy diferentes. En ese caso intentaríamos dividir las responsabilidades de la clase que recibe esas 10 dependencias de tal forma que cada caso de uso únicamente haga 1 cosa (Single Responsibility Segregation de SOLID), y así evitar tener que recibir tantos argumentos.
* Modificar entidades ya construídas:
** A través del repositorio de ejemplo de DDD y CQRS te puedes hacer una idea. Primero recuperaríamos la entidad (por ejemplo a través de su correspondiente finder github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Application/Find/VideoFinder.php )
** Después llamaríamos a los métodos que tendría esa misma clase (en `Vídeo` github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Domain/Video.php podría haber un método `updateTitle`).
** Ese método de la entidad, registraría el evento de dominio correspondiente (por ejemplo `VideoTitleUpdatedDomainEvent` tal y como se hace con el de creación github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Domain/Video.php#L28 )
** El Application Service finalmente publicaría los eventos de dominio registrados por la entidad en el event bus (por ejemplo, aquí tienes el momento de publicación de los eventos del caso de uso de creación de vídeo ( github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Application/Create/VideoCreator.php#L30 )
** Finalmente, si hay alguna acción a realizar en base a la actualización del título del vídeo (por ejemplo notificar a los subscriptores), lo haríamos a través de la subscripción a ese evento de dominio (aquí el ejemplo de enviar notificación al crear un vídeo: Definición del servicio: github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Notification/Infrastructure/DependencyInjection/notification_services.yml#L6 e implementación del subscriber: github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Notification/Application/Create/CreateNotificationOnVideoCreated.php#L20 )
** Si te interesa más este tema, te recomendamos los vídeos de:
*** Introducción a Arquitectura Hexagonal: ruclips.net/video/GZ9ic9QSO5U/видео.html
*** De código acoplado al framework hasta microservicios pasando por DDD: ruclips.net/video/o0w-jYun6AU/видео.html
*** DDD y CQRS: Preguntas Frecuentes: ruclips.net/video/auEhX4WfCRA/видео.html
Esperamos haberte ayudado, ¡saludos!
@@CodelyTV Hola buenas, no funcionan los links a github para ver el ejemplo de como proceder a modificar entidades ya construidad. Saludos
@@CodelyTV excelente. Muy buena la pregunta.
@@CodelyTV no había dicho nada porque me pilló a contrapié la respuesta! xD En cualquier caso, más vale tarde que nunca así que muchas gracias!!
@@CodelyTV Thnx. Enlaces rotos del repo :). Es cierto que no deberiarmos recurrir a los "new Clase(..)" que deberiamos recurrir a la inyección de dependcias o factorias? Porque?
Chicos, enseñan muy bien. Es increíble que estas cosas hayan que explicarlas todo el tiempo. Los felicito.
Que grande Javi explicando Tell don't ask! Estos videos valen oro!
llevo ya un par de vídeos vuestro y creo que de cabeza iré a pagar la suscripción de vuestros cursos por soís oro!
mucha gracias excelente información, tambien garantizar la independencia de nuestras clases, y se puede usar patrones de diseño como el builder para crear objetos con distintas propiedades
Gran video, me ha gustado mucho como se ha refactorizado, solo he echado de menos que no se ha validado en el constructor que los campos email, fecha, etc, pudieran ser nulos, pero es solo un detalle, no creo que afecte al mensaje que quería transmitir el video, de nuevo me ha gustado mucho!
¿La validaciones las podríamos directamente en el constructor? ¿Cómo quedaría por ejemplo la validación de un formato correcto de correo electrónico?
En ese caso usaríamos el patrón Value Object para modelar concretamente el objeto Email. Y en el constructor de esta clase introduciríamos esa validación. De esta forma nos aseguraríamos de que cuando le llegue el valor de tipo Email al constructor de User, ya podamos asumir que es un valor correcto. Puedes ver un ejemplo en el repositorio del curso de Arquitectura Hexagonal: github.com/CodelyTV/cqrs-ddd-php-example/blob/master/src/Shared/Domain/EmailAddress.php
Buenas. Si tuviera que hacer más validaciones (en casi todos los atributos)? dni, teléfono, cp, país, provincia (dependiendo de país) etc. debería crear una clase de validación para cada atributo? y su a su vez, si estos atributos también forman parte de otra entidad, "cliente" pero con unas validaciones especiales... que los cp de los clientes empiecen obligatoriamente por XX que su teléfono tenga prefijo +n1n2, su dni no termine en J, etc. Como se atacaria esta causistica?. Gracias por el contenido.
13:10 No es recomendable tener publicos los atributos o propiedades de una clase. Esto permite que no se creen objetos inconsistente a partir de la clase. Para esto es mejor pasar los campos necesarios para construccion del objeto por el constructor de la clase, o mediante un named constructor [constructor semantico]. Y dejar todas las propiedades privadas.
Si en un futuro se desea modificar el estado del objeto [cambiar el valor de una propiedad], sera mejor crear un nuevo objeto con el nuevo valor del estado en vez de modificar el objeto anterior. Este permite que exista inmutabilidad en los objetos creados, y evitamos que puedan existir objetos con valores extraños de sus propiedades.
con los méodos mágicos __get y __set puedes implementar lo mismo pero de manera transparente para el usuario, $clase.algo = 4; se convierte en un llamado a un setter, de esa manera puedes tener por ejemplo atributos de read only y disparar exceptions si se tratan de sobreescribir ( muy al estilo pascal )
He escuchado que no se usan los else, luego los switch, luego static y ahora los geters y seters, lo próximo voy a ver sera que no es buena idea programar....
Lo que estás explicando responde al principio de encapsulamiento. QUé significa? Que no se pueden tener estados compartidos. Es decir: Que solo la case puede alterar su estado.
De otro modo, cualquier parte del sistema puede modificar el estado de la clase.
Con ellos se consigue un requerimiento no funcional llamado "seguridad".
El principal objetivo de todo desarrollador es controlar el estado de las abstracciones o "Clases".
Saludos.
tus videos son de gran ayuda, no se encuentra mucho contenido de esta calidad en español :)
Yo los uso en flutter para prevenir errores con null safety. Es decir, todas las propiedades de una clase las hago nullables, pero en los getters retorno el valor del atributo si no es null o un valor por defecto.
Excelente maestro!
Llego bastante tarde, pero, hasta donde yo sé, la convención dice que los nombres de los métodos han de ser verbos (acciones). El poner nombres de campos como getters (sin el "get"), no sería una violación de esta convención?
Un saludo y gracias. Muy de acuerdo con todo lo demás.
Gracias por el video, en mi opinion, loos getters y setters solo cumplen una funcion "sintactica" pero son equivalentes a acceder directamente a los atributos de una clase que, por ejemplo, sirva de simple contenedor de datos como un DTO. Ya que la mayoria de DTOs tampoco tienen restricciones que comprobar en sus getters ni setters y simplemente manipulan atributos. Igual que si accedieramos directamente a ellos. Esto no es una mala practica en casos puntuales, como el del DTO, pero si lo seria aplicado a otras clases que implementen una logica que manipula unos datos privados ya que romperian la encapsulacion. Lo que hace el video es "evolucionar" una clase desde DTO a otro tipo (que contiene por ejemplo, resitricciones), pero son dos casos diferentes (diriamos que ha dejado de ser un DTO). La pregunta seria si nunca hay que utilizar DTOs (para eliminar esa "mala practica"), pero sabemos que hay casos en los que son necesarios. Siendo indiferente que se acceda a sus atributos directamente o con getters y setters.
También depende mucho del lenguaje, no puedes aplicar esto a, por ejemplo, lenguajes funcinales como Haskell o F#, donde no importa tanto que los modelos sean anemicos (ya que como lenguajes se basan en funciones).
Exacto, para un no tipado vendría esa lógica, pero para un c# donde los atributos son funcionales.
Pero el comportamiento del ejemplo (ver si es el día del cumpleaños) se puede encapsular como bien se realiza en el video dentro de la clase User y por otro lado se puede tener getters y setters para poder ver y modificar con estos últimos determinados atributos. O sea, que el código para saber el cumple esté dentro de la clase no quita que sea posible modificar la fecha de nacimiento por ejemplo, mediante un set (con las validaciones que sean necesarias) o sino gustan los getters y los setters mediante un método con esas validaciones. Pero de vuelta a mi criterio se mezclan conceptos en forma no muy acertada como veo muy habitualmente en muchos videos de CodelyTV. Tener encapsulado el comportamiento no quita tener atributos que puedan ser vistos o modificados con las validaciones necesarias mediantes getters y setters o mediante métodos.
Medio largo el video pero excelente conclusión! Muy interesante el principio
Sé que está explicado en php pero el concepto es válido para cada lenguaje, es una buena práctica.
Luego llegó el concepto de Beans en Java y mandaron todo a la mierda, muchos frameworks se basan en ese concepto y es muy difícil programar como en el video porque los setter/getter son casi obligatorios y ni hablar de la inmutabilidad.
Sin embargo, a mí no me gusta mucho colocar lógica de negocio en ese tipo de clases (User, Product), porque muchas veces hay validaciones que son dependientes de configuraciones dinámicas en el sistema, por ejemplo, no poder registrar usuarios menores de 18 años, pero ese "18" es configurable por un administrador. Así que la clase User tendría dependencia de algo como UserConfiguration y necesitarias esa información a la hora de instanciasión... es decir, en algún punto hay que dar el brazo a torcer y casar un par de clases para toda la vida...
podrías hacer una nueva versión con un sonido no tan deficiente xD
Demasiado top este material.
Hola, muy buen video, me gustó mucho la explicación. Quiciera plantear una caso, supongamos que una clase tiene 20 propiedades, este patrón me obliga a tener los 20 valores en propiedades locales antes de llamar el constructor, eso no me termina de convencer, por otro lado el 99% de los casos el setter no realiza ningún tipo de validación
Eso me hace que pensar tmb amigo. Si tengo 20 atributos tendría que hacer un supercontructor
Gracias por tus vídeos!
en aras de la reusabilidad el principio de responsabilidad única sugiero usar un Trait para la función de fecha de nacimiento.
Buenas!
Se agradece la sugerencia! No obstante me surge una duda: ¿por qué un trait?
Hola tocayo 😉
Porque te permite tomar el comportamiento que necesites sin tener que implementar toda la interface...
En ese caso, seríamos más partidarios de modelarlo como un ValueObject como por ejemplo `BirthDate`.
De esa forma ganaríamos en tipado y testabilidad :)
CodelyTV - Redescubre la programación podrías indicarme el Github. Hay 9 repos y no logro encontrarlo! gracias!!!!!
PS: sobre la el ppo de responsabilidad única haría unos cuantos matices pero como este sitio es un poco incómodo te lo mandaré por correo...
un saludo!
Buenas!
¿A qué repo te refieres?
Sobre la duda de SRP, comenta sin problema en el vídeo ( ruclips.net/video/c97P1UmF1cs/видео.html ) y así también le puede servir a otra gente :)
Se puede decir que se estaria cumpliendo el principio solid abierto cerrado?
Hola Javi, que sentido tiene citar el artículo de alguien para explicar un tema (Tell don't ask), si en el mismo artículo que citas o referencias, te sale con esta joyita de artículo: martinfowler.com/bliki/GetterEradicator.html ... gracias por tu tiempo!
Yo casi no uso los setter en java, no los necesito, en cambio implemento un patrón builder (me apoyo en lombok). Qué logro con esto? Pues lo que logro es un objeto inmutable y con esto me aseguro que el objeto de principio a fin sea siempre el mismo
Hola Javier, buen video., pero tengo una duda al respecto, en el caso de que quieres persistir tu objeto (usando un ORM por ejemplo), no necesitarías poder extraer los datos de él? como lo harías?
Buenas Fernando!
Los ORM por lo general se basan en reflexión para la obtención de los valores de los atributos de clase. Con lo cuál, en ese caso no nos haría falta tener getters.
En caso de no usar un ORM, y no querer hacerlo por reflexión, no nos quedaría más opción que exponer los atributos a través de métodos que retornen su valor (getters). No obstante, el objetivo del vídeo no es negar por completo la opción de usar getters. El objetivo es promover lo que se cita en el vídeo acerca del principio de diseño "Tell don't ask", y evitar así tener modelos de dominio anémicos. Este tipo de modelos de dominio se basan en no contener nada de comportamiento (sólo getters y setters).
Aplicando estas técnicas (tell don't ask):
* Promovemos que la lógica relativa a nuestras entidades esté más cerca del punto donde se encuentran los atributos que se necesita para llevarlas a cabo.
* Evitamos duplicar esa lógica en los puntos desde los que se hace uso de nuestra entidad.
* Permitimos una mayor cambiabilidad de la estructura de nuestras entidades ya que los servicios externos no conocen cómo se representan determinados valores. Por ejemplo en el caso del método `isHisBirthday`, simplemente devolveríamos un booleano, y como se explica en el vídeo, los servicios externos no sabrían cómo se representa la fecha de nacimiento dentro del objeto `User` (si con un `DateTime`, con un timestamp, o con `day`, `month` y `year`, etc)
Esperamos haberte ayudado, ¡saludos!
13:40 Principio de Marín Tell Dont Ask
Entonces en lugar de los set y get creamos, personalizamos los métodos para solo conocerlos nosotros? Ya que si uso get o set, por más que los atributos sean privados, dichos atributos pueden modificados si alguien exterior utilizara los ya, demasiado, conocidos get y set?
Saludos y gracias!
No existen las propiedades (o atributos, como los llaman) inmutables en php? en c#, por ejemplo, creas propiedades inmutables que reciben los datos por el constructor, así se mantiene el estado, y se sugue teniendo acceso a los datos de la clase.
Muchas gracias
Muy bien explicado, Javier :-)
Gracias +Isidro López !
Si ves alguna cosilla que no te acaba de cuadrar o cualquier sugerencia, comenta sin problemas, se agradece el feedback :D
Saludos
Si la utilización de getters y setters es incorrecta y una mala practica como mencionas por que los framework mas utilizados y potentes de PHP Symfony, Laravel, y el mis Zend los utilizan? Sabiendo que estos se basan en las mejores practicas de programación del lenguaje y de la industria
Buenas Ludwring!
Los matices son importantes :D. Es decir, el mensaje que tratamos de enviar en este vídeo es: Delega la lógica de negocio de tus modelos a sus propias clases en la medida que esto sea posible.
El punto que quizá da lugar a malinterpretación del vídeo está en el enfoque que le decidimos dar, es decir, está enfocado desde los síntomas para detectar el problema (*abusar* de getters y setters), y no desde el problema en sí mismo (repartir la lógica que le pertoca a los modelos de dominio por toda nuestra aplicación).
Dicho esto, lo que comentas es un MUY buen punto. Es decir, todo esto se basa en la teoría que se cita en el vídeo (principio de diseño "Tell don't ask" y evitar "anemic domain model", estas keywords te servirán para buscar más info y profundizar así en el tema). No obstante, habrá contextos en los que NO nos interese seguir estos principios, como por ejemplo, en caso de estar desarrollando una librería para terceros.
En este caso, quizá nos puede interesar tener unos modelos lo más livianos posibles para que, si el usuario final de ese código, decide hacer uso de éstos, no implique llevarse consigo toda una serie de lógica que quizá no necesita. En este caso, seguramente nos interesa más tener estas lógicas en una capa de servicios mucho más fuerte, para que así el cliente final de la librería use aquello que le interese.
Tienes más info en los comentarios del post en el blog correspondiente a este mismo vídeo: codely.tv/screencasts/no-getters-setters-tell-dont-ask/
Esperamos haberte ayudado a formarte un poco más de criterio al respecto. Al fin y al cabo, estas cosas muchas veces no van de "esto es lo correcto y cualquier otro enfoque es basura", si no que como comento, dependiendo del contexto y los matices del proyecto en el que trabajemos, nos interesará seguir una estrategia u otra. Eso sí *siempre deberemos saber a qué juego estamos jugando*, y evitar así acabar en una maraña de estilos que no haya por donde cogerla :D
Saludos!
+CodelyTV Saludos desde tu respuesta deberías cambiar el titulo del vídeo a algo como "Contexto en el que NO usar Getters y Setters"
En mi opinión el título esta bien, como mucho se podría decir lo mismo que dice Javier al final "no usar getter/setter por convención" no ha dicho que no se usen jamás, sino que no es buena práctica usarlos porque sí sin cabeza
Los comencé a seguir hace un par de días y felicidades por los excedentes videos.
Una duda: En el caso de tener un objeto por ejemplo con 12 atributos. 3 obligatorios y el resto opcionales. Encontré que para esos casos es de gran ayuda el patrón builder para construir el objeto. Viendo ejemplos entiendo que lo normal sería pasar los obligatorios por constructor y el resto por haciendo uso del builder.
En el caso que necesite cambiar el estado de un atributo del objeto ¿Lo mejor sería crear un nuevo objeto pasándole el dato actualizado? O cuál sería la mejor manera de afrontar estas situaciones?
Gracias antemano.
Hola, excelente video, solo tengo una duda ya que voy empezando con POO, que pasa si quiero crear una validación del email? La mejor practica es crear una función en la clase o hacer todo dentro del constructor?
Tengo la misma duda
En ese caso usaríamos el patrón Value Object para modelar concretamente el objeto Email. Y en el constructor de esta clase introduciríamos esa validación. De esta forma nos aseguraríamos de que cuando le llegue el valor de tipo Email al constructor de User, ya podamos asumir que es un valor correcto. Puedes ver un ejemplo en el repositorio del curso de Arquitectura Hexagonal: github.com/CodelyTV/cqrs-ddd-php-example/blob/master/src/Shared/Domain/EmailAddress.php
Muy buen vídeo, he estado usando getters y setters (norrmalmente trabajo con ZF2 y en breve 3) pero voy a aplicar lo que explicas, sin duda esta mucho mejor planteado de esta manera. Me surge una pregunta: ¿Si un atributo de nuestra clase no es obligatorio que tenga valor, es decir nos da igual que pueda ser null, también lo pasarías al constructor o en ese caso si tiene sentido definirse un setter y setearlo sólo cuándo nos interese?
Buenas Alberto, buena esa :)
Supongamos que nuestro `User` puede que tenga valor para `PostalAddress` o no. Aquí tenemos varias opciones y optaríamos por una de ellas dependiendo del contexto:
* a) Modelar 2 entidades de user ya que representan casos distintos. Una con el atributo para `PostalAddress` (`User`) y otra sin (`Admin`).
* b) Modelar esa opcionalidad bajo un Option (en lenguajes como Scala o Java son tipos propios del lenguaje. En PHP nos podríamos crear nuestra propia implementación). Aquí la intro al concepto en Scala: danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html . No obstante, el concepto es muy sencillo: Un `Option` tiene 2 subtipos: `Some` y `None`. De esta forma hacemos explícita la posibilidad de que ese valor esté establecido (sería un `Some[PostalAddress]`), o no (sería un `None`).
Evitaríamos así la opción que proponías de no pasarle el valor y luego establecerlo vía setter. Haríamos esto debido a que esa implementación permitiría instanciaciones de nuestro modelo de dominio que serían inconsistentes (caso a), o esconderían esa opcionalidad detrás de un setter que nos obligaría a conocer los detalles de nuestro `User` en vez de hacerlo explícito (caso b).
Esperamos haberte ayudado, ¡saludos!
Obligar a que se pasen parámetros por el constructor equivale a cumplir con los campos required o constraint en bd? Para el resto de campos (opcionales) si se podría crear getters y setters? Si voy a persistir un nuevo usuario y tengo un campo autoid que es pk como se gestionaría por constructor?
¿Y conocer la fecha actual pertenece al ámbito del usuario?
desde el momento que hizo una validacion if condition { true } ; return false, ahi perdio credibilidad en todo aspecto, segundo, si bien es cierto que esas invocaciones de modelo son transparentes para las otras capas, tambien es cierto que el cambio en los campos sean privados o no siempre tendrá impacto en la demas estructura, el como se guarda un usuario (para este ejemplo cambia) ya sea para crear, actualizar o para su uso.
Son muchas incongruencias que tiene el video, que basicamente busca incentivar el DDD lo cual no es malo pero no por esos argumentos tan carentes de sentido
Y luego para persistir la información?
Excelente, nunca me parecieron una forma muy elegante y aparte que se generaba código poco útil, pero con este ejemplo todo muy bonito. Como objetivo del video entiendo tratar de buscar la mejor forma para omitir los getters y settes. Es correcto o la idea es quitarlos completamente? Y si fuera así es posible en el 100% de los casos?
Al final aplicamos el concepto de RAII(Resource acquisition is initialization)
que bien explicado ❤
Creo que el título es un poco confuso… da a tendner otra csoa
isHisBirthDay es básicamente un Getter de una propiedad "virtual" igual que podría ser getFullName que sería una propiedad compuesta… todo correspondiente a los datos del modelo.
Caso rápido que se me ocurre de por que usar setters getters … "data binding" Tienes un modelo con 10 propiedades 3 obligatorias. Envías un formualrio o api y quieres hacer data bind de los datos. si las pones en el constructor no vas a poner 10 , si vas a poner 3 , ya rompes el dinamismo y tendrás que definir en cada caso cuales van a constructor cuales no y empeiza a complicarse todo no podrás crear el objecto. Esto por ejemplo se usa con forms de laravel, ORMs etc. etc.
Igual también hay muchos casos de mantenibilidad que se me ocurren. Muchas más ventajas de usar que no. Pero como dije creo que el problema es el título no? Quizás se me escapa algo de la explicación
Buenas!
El título hace referencia al principio tell don't ask, que es justamente lo que estamos aplicando. Me explico 🙂
A pesar de que el método isHisBirthDay devuelva algo, y por lo tanto sea un query method, lo que queremos decir con que no es un getter, es que no expone el modelado interno de la clase. Es decir, encapsula la lógica de dominio necesaria para determinar si es el cumpleaños o no de un determinado usuario en vez de, como se explica en el vídeo, devolver la fecha de nacimiento delegando así esa lógica en una capa de servicios que use nuestro modelo.
Es a eso a lo que nos referimos: Encapsulación del modelado interno de nuestros datos.
Sobre el ejemplo que pones, justamente se trataría de un caso donde nos veríamos forzados a implementar nuestros modelos de forma que permitan instanciaciones en estados incosistentes. Por ejemplo, el supuesto usuario podría ser que en un determinado momento no tuviera email o identificador. Propiedades que, dependiendo del contexto, podríamos entender como imprescindibles para la entidad usuario. Por este motivo, es por el cuál para el problema que planteas (data binding de formularios), lo que haríamos sería tener un DTO en nuestra capa de interacción con base de datos que, una vez validado el formulario, daría pié al caso de uso de registrar usuario donde sí nos encontraríamos que se intenta instanciar nuestro modelo de dominio usuario. El cuál no tendría setters innecesarios. Consiguiendo así que no permitamos instanciaciones en estados inconsistentes, y simplificando la API (es decir, los métodos que expone públicamente) la clase usuario.
¡Saludos!
Aún sigo pensando que el título causa confusión :P pero gracias por responder. Seguid haciendo vídeos los de anti patterns estaban genial. Saludos.
Entiendo el conflicto setters vs modelo anémico, pero sin setters ¿cómo implementas el caso de uso de update (el de campos básicos, no agregados)?
Porque la entidad hay que recuperarla del repositorio y cambiar algunos campos sueltos...
Gracias por adelantado
Creo que muchos de los comentarios qué hay aquí y que dicen que Javier está mal, es porque no entienden la separación de responsabilidades o la diferencia entre modelos.
Pensaría yo, aunque no esté 100% seguro es que pra estos escenarios cuando recuperas un usuario de base de datos, puedes crear una copia de tu modelo de dominio pasándole la diferencia, que sería el email. Y luego volvería a solicitar a tu repositorio que te guarde ese modelo de dominio e internamente mapear tú modelo dominio a modelo de base de datos y persistirlo
@@kmiiloberrio-dev
Ok, pero ¿pero cómo se pasa la información de la BD al objeto sin setters?
En PHP hay un constructor que no invoca new pero me parece un tanto artificioso...
El cambio de un campo simple (el email, p.e.) es perfectamente posible aunque sea con un nombre semántico (changeEmail en vez de setEmail) pero es lo mismo...
@@javi68yt2 por constructor o revísate el comentario fijado, creo que alguien tuvo esa duda similar y la respondieron básicamente con algunas estrategias para llevar acabo esto.
Oe tio ya nos quedan pocas optiones con que programar. Setter and getters; statics; for loops; abstract class y luego que sera? porque no usar ni java, javascript, python, c++, c, etc ? xD
Y donde queda el encapsulamiento? sea PHP, Java, Python POO es POO.
Justamente con este tipo de técnicas estamos fomentando el encapsulamiento. Es decir, desde el momento en el que promovemos que sean las clases quienes tengan la lógica (Modelos de dominio ricos en comportamiento), evitamos que estas sean meros contenedores de datos para moverlos entre un servicio u otro donde sí implementemos la lógica asociada a la clase (modelos de dominio anémicos). Con lo cuál, la POO justamente se ve potenciada ya que estamos fomentando la cohesión del comportamiento con sus datos.
¡Saludos!
Clickbait. Quito los accesos porque no los uso, bien! Utilizo GRASP! Ok! Esto es básico en OO, y?
Hay demasiadas cosas que están mal en este vídeo, pero se agradece el esfuerzo
cuáles?
@@ivanparedes7894 Pues el que yo veo más grave es hacer una operación de un atributo de la clase usuario en la propia clase usuario, estás acoplando el código, y si quieres usar esa función por ejemplo en otro sitio, vas a tener que importar la clase usuario cuando realmente esa operación se puede meter en un controlador o en otra clase completamente distinta y llamarla pasando por parámetro el objeto con el que quieres operar. Decir que quitas los setter para "evitar que te modifiquen el estado de la clase" es una completa barbaridad. Si por lo que sea quieres cambiar el email del usuario, la mejor manera es a través de un setter.
Luego dice otra barbaridad diciendo que haciendo eso que hace "reduce el acoplamiento y aumenta la cohesión" cuando está haciendo todo lo contrario, y por si fuera poco, dice encima que está "ocultando" la implentación, cuando para eso ya tenemos en PHP las interfaces que hacen exactamente eso, ocultar la implentación de las clases a los clientes.
Cómo dice el compañero, el esfuerzo es positivo, pero en este canal en concreto he visto muchísimos vídeos que tienen fallos graves de conceptos que los están explicando mal a la gente.
Tocando el tema de SOLID, en single responsability dice que una clase debe tener una sola responsabilidad, entonces en un CRUD de usuario no se puede tener una clase usuario que tenga sus 4 metodos de CRUD? es decir una clase debe ser CreateUser otra UpdateUser y así?, no me queda muy claro como poner en practica ese principio.
Una sola responsabilidad no significa un solo método. Un CRUD puede ser una responsabilidad.
es decir que esto solo aplica en java, pero en php en el constructor de la clase va la lógica, sin necesidad de instanciar la clase y llamar los respectivos métodos!!, es buena practica usar NULL O " ", EN LAS PROPIEDADES, como parametros del constructor??, ya que no siempre vas a introducir todos las propiedades, porque una búsqueda solo seria por id o por nombre , siera algo asi $objP = new Persona(null,"Maria").
+BrabuZ Buenas!
No entiendo muy bien lo que comentas de que sólo aplicaría a Java y no a PHP. Lo que comentamos en este vídeo es justamente lo contrario, es decir, que estos patrones o anti-patrones aplican de igual forma indistintamente del lenguaje de programación que se use :-/
Sobre lo que comentas de los valores a null en constructores, es un debate interesante. Próximamente haremos un vídeo sobre el tema :D
Por lo que he entendido de la pregunta, esque si tuviéramos que validar por ejemplo el email, si pondriamos un metodo que se encargara de eso, que sería llamado desde el constructor.
Buenas Samuel!
Perdona por tardar tanto en responder, ahora hemos hecho repaso de comentarios y hemos visto que este se nos coló :P, te comento:
En el caso concreto que planteas, personalmente optaría por aplicar un patrón de diseño denominado Value Object. Básicamente consiste en encapsular ese valor de email en un clase específica para ello (EmailAddress). De esa forma, en el momento de construir una nueva instancia de esta clase (public function __construct($rawEmailAddress)), ya validaríamos que fuera un email correcto.
Así, ganaríamos semántica en la firma del resto de métodos, por ejemplo en el método sendEmail ya recibiríamos un $target de tipo EmailAddress y un Copy de tipo EmailAddress[] por ejemplo. Además, no necesitaríamos duplicar la responsabilidad y lógica de validación de emails allá donde tratásemos con emails, ya que nos podríamos fiar de que si la instancia de EmailAddress se ha podido llegar a construir, es porque es una dirección válida (si no, en el momento de construcción, saltaría excepción :)).
Espero haberte ayudado, en cualquier caso, próximamente tocaremos este tipo de temas cuando hablemos de modelado del dominio en la serie de Domain-Driven Design :D
Saludos y gracias por tu comentario!
if true -> return true; else -> return false. Like si lo viste.
7:40 yo haciendo la validación en la capa controlador xD
Es bueno sabe que puedo mejor hacerla en ese punto
gracias de antemano por el contenido te sugiero mejorar la calidad de la voz porque se hace dificil seguir lo que dices toca subirle mucho el volumen hay muchos hizz lo cual causa dolor de cabeza. te aseguro que con un mejor microfono se te multiplicarán las vistas., gracias. master.
Es un poco ambiguo eso de pasar los parámetros al constructor de la clase, para crear nos viene bien pero en el caso de obtener uno o más usuarios, no son necesarios esos parámetros
En el caso de obtener uno o más usuarios también vas a utilizar el constructor cuando crees los objetos del dominio que devuelven tus consultas. Si usas algún ORM es posible que esta creación de objetos del dominio la delegues en el mapper que éste ofrezca.
Pffff. pues yo no le veo mal uso a los getters. Los setters, bueno, te lo medio compro, pero cuando tienes propiedades que no sean obligatorias, necesitas setters o algún tipo de solución alternativa. Yo sigo usando getters y setters pero única y exclusivamente de las variables que necesito o recuperar o setear. Y no de todas. Eso de botón derecho, completar código, crear getters y setters hace mucho que no se hace.
¡Saludos!
pero si mi user tuviera mas atributos(Ejemplo 20 atributos), el constructor debería recibir 20 parámetros o ¿se podría recibir de alguna otra forma?
Si una clase tiene 20 atributos, lo más probable es que esté mal diseñada.
@@MaximoPower2024 bueno y si tuviera 15?
@@ronyaleittelagos4803 ja ja. Si los necesitas. Ese constructor va a estar denso. Comparto que muy probable pudieras generar algún tipo de agrupación que además puedas re usar. Pero eso lo sabrás vos. Saludos
@@cristianmosteiro6458 y si tuvieras que pasar los datos del un cliente para grabar. ¿Cómo lo haces?
@@ronyaleittelagos4803 desde frontend?
Excelente ! Me llevo algunos conceptos para usar aca: medium.com/dise%C3%B1o-de-software/information-showing-chapter-i-setters-138deb558e5d
Genial!! una pregunta, si tengo atributos que no son "requeridos", si podria tener los set y get de ellos??? o siempre irian en el. constructor?
Hola amigo, me animo a responderte. Creo que los getters and setters sirven justamente para estos casos, cuando tenés atributos opcionales que podrían ser completados luego de la creación del objeto. En este caso perfectamente podrías usar un Set para establecer el valor del atributo (inclusive hacer validaciones antes de setear el valor) y Get para leer su valor.
Por otro lado, los getters and setters son parte del comportamiento de la clase.
@@jesusarce7627 Muchsimas gracias!
Una pregunta. Si quitas el setter del email, cómo harías para editarlo?
¡Buenas Juan!
Efectivamente habrá situaciones en las que necesitaremos actualizar el valor de alguno de los campos de nuestras entidades. Aquí no tenemos más remedio que tener algún tipo de método para ello. No obstante, el punto de este vídeo es el de destacar que a pesar de que tengamos esta necesidad, ello no implica que apliquemos la generación automática de Getters y setters.
Aquí tienes el ejemplo que poníamos en un comentario anterior:
** A través del repositorio de ejemplo de DDD y CQRS te puedes hacer una idea. Primero recuperaríamos la entidad (por ejemplo a través de su correspondiente finder github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Application/Find/VideoFinder.php )
** Después llamaríamos a los métodos que tendría esa misma clase (en `Vídeo` github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Domain/Video.php podría haber un método `updateTitle`).
** Ese método de la entidad, registraría el evento de dominio correspondiente (por ejemplo `VideoTitleUpdatedDomainEvent` tal y como se hace con el de creación github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Domain/Video.php#L28 )
** El Application Service finalmente publicaría los eventos de dominio registrados por la entidad en el event bus (por ejemplo, aquí tienes el momento de publicación de los eventos del caso de uso de creación de vídeo ( github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Video/Application/Create/VideoCreator.php#L30 )
** Finalmente, si hay alguna acción a realizar en base a la actualización del título del vídeo (por ejemplo notificar a los subscriptores), lo haríamos a través de la subscripción a ese evento de dominio (aquí el ejemplo de enviar notificación al crear un vídeo: Definición del servicio: github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Notification/Infrastructure/DependencyInjection/notification_services.yml#L6 e implementación del subscriber: github.com/CodelyTV/cqrs-ddd-example/blob/master/src/Context/Video/Module/Notification/Application/Create/CreateNotificationOnVideoCreated.php#L20 )
Cualquier cosa, aquí estamos :)
Muchas gracias por la respuesta! Le echaré un ojo a esos enlaces ;)
Creo que soy el primero en darse cuenta que si construyes dentro de una clase una propiedad private email y luego creas un metodo email tendras un conflicto de nombre xD
Sorry pero gente y setters se usan para properties. Que son públicas. Para atributos,que son privados no se usan.
Entones es malo usa DTO :O yo lo uso en todos lados
Hola
lombok ;)
No me convence tu explicación sobre poner validaciones en los métodos de un modelo.
Es mas práctico el dto ya que solo es un contenedor de datos, no importa si la data está correcta o no.
Esas son reglas de negocios que se deben poner en un repositorio.
7:50 no se por que tienes que crear un usuario sin email ?? puedes pasarle el email como parámetro y si devuelve falso salta una excepciona y no se crea la clase. el problema esta en la lógica no en el getter y el setter. por favor no enseñéis malas costumbres.
no entendí como se supone que tiene que ser y como no usar los getter o setters xd
La desventaja de los constructores es cuando los atributos son muchos, se pierde legibilidad al instanciar, algo que se gana si se usaran los setters.
Este video es de hace 5 años y no se por que yo los uso xD
No se escucha nada, lástima
porque siempre quieren dañar la poo, porque no reiventan la poo ustedes entonces?
No sabes programar y lo peor es que confundes a la gente que no sabe.
Buenas Raúl! Encantado de seguir aprendiendo 🙂
En el post del vídeo tienes el material relacionado: codely.tv/screencasts/no-getters-setters-tell-dont-ask/
Verás que lo que explicamos en el vídeo se basa en principios ampliamente aceptados en la industria como "Tell don't ask", o evitar caer en "Anemic Domain Models". En el post enlazamos a referencias de Martin Fowler al respecto por ejemplo. No nos inventamos nada vaya, sólo tratamos de explicarlo a nuestra manera para hacer divulgación de lo que consideramos como buenas prácticas de programación por si eso le ayuda a más gente 🙂
Te recomendaría que le echases un vistazo al resto de vídeos del canal. Estoy seguro de que podrás hacerte una mejor idea de lo que tratamos de transmitir 🙂
¡Saludos!
primero aprende un paradigma, tu respuesta en el vídeo de decir que no es la manera habitual de trabajar en PHP demuestra que no sabes programar. Y cómo puedes decir que es una mala practicar usar Getters y Setters, joder!! vuelvo apoyar mi primer comentario. Confundes a los que inician a programar, lee correctamente la documentación. En ningún lado dicen que es malo es bueno, hacen sus recomendaciones en base a las necesidades el sistema a desarrollar con sus pro y contras.
@@rafexnene what?? No defiende utilizar propiedades públicas en lugar de get/set, habla de prescindir del set mediante el constructor para garantizar la consistencia del domino (y de paso favorecer la inmutabilidad), y no utilizar el getter por convención manteniendo el negocio dentro del dominio y mejorando los niveles de abstracción que este expone.
Te has visto el vídeo o sólo el título?
yo creo que tú realmente tampoco lo viste ni lo entendiste. Por ejemplo el minuto 5:53 dice que PHP es un lenguaje que no es fuertemente tipado WTF!!?? es idiota el tipo o que le pasa, no sabe que es fuertemente tipado y explícitamente la declaración de tipo de una variable. En el minuto 1:22 dice que no es la forma de ser del PHP, WTF así o más orate, PHP es multi paradigmas. En el minuto 1.38 dice que usar getter y setter son malas practicas JODER, en serio analizaste el vídeo o también eres orate. Y precisamente como se ve que no conoce de patrones de diseño, eso que "intento" explicar (apartir del minuto 9.30 dónde habla de los constructores y clases con lógica de negocio), debió aclarar la poca extensibilidad que te da esta idea, que no es mala la documentación a que hace referencia no esta mal, pero a lo que me refiero que toda esa falta de información y conocimiento lo que genera es generar vicios.
@@rafexnene en parte tienes razon es un poco confuso como trata de explicar el DDD camuflado en una critica a los beans, pero para alguien q empieza en esto le pica la curiosidad de investigar mas por su cuenta y revisar los links. Me parece q debio hablar mas de EJBs q es la causa de toda una generacion dividida.