Introducción
Robert L. Glass argumentó que la práctica precede a la teoría. El desarrollo de software, en particular, es un campo donde la práctica se adelanta a la teoría, y los desarrolladores aprenden más cuando se ensucian las manos creando código concreto. Por lo tanto, dejaremos la teoría y los conceptos para más adelante y veremos un programa simple.
Implementar una aplicación de venta de entradas
- Queremos organizar un pequeño evento para promocionar un pequeño teatro.
- Contenido del evento: enviar invitaciones a los espectadores seleccionados al azar para que puedan asistir a la función de forma gratuita.
- Debemos separar a los espectadores que ganaron el evento de los que no lo hicieron.
- Espectadores que ganaron el evento: cambiar la invitación por una entrada.
- Espectadores que no ganaron el evento: comprar la entrada con dinero.
Código de práctica: https://github.com/Java-Crew/objects-study/tree/jayon/src/main/java/chapter01/step01
¿Qué está mal?
Robert Martin explica tres funciones que debe tener un módulo de software (cualquier elemento que compone un programa, independientemente de su tamaño, como una clase, un paquete o una biblioteca).
- Funciona correctamente durante la ejecución.
- Existe para cambios.
- Debe ser posible realizar cambios con solo una pequeña cantidad de trabajo.
- Se comunica con las personas que leen el código.
- Los desarrolladores deben poder leerlo y comprenderlo fácilmente.
La aplicación de venta de entradas anterior cumple con la primera restricción de funcionar correctamente, pero no cumple con los objetivos de facilidad de cambio y comunicación.
Código que no cumple con las expectativas
Veamos por qué no cumple con el objetivo de la comunicación.
- Lo que hace el método enter() de la clase Theater
- El teatro abre el bolso del espectador para ver si hay una invitación dentro.
- Si hay una invitación en el bolso, le dice al vendedor de entradas que mueva la entrada que está en la taquilla al bolso del espectador.
- Si no hay una invitación en el bolso, saca el efectivo del bolso del espectador por el precio de la entrada, compra la entrada y la coloca en el bolso.
- Desde el punto de vista del espectador, el teatro, un tercero, tiene que hurgar en su bolso, tomar su dinero y poner una entrada, sin su consentimiento.
- Desde el punto de vista del vendedor de entradas, el teatro, un tercero, manipula las entradas y el efectivo en la taquilla sin permiso.
- Un código comprensible es un código que no se desvía mucho de nuestras expectativas, y este teatro no cumple con nuestras expectativas.
- El espectador debe sacar el dinero de su bolso y pagar al vendedor de entradas para recibir la entrada.
- El vendedor de entradas debe sacar la entrada de la taquilla y entregársela al espectador, y recibir el dinero del espectador y guardarlo en la taquilla.
- Además, para comprender el método enter(), debe recordar varios detalles.
- Audience tiene un objeto Bag.
- El bolso contiene efectivo y entradas.
- TicketSellet vende entradas en TicketOffice, y TicketOffice almacena dinero y entradas.
Código vulnerable a los cambios
El método enter() asume dos condiciones.
- El espectador siempre lleva un bolso para guardar efectivo y invitaciones.
- El vendedor de entradas solo vende entradas en la taquilla.
¿Qué pasa en las siguientes situaciones?
- El espectador puede no tener un bolso.
- El espectador puede pagar con tarjeta de crédito en lugar de efectivo.
- El vendedor de entradas puede vender entradas fuera de la taquilla.
Por ejemplo, para satisfacer el primer requisito, debemos eliminar el objeto Bag de Audience y modificar el método enter() de la clase Theater relacionado con él. Esto se debe a que la clase Theater depende demasiado del hecho de que el espectador lleva un bolso y el vendedor de entradas solo vende entradas en la taquilla. Si cualquiera de estos detalles cambia, debemos modificar la clase en cuestión y las clases que dependen de ella (por ejemplo, Theater).
Este es un problema relacionado con la dependencia entre objetos, y la dependencia implica el impacto de los cambios. Sin embargo, el objetivo del diseño orientado a objetos es construir una comunidad de objetos que cooperen y dependan unos de otros, por lo que no debemos eliminar las dependencias sin más, sino mantener solo las dependencias mínimas necesariasy eliminar las dependencias innecesarias.
Cuando la dependencia entre objetos es excesiva, decimos que el acoplamientoes alto, y cuanto mayor sea el acoplamiento entre dos objetos, mayor será la probabilidad de que cambien juntos. Por lo tanto, el objetivo del diseño es reducir el acoplamiento entre objetos para crear un diseño que sea fácil de cambiar.
Mejorar el diseño
Theater no necesita saber que el espectador lleva un bolso ni que el vendedor de entradas vende entradas en la taquilla. Theater solo quiere que el espectador entre en el teatro. Por lo tanto, debemos hacer que el espectador maneje el efectivo y la invitación en su bolso y que el vendedor de entradas maneje las entradas y el precio de venta en la taquilla de forma autónoma.
Aumentar la autonomía
Decimos que un objeto que realiza solo tareas estrechamente relacionadas y delega las tareas no relacionadas a otros objetos tiene una alta cohesión. Crear objetos autónomos que manejen sus propios datos reduce el acoplamiento y aumenta la cohesión.
Orientación a procedimientos y orientación a objetos
- Orientación a procedimientos
- El método enter() de Theater es un proceso, y Audience, TicketSeller, Bag y TicketOffice son datos.
- La forma en que los procesos y los datos se colocan en módulos separados se llama programación orientada a procedimientos.
- Hay mucho código que va en contra de nuestra intuición. (por ejemplo, el espectador maneja su propio dinero e invitación).
- Es difícil reducir el impacto de los cambios de datos.
- La responsabilidad se gestiona de forma centralizada. (Theater lo gestiona todo).
- Orientación a objetos
- La forma en que los datos y los procesos se colocan dentro del mismo módulo se llama programación orientada a objetos.
- Podemos escribir código que se ajuste a nuestra intuición.
- Podemos reducir eficazmente el impacto de los cambios de datos mediante la encapsulación.
- Cada objeto es responsable de sí mismo.
Código de práctica: https://github.com/Java-Crew/objects-study/tree/jayon/src/main/java/chapter01/step02
Se puede mejorar aún más.
- La clase Bag de la clase Audience sigue siendo una entidad pasiva arrastrada por la clase Audience, por lo que debemos convertir la clase Bag en una entidad autónoma.
- TicketOffice de la clase TicketSeller también está controlado a voluntad por TicketSeller. Debemos convertir TicketOffice en una entidad autónoma.
- Sin embargo, después del cambio, TicketOffice se ha acoplado adicionalmente a Audience.
- Como este, el diseño debe considerar cuidadosamente las compensaciones. En este caso, podemos llegar a un acuerdo para hacer que TicketOffice sea una entidad algo pasiva para reducir el acoplamiento con Audience.
Código de práctica: https://github.com/Java-Crew/objects-study/tree/jayon/src/main/java/chapter01/step03
¡Eso es mentira!
- En la realidad, incluso si es una entidad pasiva, todo se convierte en una entidad activa y autónoma una vez que entra en el mundo de la orientación a objetos.
- Es bueno usar la personificación para pensar en las entidades pasivas como si estuvieran riendo, hablando y enfadándose.
Diseño orientado a objetos
¿Por qué es necesario el diseño?
- El diseño es la colocación del código.
- Un buen diseño es aquel que realiza completamente las funciones requeridas hoy y puede acomodar los cambios futuros sin problemas.
Diseño orientado a objetos
- El código que se puede cambiar es código fácil de entender.
- El paradigma orientado a objetos nos ayuda a escribir código de la misma manera que vemos el mundo.
- Los objetos son entidades autónomas que son responsables de sus propios datos.
- Un buen diseño orientado a objetos es un diseño que gestiona adecuadamente las dependencias entre los objetos que cooperan.
Referencias
- Objetos
Comentarios0