Para resolver un problema como desarrollador es de gran utilidad dividirlo en subproblemas y generar un modelo que te permita implementar las soluciones en código. A lo largo de este curso vas a analizar Uber, una de las aplicaciones más usadas en el mundo, para entender cómo está construida. A partir de este análisis harás la extracción y definición de los objetos, clases y métodos que conforman la aplicación, usarás UML para modelarla y, finalmente, usando diferentes lenguajes como Java, PHP y Python harás la implementación de las clases y objetos de la aplicación.
¡Bienvenidos al curso de Programación Orientada a Objetos!
Te presentamos a la profesora de este curso: Anahí Salgado, ella ha sido desarrolladora por aproximadamente unos 8 años y la Programación Orientada a Objetos ha sido clave en su análisis y desarrollo para cualquier sistema, aplicación móvil, cualquier cosa con la que se ha topado por desarrollar. A la par ha sido profesora unos 6 años donde ha conjugado su pasión por enseñar al mismo tiempo que la de desarrollar y le ha encantado enseñar programación a todas las edades.
Hoy en día forma parte del Education Team en Platzi, podemos encontrarla en otros varios cursos en las rutas de Java, Android y Firebase. La Programación Orientada a Objetos ha sido clave en todos esos cursos siempre comienza analizando problemas, modelando problemas y al final programado las soluciones.
¿Porque es importante aprender Programación Orientada a Objetos?
Programar más rápido: Tener un análisis previo de lo que estas realizando te ayudará a generar código mucho más veloz en comparación a otros. Siempre que te tomes un tiempo para analizar, para retomar, para pensar lo que estás haciendo antes de ir directamente al código te ayudara a que programes mucho más rápido.
Dejar de ser Programador Jr.: Analizar tus problemas y entender mejor la Programación Orientada a Objetos dejaras de ser un Programador Junior. Para la mayoría de los reclutadores estas son las preguntas más frecuente: ¿Qué es encapsulamiento?, ¿Qué es Abstracción?, ¿Qué es Herencia?, ¿Qué es Polimorfismo?
Estas son una de sus preguntas favoritas, con la Programación Orientada a Objetos dejaremos de ser Programadores Junior y pasaremos a ser Programadores Senior, dominaremos estos conceptos que seguramente nos harán en una entrevista de trabajo de programación.
Dejar de copiar y pegar código: Esto es el síndrome de todos los Programadores Jr. que comienzan construyendo sus aplicaciones, trayendo trozos de código de aquí y allá hasta que finalmente arman su aplicación y se sienten super orgullosos. Pero, ¿Qué pasa con esas aplicaciones? Se convierten en pequeños Frankenstein donde de repente no sabemos de dónde comienzan ni por donde terminan, o en donde está la parte que está fallando. La POO te ayudara a tomar el control del proyecto, tomar el control del código, y entonces generar código de calidad, proyectos profesionales y por supuestos que dejes de copiar y pegar.
En resumen, al dejar de ser un Programador Jr. y poder analizar mucho mejor tus programas, poder programar mucho más rápido, dominar las entrevistas de trabajo podrás ser un Programador Sr y conseguir un mejor salario. Los reclutadores, los líderes técnicos, toda la gente de programación piden bases sólidas en POO, por eso las personas que llegan a dominar estos conceptos se llegan a considerar Programadores Sr. aquellos que saben aplicarlos para resolver problemas de la vida real.
¿Qué vamos hacer en este curo?
Analizar
Durante nuestro análisis lo que estaremos haciendo es observar, entender y leer muy bien la situación del problema, que está ocurriendo y comenzaremos a pensar de forma distinta.
Plasmar
Posteriormente iremos a un diagrama a plasmar nuestro análisis. A generar algo gráfico.
Lo recomendables es comenzar con un análisis técnico, un poco teórico, posteriormente por unos bocetos de papel y finalmente ir a los diagramas para generar un efecto gráfico. Algo que sea un poco más amigable
Programar
Aquí programaremos lo que acabamos de diagramar. En este curso no usaremos un solo lenguaje de programación, sino que aprenderemos varios, en cómo llevar nuestro análisis en varios lenguajes y tener una variabilidad.
En la clase anterior ya vimos cuán importante es la POO, así que ahora aprenderemos un poco lo que nos resuelve la Programación Orientada a Objetos.
Primeramente, si estas comenzando este curso de seguro vienes del curso Programación Estructurada donde estuviste aprendiendo a programar con C, resolviendo problemas con un lenguaje de programa generada de forma secuencial, es decir, una línea tras otra tras otra. Esencialmente la Programación Orientada a Objetos se dedica a resolver mucho de los huecos que la programación estructurada nos dejó en el camino. No es del todo malo, sino que a medida que van creciendo los problemas te darás cuenta que necesitamos reutilizar código, que sea más corto, que resuelva muchos problemas que la programación estructurada nos está dejando en el camino, y por supuesto la POO nace de todos los problemas dejados por la programación estructurada.
Problemas de la Programación Estructurada
Código Espagueti
El código espagueti es un término “despectivo” que se utiliza para los programas de computación que tienen una estructura de control de flujo compleja e incomprensible. Su nombre deriva del hecho que este tipo de código parece asemejarse a un plato de espaguetis, es decir, un montón de hilos intrincados y anudados.
Tradicionalmente suele asociarse este estilo de programación con lenguajes básicos y antiguos, donde el flujo se controlaba mediante sentencias de control muy primitivas como GOTO y utilizando números de línea.
Muchos programadores consideran que escribir código espagueti es un verdadero desastre, pero lo cierto es que no tiene nada de malo, si esto permite a la persona entender la comprensión del problema, lo inadecuado sería considerar que ese código está terminado. Lo más importante es utilizar la refactorización, es decir iterar sobre varios repasos del código.
Podemos decir que lo importante es ir de más a menos, en un principio el código espagueti puede ser la base enredada de lo que se quiere programar, pero al momento de refactorizar, se tendrá que ser cada vez más específico.
Un ejemplo del Código Espagueti:
Los Callback Hell también podrían considerarse como Códigos Espaguetis:
<h3>Clase 3 Paradigma Orientado a Objetos</h3>La Programación Orientada a Objetos (POO o OOP, Object Oriented Programming) viene de una filosofía o una forma de pensar, una metodología de pensamiento, que es la Orientación a Objetos.
Orientación a Objetos
Específicamente surge a partir de los problemas que tienen los programadores que necesitamos plasmar en código. Cuando tienes un sistema o reto que resolver con software o programación se considera un problema, una necesidad, algo que resolver. Y lo que sucede aquí es que no sabemos por dónde empezar, por dónde comenzar a analizar, a plasmar las líneas de código.
La Orientación a Objetos surge precisamente de la análisis que hacemos a nuestro problema para que posteriormente podamos plasmarlo en código. Entonces, analizar un problema en forma de objetos para posteriormente llevarlos a una solución de código, eso significa la Orientación a Objetos, que empieces a ver todo de forma que lo orientes a un objeto ubicado a los problemas y que sea mucho más sencillo llevarlo a una solución en código.
Entonces, lo que decimos es que la Programación Orientación a Objetos es un paradigma
¿Qué significa que sea un Paradigma?
Un paradigma es la teoría que suministra la base y modelo para resolver problemas.
La Orientación a Objetos lo que hace es resolver problemas. Es tener una manera de pensar orientada a objetos que nos permita resolver problemas para llevarlo a código.
Eso quiere decir que POO es un Paradigma de Programación Orientación a Objetos y se va a componer de 4 elementos:
Además de tener 4 pilares:
Existen muchos lenguajes para programar orientado a objetos, y por supuesto en tu camino como desarrollador te vas a encontrar con algunos de ellos o vas a definirte especializar con alguno de ellos. Se que vas apasionarte por un lenguaje, es la historia de todo desarrollador que ama el lenguaje con el que aprendió. Por supuesto, no te cases con ningún lenguaje si no que aprendas de todos.
Algunos de los lenguaje de Programación Orientada a Objetos son:
La mayoría de los cursos de Programación Orientado a Objetos te lo enseñan con Java, pero en este curso estaremos viendo varios lenguajes al mismo tiempo para entender como la POO se aplica en cada uno de ellos y así, al finalizar este curso, puedas elegir el que más te agrade.
Java
Java es un lenguaje de programación orientado a objetos especialmente diseñado para permitir a los desarrolladores disponer de una plataforma de continuidad. Java se distingue de otros paradigmas de la programación (como la programación funcional o lógica) porque los desarrolladores pueden retomar o actualizar algo que ya han acabado, en oposición a empezar de cero. Los objetos mantienen el código bien organizado y resulta fácilmente modificable de ser necesario.
Hay muchas aplicaciones y sitios web que no funcionarán, probablemente, a menos que tengan Java instalado y cada día se crean más. Java es rápido, seguro y fiable. Desde ordenadores portátiles hasta centros de datos, de consolas para juegos hasta computadoras avanzadas, de teléfonos móviles hasta Internet, Java está en todas partes, si es ejecutado en una plataforma no tiene que ser recompilado para correr en otra.
PHP
Es un lenguaje muy odiado o muy amado pasionalmente. PHP es un lenguaje de scripting de código abierto, destinado a desarrollar aplicaciones para la web y crear páginas web, favoreciendo la conexión entre los servidores y la interfaz de usuario. Las ventajas de PHP son su flexibilidad y su alta compatibilidad con otras bases de datos. Además, PHP es considerado como un lenguaje fácil de aprender.
Python
Python es un lenguaje de programación de propósito general muy poderoso y flexible, a la vez que sencillo y fácil de aprender cuya filosofía hace hincapié en una sintaxis que favorezca un código legible. Se trata de un lenguaje de programación multiparadigma, dado que soporta orientación a objetos, programación funcional (aunque en menor medida) y programación imperativa. No sólo eso, sino que además usa un tipado dinámico y multiplataforma.
JavaScript
JavaScript es un lenguaje de programación que se utiliza principalmente para crear páginas web dinámicas. Técnicamente, JavaScript es un lenguaje de programación interpretado, por lo que no es necesario compilar los programas para ejecutarlos. En otras palabras, los programas escritos con JavaScript se pueden probar directamente en cualquier navegador sin necesidad de procesos intermedios.
No conviene confundir JavaScript con Java, que es un lenguaje de programación muy diferente. La confusión proviene del nombre, registrado por la misma empresa creadora de Java (Sun Microsystems). JavaScript se creó posteriormente, y la empresa norteamericana lo que hizo simplemente fue cambiar el nombre que le habían puesto sus creadores al comprar el proyecto (LiveScript). El lenguaje de programación Java está orientado a muchas más cosas que la web desde sus inicios.
Entorno de Desarrollo
Es un conjunto de procedimientos y herramientas que se utilizan para desarrollar un código fuente o programa. Este término se utiliza a veces como sinónimo de entorno de desarrollo integrado (IDE), que es la herramienta de desarrollo de software utilizado para escribir, generar, probar y depurar un programa. También proporcionan a los desarrolladores una interfaz de usuario común (UI) para desarrollar y depurar en diferentes modos.
A la hora de elegir en entorno de desarrollo o IDE (Integrated Development Environment) es fundamental tener definido qué lenguaje de programación se va a utilizar tanto en el Frontend como en el Backend.
En nuestro caso usaremos un entorno de desarrollo que soporta todos los lenguajes que estaremos viendo en esta clase que es el: Visual Studio Code.
<h3>Clase 5 Instalando Visual Studio Code</h3>Pues que comience la aventura y digo aventura porque te darás cuenta de lo emocionante que será poder trabajar 4 lenguajes de programación en un solo entorno de desarrollo y sí, precisamente eso es lo que nos resuelve Visual Studio Code el cual será nuestro campeón en este curso.
Visual Studio Code lo puedes encontrar en las tres versiones básicas de Sistema Operativo (Windows, Mac y Linux) y lo puedes descargar directo en este enlace: https://code.visualstudio.com/download. Es muy ligero y basta con un Siguiente, siguiente, siguiente para instalar.
Cuando la instalación haya finalizado verás algo como esto:
¡Súper! Todo salió bien. Ahora pasemos a configurarlo para cada lenguaje.
Primero ubica la sección de Extensiones o en inglés Extensions, además de la barra de Search porque estaremos buscando la extensión para cada lenguaje.
Java
En la barra de Search Extensions escribe: Java Extension Pack y da clic en el botón verde Install.
Ahora, para tener una mejor experiencia en Debugging, instala el Debugger for Java, el cual encuentras siguiendo el procedimiento anterior.
Listo, terminamos con Java. Aprende más en este enlace.
Ahora vamos por Python.
Python
Comencemos instalando Python en nuestra computadora. Dirígete al sitio python.org y dale clic en el botón de Descargar.
Ve de la mano con el asistente hasta finalizar la instalación:
Terminaremos la configuración de Python en Visual Studio Code más adelante. Aprende más aquí.
Mientras tanto sigamos con PHP.
PHP
Para configurar PHP buscaremos la extensión PHP Server y pulsamos Instalar
JavaScript
En este caso no necesitamos instalar absolutamente nada, utilizaremos el editor con su configuración por defecto.
Comencemos nuestro proyecto
Ya está todo listo, ahora dejemos creado el proyecto.
Para esto seleccionaremos la opción Add workspace folder
A continuación creamos una carpeta llamada CursoPOOUber y damos clic en Add para finalizar. Ahora generemos esta estructura de carpetas para manejar los documentos correspondientes al lenguaje de programación:
Ahora que tenemos listo nuestro sistema de archivos terminemos la configuración de Python en VSC, vamos al menú View -> Command Palette y escribimos python “Seleccionar intérprete”, tal como se muestra en la figura.
¡Ya terminamos, estamos listos!
<h3>Clase 6 Diagramas de Modelado</h3>Es momento de comenzar a aprender que opciones tenemos para plasmar nuestros análisis, es decir, generar los gráficos que serán los intermediaros entre nuestra observación del problema y la diagramación.
Algunos diagramas de modelado son: OMT y UML
OMT
Técnica de Modelado de Objetos (en inglés, Object Modeling Techniques) es un enfoque de modelado de objetos para el modelado y diseño de software. Fue desarrollado alrededor de 1991 como un método para desarrollar sistemas orientados a objetos y para soportar la programación orientada a objetos. OMT describe el modelo de objeto o la estructura estática del sistema.
OMT fue desarrollado como un enfoque para el desarrollo de software. Los propósitos de modelar son:
OMT ha propuesto tres tipos principales de modelos:
OMT es un predecesor del Lenguaje de Modelado Unificado (UML).
Muchos elementos de modelado OMT son comunes a UML. Modelo funcional en OMT: En resumen, un modelo funcional en OMT define la función de todos los procesos internos en un modelo con la ayuda de “Diagramas de flujo de datos (DFD)”. Detalla cómo se realizan los procesos de forma independiente.
UML
Este es el modelado al que debemos tener como un aliado porque nos permitirá tener de forma visual lo que está plasmado en el código, además cuando el proyecto pase a otras manos o a otro equipo de trabajo esto es lo primero que nos pedirán: el diagrama UML.
Lenguaje de Modelado Unificado (en inglés, Unified Modeling Language) es un lenguaje de modelado de desarrollo de propósito general en el campo de la ingeniería de software que está destinado a proporcionar una forma estándar de visualizar el diseño de un sistema.
El UML está compuesto por diversos elementos gráficos que se combinan para conformar diagramas. Debido a que el UML es un lenguaje, cuenta con reglas para combinar tales elementos.
La finalidad de los diagramas es presentar diversas perspectivas de un sistema, a las cuales se les conoce como modelo. Recordemos que un modelo es una representación simplificada de la realidad; el modelo UML describe lo que supuestamente hará un sistema, pero no dice cómo implementar dicho sistema.
A continuación se describirán los diagramas más comunes del UML y los conceptos que representan:
Como ya viste UML significa Unified Modeling Language el cual es un lenguaje estándar de modelado de sistemas orientados a objetos.
Esto significa que tendremos una manera gráfica de representar una situación, justo como hemos venido viendo. A continuación te voy a presentar los elementos que puedes utilizar para hacer estas representaciones.
Las clases se representan así:
En la parte superior se colocan los atributos o propiedades, y debajo las operaciones de la clase. Notarás que el primer carácter con el que empiezan es un símbolo. Este denotará la visibilidad del atributo o método, esto es un término que tiene que ver con Encapsulamiento y veremos más adelante a detalle.
Estos son los niveles de visibilidad que puedes tener:
Una forma de representar las relaciones que tendrá un elemento con otro es a través de las flechas en UML, y aquí tenemos varios tipos, estos son los más comunes:
Asociación
Como su nombre lo dice, notarás que cada vez que esté referenciada este tipo de flecha significará que ese elemento contiene al otro en su definición. La flecha apuntará hacia la dependencia.
Con esto vemos que la ClaseA está asociada y depende de la ClaseB.
Herencia
Siempre que veamos este tipo de flecha se estará expresando la herencia.
La dirección de la flecha irá desde el hijo hasta el padre.
Con esto vemos que la ClaseB hereda de la ClaseA
Agregación
Este se parece a la asociación en que un elemento dependerá del otro, pero en este caso será: Un elemento dependerá de muchos otros. Aquí tomamos como referencia la multiplicidad del elemento. Lo que comúnmente conocerías en Bases de Datos como Relaciones uno a muchos.
Con esto decimos que la ClaseA contiene varios elementos de la ClaseB. Estos últimos son comúnmente representados con listas o colecciones de datos.
Composición
Este es similar al anterior solo que su relación es totalmente compenetrada de tal modo que conceptualmente una de estas clases no podría vivir si no existiera la otra.
Con esto terminamos nuestro primer módulo. Vamos al siguiente para entender cómo podemos hacer un análisis y utilizar estos elementos para construir nuestro diagrama de clases de Uber.
Sabemos que la programación Orientada a Objetos lo que hace es modelar los problemas para ayudarnos a plasmarlos en código. Esto es específicamente lo que debemos hacer: cuando tenemos un problema lo primero es identificar los objetos, y aquí viene la primera fase que es la parte de los análisis.
Tenemos que identificar los objetos. Cuando nosotros tenemos un problema de software es muy natural, que si somos programadores, irnos directamente al código sin interesarnos de donde provienen los datos o su comportamiento. Lo que debemos hacer es observar nuestro problema, identificando los objetos involucrados.
¿Como identifico los objetos?
Los objetos son aquellos que tienen propiedades y comportamientos, también serán sustantivos. Estos pueden ser físicos o conceptuales, por ejemplo, un objeto User es físico mientras que un objeto Session es conceptual. Ambos tienen propiedades y comportamientos.
Esa también es otra manera de identificarlos, analizando si poseen atributos y comportamientos.
Propiedades
Las propiedades, también llamado atributos, siempre serán sustantivos. Son las características que posee el objeto como el nombre, tamaño, forma, estado, etc.
Cuando estés analizando un objeto es un error común poner el resultado en lugar del atributo. Por ejemplo, puedes poner o decir que el atributo es Verde cuando en realidad debe ser color o decir que el atributo es Anahí Salgado cuando en realidad es nombre.
Comportamientos
Son todas las operaciones que el objeto puede hacer, suelen ser verbos o sustantivos y verbo.
Un objeto «User» puede hacer login() o logout, mientras que un objeto «Archivo» puede hacer makeReport().
EJEMPLO
Para nuestro ejemplo tenemos al objeto «Perro» con propiedades y comportamientos. Sin embargo, para entenderlo mejor, debemos verlo en un contexto diferente, y esto es importante a la hora de plasmar un código; ver el contexto de nuestros objetos.
Imaginemos lo siguiente: Tenemos un sistema de adopciones con un catálogo de perros disponibles a ser adoptados. El contexto cambiaria así como también lo haría como algunas propiedades o comportamiento.
Primero necesitaríamos un identificador único para diferenciar cada perro, porque pueden tener el mismo nombre o raza o color. Además de que nuestro comportamiento cambiaria, dentro del contexto de adopciones no nos importaría que el perro pueda ladrar, comer o correr, solo importaría si están disponibles para ser adoptados o no.
<h3>Clase 9 Abstracción y Clases</h3>En la clase anterior definimos un objeto «Perro» del cual conseguimos el identificador, nombre, color, raza y altura. Pero imaginemos que tenemos un objeto Perro al que ponemos de nombre Franky, es de color café, de la raza french poodle y su altura es de 14cm. ¿Qué pasaría si nosotros queremos más perros? Pues aquí es donde entraría el concepto de Clase.
Clase
Es el modelo sobre el cual se construirá nuestro objeto. Es decir, a partir de nuestro objeto Perro definimos la forma más general para poder obtener otros objetos con propiedades diferentes, tal vez crear otro objeto Perro al que ahora llamaremos Mike, de color negro, cuya raza sea distinta y su altura similar.
Con las clases podremos generar más objetos, y eso es justamente lo que deseamos. Generamos un molde que nos permita obtener muchos más objetos. Para hacerlo analizamos nuestros objetos, traemos sus atributos y entonces generamos modelos llamada Clase.
Tomemos como un ejemplo una estrella.
Nosotros obtenemos el molde de esa estrella y así podemos obtener más estrellas de distintos colores. A esto se le llama Abstracción.
Abstracción
Es cuando nosotros separamos los datos de un objeto para entonces generar un molde.
<h3>Clase 10 Modularidad</h3>La modularidad es un concepto que va muy relacionado con las clases y que también es uno de los principios de la Programación Orientada a Objetos, esto por supuesto va muy de la mano con el diseño modular.
Diseño Modular
El diseño modular viene de la arquitectura e incluso del diseño per se que significa subducir un sistema en partes más pequeñas llamadas módulos. Estos módulos pueden funcionar de manera independiente y podrán comunicarse con ellos (con todos o sólo con una parte) a través de unas entradas y salidas bien definidas.
Tenemos como ejemplo este sofá:
Este sofá fue divido y diseñado completamente módulos, cada asiento o lugar es un módulo que se pensó para robustecerla a medida que se van añadiendo más asientos.
Cada módulo (asiento) vive por sí mismo, y puede ser movido y unificado para crear un sistema entero.
Modularidad
Es a capacidad que tiene un sistema de ser estudiado, visto o entendido como la unión de varias partes que interactúan entre sí y que trabajan para alcanzar un objetivo común, realizando cada una de ellas una tarea necesaria para la consecución de dicho objetivo. Cada una de esas partes en que se encuentre dividido el sistema recibe el nombre de módulo.
La modularidad es una opción importante para la escalabilidad y comprensión de programas, además de ahorrar trabajo y tiempo en el desarrollo.
Otro ejemplo de modularidad está en el diseño de este edificio:
Se puede ver como las construcciones actuales son radicalmente más rápido ya que anteriormente se tomaba muchos años poder terminar un edificio, pero ahora gracias al sistema de la modularidad es muy fácil hacer la construcción en muy corto tiempo. Esto se debe a que se genera cada elemento por separado y esto permite que se creen edificaciones en masa.
Programación Estructurada vs Programación Orientada a Objetos
En la Programación Estructurada vimos código en un solo módulo, sabemos que la desventaja está en que los programas son muy grandes, con muchas líneas donde vamos encontrando errores, era difícil de leer y mantener, y si algo tronaba todo el programa caía destrozado.
En cambio nuestro lema, que debemos entender y aplicar en la Programación Orientada a Objetos, será divide y vencerás. La modularidad nos va ayudar a tener los elementos separados de tal forma que puedan vivir independientemente y cumplan el principio de la edificación, es decir, podamos generar sistemas en masa.
Entonces, si algo sucede en uno de los módulos, el error solo afectara a ese módulo y por lo tanto toda el sistema no colapsará. Puedes decidir si quieres crear un programa en trozos de código y esto por supuesto tiene un grado de complejidad que a muchos le cuesta entender o analizar para llevarlo ahí. Pero, lo primero es quitarnos esa barrera de “No puedo” e intentarlo para empezar a trabajar ese fragmento de código.
La modularidad de nuestro código nos va a permitir:
No olvidemos el programar en pequeños trozos. En vez de imaginarnos un código grande, lo importante es imaginar y empezar a pensar que debemos programar en pequeños trozos.
Esto precisamente es uno de los principios de una clase. La clase será precisamente lo que provoque la modularidad y nos va a permitir analizar nuestros problemas, modularizar para que nuestras clases que vivan por separados, separar el comportamiento del objeto de otro comportamiento. Tener una clase nos va permitir fomentar la modularidad, además de los otros beneficios.
Uno de las buenas prácticas es que las clases deberán vivir en archivos separados, esto por supuesto para mantener la modularidad, y evitar que un código este encima de otro o que un código viva en el mismo archivo. La forma en que vamos a generar la modularidad es separar las clases en archivos diferentes, de esta forma mantenemos aislado el código una vez que tengamos nuestro análisis.
<h3>Clase 11 Analizando Uber en Objetos</h3>Ahora que aprendimos de modularidad sabemos que para resolver un problema debemos dividirlo en pequeños subproblemas y eso lo que haremos ahora mismo. Analizaremos nuestro proyecto Uber en pequeños subproblemas.
Paso 1
Solicitar un Uber nace de nuestra necesidad de trasladarnos del punto A al punto B, y para eso necesitamos un celular.
Paso 2
Solicitamos el auto y asignamos a la aplicación de donde a donde queremos desplazarnos.
Paso 3
Nos aparece un catálogo de autos a elegir: X, Pool, Black y Van. Además, también se encuentra el Conductor puesto que, sin importar el tipo de Uber a elegir, siempre estará involucrado.
Paso 4
Una vez elegido el auto, nosotros tenemos que únicamente esperar mientras el auto se dirige hacia nuestra ubicación y finalmente nos llevara del punto A al punto B. Ya, una vez nos esté llevando, será visible un saldo a cobrar por el viaje realizado.
Y ese es nuestro breve análisis de como funcionara nuestra aplicación Uber. Lo siguiente que aprendimos es que debemos analizar nuestros objetos y extraerlos.
Objetos
En el paso 1 tenemos a nuestro objeto User (Usuario) que estará solicitando el auto. Así como el objeto Route (Ruta) para trasladarnos.
En el paso 3 tenemos un catálogo con diferentes tipos de autos y eso significa diferentes tipos de objetos: UberX, UberPool, UberBlack y UberVan. Además, también tenemos nuestro objeto Driver (Conductor).
Finalmente en el paso 4 podemos ver que cuando nuestro viaje fue realizado se nos cobrara un monto y eso significa que tendremos distintas formas de pagar ese viaje. Ya sea a través de un objeto Card (Tarjeta), PayPal o Cash.
También tenemos un último objeto que es del tipo conceptual y está presente durante todo nuestro análisis, ese es el objeto Trip (Viaje) que captura quien ejecuta el viaje, a donde quieres ir, que auto elegiste y que forma de pago realizaras.
<h3>Clase 12 Reto 1: identificando objetos</h3>Ya estás listo para resolver tu primer reto y poner en práctica todo lo que aprendiste para identificar objetos en un problema.
Toma como referencia nuestro Sistema de Adopciones e identifica todos los objetos.
Compártenos tu análisis en la sección de discusiones.
En esta clase veremos cómo podemos definir las clases para plasmarlas en un diagrama UML. Recordemos que nuestro proceso es: identificar el problema, identificar los objetos, definir las clases y finalmente plasmarlas en un diagrama.
Clases en UML
En UML, una clase será representada como un rectángulo con tres zonas:
Con esto le daremos a nuestras clases en UML una identidad (nombre de la clase), estados (atributos o propiedades) y operaciones (comportamientos).
Para nuestro ejemplo, imaginemos que tenemos una clase Person cuyo atributo o propiedad es name y su comportamiento sea walk():
Ya, una vez identificado el objeto Person y puesto en un diagrama UML, nuestro siguiente paso es definirlo en código.
SINTAXIS EN CÓDIGO
Nosotros ya habíamos dicho que trabajaremos en cuatro lenguajes: Java, Python, JavaScript y PHP.
Java: Para declarar una clase utilizamos la palabra reservada class, seguido del nombre de la clase y finalizamos con llaves.
Python: Aquí usamos la palabra reservada class, seguido del nombre de la clase y finalizamos con dos puntos.
JavaScript: Como sabemos este lenguaje se maneja de forma distinta y eso se debe a que todo es a través de prototipos. Sin embargo, utiliza la Programación Orientada a Objetos para analizar problemas y posteriormente poder plasmarlos en código de la mejor forma, por lo tanto al tenerlo en prototipos utilizaremos “funciones especiales” para definir las clases. Usaremos la palabra reservada function seguido del nombre de la clase con paréntesis y finalizaremos con llaves.
PHP: Para declarar una clase nueva es totalmente idéntico a como declaramos en Java.
DEFINIR ATRIBUTO Y COMPORTAMIENTO
Java: Para declarar un atributo es necesario poner el tipo de dato seguido del nombre, y para declarar un método ponemos el tipo seguido del nombre con dos paréntesis y finalizamos con llaves.
Python: Este lenguaje no es estricto en su tipado, por lo que para definir variables es simplemente necesario poner el nombre, en nuestro ejemplo ponemos comillas dobles para que Python infiera que es un string, y para declarar un método utilizamos la palabra reservada def. La forma que usa Python para agrupar declaraciones es mediante indentaciones, por lo que en el intérprete interactivo debes teclear un tabulador o espacio(s) para cada línea indentada.
JavaScript: Cuando se empieza a programar en un lenguaje como JavaScript, es decir, permisivo hasta no poder más, dar los primeros pasos puede resultar realmente complicado. Para declarar nuestras propiedades se utiliza la palabra reservada this y los métodos son declarados afuera usando la palabra reservada prototype seguido de la función.
PHP: Siendo también un lenguaje bastante flexible así que para declarar una variable usamos el símbolo $, sin importar el tipo de dato, y para declarar un método es lo mismo que una función.
Acabamos de aprender como plasmar objetos en diagramas de clase en UML, hagamos esto mismo en nuestro proyecto Uber. Analicemos los objetos y quitemos todos sus atributos.
User
Driver
Route: Como sabemos que se compone de un punto A y un punto B, sabemos que las ubicaciones tienen una latitud y una longitud.
UberX
UberPool
UberBlack
UberVan
Si quieres saber más de los requerimientos de autos puedes ingresar en Requisitos de autos.
Ahora analizaremos los últimos objetos que nos quedan:
Card
PayPal
Cash
Con esto ya tenemos analizado todos nuestros objetos, pero hay algo de redundancia en el diagrama. En la siguiente clase veremos cómo podemos solucionarlo con la herencia.
<h3>Clase 15 ¿Qué es la herencia?</h3>En la clase anterior notamos que nuestro ejemplo tenía atributos repetidos y no solo fue uno, sino que fueron varios. Muchas clases entre sí tenían atributos que estaban siendo redundantes entre ellas, pues esto estaba violando una de las leyes del código.
«Don’t repeat yourself» es una filosofía que promueve la reducción de la duplicación en la programación. Siempre nos inculcara que no tengamos líneas de códigos duplicadas y, en este caso, todavía no hemos hecho código estamos a un paso de hacerlo, pero si lo llevamos así como esta estaríamos violando esta filosofía. Por lo tanto, toda pieza de información no debería ser duplicada debido a que la duplicación incrementa la dificultad en los cambios y su evolución.
Si nosotros dejamos esto así como esta, se nos va a dificultar que en el futuro podamos ejecutar cambios e incluso involucrar un objeto o un elemento más en el proyecto, hará que el código sea más difícil de leer y entender, y hace un mantenimiento se nos va a complicar bastante. Por lo tanto no debemos tener líneas duplicadas en la medida posible.
¿Qué debemos hacer?
Haremos uso de uno de los principios de la programación orientada a objetos, la reutilización de código. La herencia es una de las piezas claves a la hora de reutilizar líneas de código a más no poder.
Herencia
La herencia es un pilar importante dentro de la programación orientada a objetos y nos permitirá crear nuevas clases a partir de otras.
Podemos definir la herencia como la capacidad de crear clases que adquieren de manera automática los miembros (atributos y métodos) de otras clases que ya existen, pudiendo al mismo tiempo añadir atributos y métodos propios.
Lo que haremos es que, una vez detectemos elementos duplicados, ejecutaremos una abstracción de tal manera que podamos generar una clase que sea la más general y, entonces, esa clase general nos permitirá crear nuevas clases. Tendremos una jerarquía, una estructura de padre e hijo, y es que un padre puede tener tantos hijos como sea necesario. Es común encontrar que un padre solo tenga un único hijo, pero, como en nuestro ejemplo, un padre puede tener bastantes hijos a través de la abstracción.
EJEMPLO
Para ejercitarnos y poder identificar herencias tenemos el siguiente ejemplo:
En donde tenemos tres clases (Futbolista, Entrenador y Masajista), si analizamos estas clases podemos ver que comparten cuatro atributos y además tienen en común dos métodos.
En programación orientada a objetos, cuando detectamos que hay elementos repetidos, esto nos indica que debemos hacer algo. Algo no está bien y que seguramente, si lo dejamos así, nos traerá problemas a futuro. Una vez detectada una relación de estos elementos, podemos generar una abstracción de eso y entonces crear una clase que tengan todos estos elementos en común.
En este caso creamos SeleccionFutbol que será la superclase y de ella se estarán heredando: Futbolista, Entrenador y Masajista. Cuando ellos heredan, esto significa que automáticamente todos los atributos y métodos que tenemos ahí serán heredados a las subclases y no tenemos que estarlo escribiendo en código, simplemente aplicando la herencia automáticamente van aparecer en cada clase.
Esta es una forma de analizar herencia, hay otra forma y es partiendo de los elementos en común. En general podemos tener elementos que no tengan ningún atributo en común, pero la lógica del negocio nos va a decir que esto debe considerarse como una clase más general, deben agruparse en una clase más general aunque y esa se puede llamar una clase padre.
<h3>Clase 16 Aplicando Herencia a nuestro proyecto Uber</h3>Aplicaremos lo aprendido en la clase anterior y eso es detectar todos los atributos que son redundantes en nuestro proyecto Uber.
Tenemos plasmado nuestro proyecto en forma de diagrama de clase:
Y ahora vamos a detectar la redundancia entre estos elementos para aplicar la herencia.
USER – DRIVE
Comencemos por las clases User y Drive que tienen todos sus atributos en común. Si sacamos esos elementos comunes podemos crear la clase Account, que poseerá la jerarquía principal y se convertirá en la Super Clase o Clase Padre, mientras que User y Driver se heredaran de Account convirtiéndose en Sub Clases o Clases Hijas.
Para ejemplificar la herencia usamos flechas vacías que apuntan hacia la Clase Padre.
CARD – PAYPAL – CASH
En el caso de las clases Card, PayPal y Cash no tenemos ningún elemento en común, sin embargo, todos son del mismo tipo así aplicaremos la otra forma de herencia: según la lógica de negocios.
UBERX – UBERPOOL – UBERBLACK – UBERVAN
En el caso de los automóviles, las cuatros clases Uber tienen cuatro atributos en común que formaran parte de la Super Clase, sin embargo, todavía existen elementos comunes entre UberX y UberPool, y UberBlack y UberVan así que podemos hacer uso nuevamente de la herencia para otra Sub Clase.
Modelo Simplificado
Así es como estaría quedando nuestro sistema Uber.
<h3>Clase 17 Reto 2: analicemos un problema</h3>Imagina que nuestro sistema de adopciones creció y ahora ofrece adoptar pericos, loros, gatos y hámsteres.
Genera un nuevo análisis, aplica herencia para abstraer mejor el problema y lograr modularidad en el software.
Comparte tus resultados en la sección de discusiones.
Ahora que está listo nuestro modelo podemos comenzar con la etapa de programación de nuestro proyecto. Como vimos, todo el módulo anterior se basó en el análisis de los objetos; analizar y obtener los objetos, abstraerlos para convertirlos en clases y finalmente ejecutar un análisis de herencia para tener una mejor versión de nuestros objetos.
Visual Studio Code
Primeramente estaremos creando cuatro carpetas llamadas Java, Python, JavaScript y PHP en donde iremos depositando los documentos para sus correspondientes lenguajes.
Siguiendo nuestro diagrama de herencia que hicimos del proyecto estaremos creando cuatro archivos para las clases principales: Account, Car, Payment y Router. Además de un archivo Main que será donde generaremos el punto de entrada al programa, todo lo que queremos que funcione y sea visto debemos declararlo en este documento.
JAVA
Main: Lo que debes saber en primer lugar es que el método main() es el punto de entrada de la aplicación, es decir, es el punto en el que comienza la ejecución de esta. Es por ello que ha de ser public (accesible desde fuera de la clase) y static (se puede ejecutar sin una instancia de la clase).
Account: Es la super clase de la que se derivaran las clases Drive y User.
Car: Es la super clase de la que se derivaran las clases UberX, UberPool, UberBlack y UberVan.
Payment: Es la super clase de la que se derivaran las clases Card, PayPal y Cash.
Router: Es la clase que contendrá las ubicaciones de nuestros puntos A y B.
PYTHON
Main: Este main, básicamente, comprueba si un módulo *.py se está importando a nuestro código y establece, de ser así (y de ahí el condicional if) que sea el módulo actual el principal, el que asume el papel de main(), aquél que deberá ejecutarse primero, mientras que el módulo importado se ejecutará a continuación.
Account: Como podemos ver, somos capaces de declarar el tipo de dato que queremos que sean de esta forma.
Card
Payment
Router
Si estás interesado en aprender JavaScript desde ahora debes saber que el concepto de clases no existía como tal hasta el nuevo estándar ECMAScript 6. El reto de encontrar sistemas construidos con este estándar es alto por esa razón te explicaré cuál fue por mucho tiempo su equivalente.
Los Prototipos fue la forma de crear clases en JavaScript y las representaremos partiendo de la declaración de una función.
Creemos nuestras clases:
Para esto crearemos el siguiente sistema de archivos dentro de la carpeta JS de nuestro proyecto:
El archivo index.js será el lugar equivalente al punto de entrada de la aplicación donde estaremos declarando nuestros objetos basado en las clases. Para esta clase lo dejaremos en blanco.
Ahora veamos el código archivo por archivo:
Account.js
Car.js
Payment.js
Route.js
Aquí podemos ver el código del proyecto.
En este código notarás el uso de la palabra reservada this. Normalmente cuando usamos la sintaxis punto siempre lo haremos a partir de un objeto instanciado, en este caso con this, se hace una simulación al objeto en cuestión, a pesar de que en ese momento visualmente sigue siendo una clase.
Digamos que se adelanta un poco al momento de ejecución y visualiza al objeto con sus atributos, más adelante verás la forma en que podemos asignar datos a un atributo del objeto en otros lenguajes y verás que es exactamente la misma sintaxis.
Si intentáramos poner this en el momento de ejecución nos traería un listado de todos los componentes de la clase que en este caso son solo estos tres: id, init y end.
This hace referencia al objeto instanciado. Para comprender del todo esta última frase mira la siguiente clase donde hablamos de objetos.
Reto
Los objetos nos ayudan a crear instancia de una clase, el objeto es el resultado de lo que modelamos, de los parámetros declarados y usaremos los objetos para que nuestras clases cobren vida.
Los métodos constructores dan un estado inicial al objeto y podemos añadirle algunos datos al objeto mediante estos métodos. Los atributos o elementos que pasemos a través del constructor serán los datos mínimos que necesita el objeto para que pueda vivir.
Lo anteriormente hecho solo fueron clases, no pudimos nada en pantalla más que unos mensajes que decían “hola mundo”, pero sin poder hacer verdaderamente nada. Para poder utilizar los elementos declarados dentro de esas clases empezaremos a trabajar con los objetos.
Recordemos que los objetos nos ayudaran a crear instancias de una clase, es decir, es el resultado de lo que moldeamos, de los parámetros declarados y usaremos los objetos para que nuestras clases cobren vida.
Declarar objetos
Java: Al momento de crear objetos en Java, debemos tener claras dos cosas indispensables, la primera es el nombre de la clase para la cual vamos a crear el objeto y segundo el constructor que dicha clase posee, es decir, si el constructor recibe o no parámetros. Para crear objetos en Java, el lenguaje nos proporciona el comando new, con este comando le decimos a Java que vamos a crear un nuevo objeto de una clase en específico y le enviamos los parámetros (en caso de ser necesario) según el constructor.
Python: Haciendo valer su fama como un lenguaje flexible, para declarar un objeto es bastante sencillo ya que solo necesita el nombre del objeto y la clase a la que hara instancia.
JavaScript: Al igual que con muchas cosas en JavaScript, la creación de un objeto a menudo comienza con la definición e iniciación de una variable. También hacemos uso del comando new para crear un nuevo objeto de una clase específica.
PHP: Para crear una instancia de una clase, se debe emplear la palabra reservada new. Un objeto se creará siempre a menos que el objeto tenga un constructor que arroje una excepción en caso de error. Las clases deberían ser definidas antes de la instanciación (y en algunos casos esto es un requerimiento).
Método constructor
Un constructor es un método especial de una clase que se llama automáticamente siempre que se declara un objeto de esa clase. Su función es inicializar el objeto y sirve para asegurarnos que los objetos siempre contengan valores válidos.
Para nosotros, las paréntesis representaran los métodos.
Java: En el lenguaje Java, si para una clase no se define ningún método constructor se crea uno automáticamente por defecto. El constructor por defecto es un constructor sin parámetros que no hace nada. Los atributos del objeto son iniciados con los valores predeterminados por el sistema.
Python: En Python, el método constructor siempre se llama init (dos subrayados antes y después de init).
JavaScript: En JavaScript, la función sirve como el constructor del objeto, por lo tanto, no hay necesidad de definir explícitamente un método constructor. Cada acción declarada en la clase es ejecutada en el momento de la creación de la instancia.
PHP: Debemos definir un método llamado __construct (es decir utilizamos dos caracteres de subrayado y la palabra construct). El constructor debe ser un método público (public function).
NOTA: No te preocupes por entender this, no te compliques, lo estaremos viendo más adelante.
Pasar datos por parámetros
Dependiendo del tipo de dato que enviemos a la función, podemos diferenciar dos comportamientos: Paso por valor (se crea una copia local de la variable dentro de la función) y Paso por referencia (se maneja directamente la variable, los cambios realizados dentro de la función le afectarán también fuera).
Java
Python
JavaScript
PHP
Como es visible en todos los casos, para enviar un valor es únicamente necesario ponerlos dentro de los paréntesis. Como en este caso estamos enviando un cadena de caracteres o string, ponemos comillas dobles, algo que no es necesario cuando enviamos como valor un número.
<h3>Clase 21 Objetos. Dando vida a nuestras clases en Java y Python</h3>Ya aprendimos la forma de crear objetos y sabemos la sintaxis básica en los cuatros lenguajes que vamos estudiando, ya es hora de ver como declarar esos objetos en Java y Python.
Recordemos que anteriormente dejamos nuestras clases listas. Por el momento, y por el bien de la práctica, usaremos la clase Car ya que es la que más sentido se nos hace.
JAVA
Primeramente, para crear objetos en el lenguaje Java, debemos ir a nuestra clase Main que recordemos tiene actualmente esto:
Debemos recordar que para crear un objeto se sigue esta sintaxis: el tipo de la clase, nombre del objeto, igualamos (=), usamos la palabra reservada new y terminamos con el método constructor que trae por defecto las clases de Java.
En nuestro caso quedaría así:
Car: Sería la clase que estaríamos usando.
car, car2: Son el nombre de los objetos.
new: Palabra reservada para la creación de objetos.
Car(): Es como llamamos al método constructor.
Operador punto (.): Nos permite acceder a los distintos atributos de la clase. Cuando tenemos un objeto de un tipo determinado y queremos acceder a uno de sus atributos solo tenemos que poner el identificar asociado al objeto seguido por un punto y por el identificador que hace referencia a un miembro concreto de la clase a la que pertenece el objeto.
System.out.println(): En Java hay algunos objetos que existen por defecto (en cualquier entorno de desarrollo). Uno de ellos es el objeto denominado System.out. Este objeto dispone de un método llamado println que nos permite imprimir algo por pantalla en una ventana de consola.
Para no tener que estar declarando System.out.println() cada vez que deseamos imprimir el valor de nuestro objeto, podemos crear un método en la clase Car:
Finalmente, en nuestra clase Main cambiamos los System.out.println() por car.printDataCar() y car2.printDataCar().
De esta forma, con los objetos, estamos accediendo a los atributos y a los objetos. Reutilizamos código para imprimir datos de los objetos.
PYTHON
Para crear objetos dentro de Python es necesario importar la clase de la que estaremos usando los elementos eso se hace escribiendo al principio de toda la clase Main:
Como en Python no es necesario escribir el tipo de clase y tampoco la palabra reservada new, simplemente creamos los objetos directo:
En general permanece bastante similar al Java, excepto por algunos cambios obvios:
El cambio está en la forma de imprimir un objeto, no necesitamos crear un método especial para poder hacerlo, sino que simplemente usamos vars dentro de un print y le pasamos el objeto como parámetro:
En la clase anterior vimos como declarar el método constructor en el caso de Java y Python, hoy vamos a ponerlo en acción para el caso particular de JavaScript y Java.
JAVA
Primeramente lo veremos en el caso de Java para entenderlo mejor, porque en JavaScript es bastante peculiar.
Lo que haremos es ir a la clase Car en donde crearemos nuestro método constructor:
public: Indica que es un método accesible a través de una instancia del objeto.
Car(): Es el nombre que tendrá nuestro método.
Dentro del constructor ponemos this., y esto no es más que una buena práctica porque se acostumbra mucho que los parámetros tengan el mismo nombre que las propiedades.
Ahora en nuestro método printDataCar si lo dejamos tal cual no imprimirá lo que estamos queriendo, por eso debemos cambiarlo y poner driver.name.
Hasta ahora también hemos estado manejando driver como un string, pero sabemos que en realidad es de tipo Account, así que debemos ir a la clase Account para crear su método constructor:
Ya hecho una vez todo esto y ejecutamos nuestro programa nos indicara un error, porque no hemos realizado los demás cambios. Cuando sobrescribimos el método automáticamente el método vacío que teníamos se pierde, así que para pasar los datos debemos hacerlo dentro de las paréntesis:
Con esto tenemos los datos mínimos necesarios para que un vehículo exista dentro de nuestra aplicación Uber.
JAVASCRIPT
Primero debemos crear un archivo HTML llamado index que funcionara como nuestro Main, esto es porque JavaScript necesita un navegador que nos permita visualizar todo.
En nuestro archivo index.html tendremos lo siguiente:
Es un esqueleto básico del HTML, lo importante está dentro del body donde tenemos tres scripts que nos permitirá incluir o llamar nuestro código de tipo JavaScript. Es importante ponerlos en ese orden porque de lo contrario no nos funcionara, esto es porque JavaScript empieza a compilar y renderizar desde el principio a medida que va leyendo línea por línea.
Ahora vamos a nuestra clase Car:
Ahora vamos a nuestra clase Account para pasar el nombre y el documento:
¿Recuerdas a index.js? Hasta el momento ha permanecido vacío, pero lo usaremos para poner ahí todas las llamadas de nuestra clases y posteriormente mostrarlas. Entonces, vamos a index.js:
Finalmente, para ver nuestros resultados, debemos abrir nuestro archivo HTML en un navegador. En el navegador damos click derecho, inspeccionar y vamos a consola, y ahí esta imprimido en pantalla nuestra clase de hoy.
<h3>Clase 23 JavaScript orientado a objetos, lo más nuevo</h3>A partir de las nuevas especificaciones del ECMAScript 6 ya podemos declarar una clase con la palabra reservada class, aunque es importante aclarar que estos no dejan de ser prototipos, sino todo lo contrario.
Además tendremos una palabra clave para definir un constructor, y dentro de este estarán las propiedades de nuestra clase definidas listas para inicializarse.
Transcribamos el código JavaScript que generamos en la clase anterior a este nuevo estándar.
La clase Car quedaría así:
Si quisiéramos declarar un método, en esta nueva sintaxis dejaremos de utilizar la palabra clave function.
Ahora veamos a la clase Account:
Y para finalizar aquí puedes ver las clases Route y Payment:
Notarás que para instanciar un objeto seguiremos usando la palabra clave new.
Y los resultados serán los mismos:
<h3>Clase 24 Declarando un Método Constructor en Python</h3>En Python encontrarás un concepto denominado Métodos Mágicos, estos métodos son llamados automáticamente y estrictamente bajo ciertas reglas. El método constructor en Python forma parte de esta familia de métodos y como aprendimos en la clase anterior lo declaramos usando init, aunque si nos ponemos estrictos este método no construye el objeto en sí. El encargado de hacer esto es new y el método init se encargará de personalizar la instanciación de la clase, esto significa que lo que esté dentro de init será lo primero que se ejecute cuando se cree un objeto de esta clase.
Para nuestro proyecto tenemos la necesidad de que algunas variables se inicialicen obligatoriamente cuando ocurra la instanciación. Así que declaremos el método init en las clases de nuestro proyecto con las propiedades obligatorias.
Para la clase Account quedaría algo así, notarás que usamos la palabra clave self, esta es muy parecida a lo que venimos trabajando a otros lenguajes con this. Y como su nombre lo dice hace referencia a los datos que componen la clase, en este caso self.name está llamando al atributo name que se encuentra en la línea 3 de la clase y, le está asignando el dato que se pasa en el método init de la línea 8.
Ahora veamos la clase Car:
Lo que notarás de diferente es que cambiamos el tipo de dato de driver, ahora es de tipo Account y como ves está solicitando los dos datos obligatorios para instanciar un objeto de este tipo. Esto lo verás más en acción en el próximo fragmento de código del archivo main.py. Además, mucho ojo, en la primera línea observamos que es importante importar la clase para poderla usar.
Nuestro archivo main.py ahora se verá así:
Observa que estamos importando las dos clases que usaremos y las estamos instanciando en los métodos constructores.
Los resultados serán los siguientes:
El código de este ejemplo lo encuentras en este enlace.
Reto 3
Ya sabemos cómo funciona la herencia de manera conceptual, pero aún lo hemos visto expresada en código dependiendo del lenguaje de programación que elijas. Y es que esto varía dependiendo de lo que estés eligiendo:
Java: Para crear una subclase se usa la palabra reservada extends, esto le indica a la clase hija cual va a ser su clase padre.
Python: Usamos la palabra class seguido del nombre de la clase hija, se la pone entre paréntesis pasamos la clase padre como parámetro.
JavaScript: Ha sido nuestro amigo rebelde durante todo el curso, por lo que no es de extrañar que JavaScript herede de una manera peculiar y es que simplemente toma a la clase hija seguido de la palabra prototype e inmediatamente instancia la clase padre.
PHP: Esté lenguaje maneja la herencia de manera similar a Java, usando la palabra extends.
JAVA
Siguiendo con la clase Car es tiempo de crear los objetos que descienden de esté, para eso creamos cuatro archivos que serán nuestras clases UberX, UberPool, UberBlack y UberVan.
UberX y UberPool
UberBack y UberVan
PHP
Al igual que en Java, también creamos cuatro archivos nuevos que serán nuestras clases.
Tenemos nuestra clase Car:
Y ahora crearemos una clase UberX:
Reto 4
En la clase anterior nos quedamos con el reto de terminar la composición de las demás clases que heredan de Car, ahora vamos a ver lo que debimos haber hecho.
UberPool:
Y, las clases UberBlack y UberVan:
Ahora vamos a probar nuestro código:
Usando require_once() traemos las clases Car, UberX, UberPool y Account.
El símbolo $ seguido de un nombre representan a nuestras variables.
Entre las paréntesis van las variables que aceptan cada clase
La sintaxis para llamar a un método en PHP es: $variable -> método().
Nota: Todavía no hemos ejemplificado en código la herencia de la clase Driver, pero con lo aprendido a estas alturas serías capaz de hacerlo por ti mismo.
Finalmente para ejecutar nuestro código damos click derecho en algún lugar del editor y vamos en PHP Server: Serve project para arrancar un servidor.
Una vez hecho eso nos aparecer un navegador con los datos imprimidos.
Como vez, todavía no imprimimos los datos de la marca y modelo, pero eso lo estaríamos viendo más adelante.
<h3>Clase 27 Aplicando herencia en lenguaje Python y JavaScript</h3>Recuerdas que en Python la herencia se expresa de manera muy similar a un método constructor de otros lenguajes. Apliquemos herencia para nuestra familia Car, para esto crearemos las siguientes clases:
El código completo puedes verlo aquí
JavaScript
En clases anteriores te expliqué cómo ejecutar herencia en estándares anteriores al ECMAScript 6. Uno de los beneficios de utilizar este nuevo estándar que ejecutar herencia es tan simple como utilizar la palabra reservada extends.
Ahora para utilizar una de las clases y crear un objeto, por ejemplo de UberX, no olvides declarar la clase en el archivo index.html.
Nuestro ejemplo se verá así:
El código completo puedes verlo aquí
<h3>Clase 28 Otros tipos de Herencia</h3>A partir de ahora las clases que estén siendo heredades las llamaremos familias.
Acabamos de aplicar herencia a la familia Car. Ahora apliquémosla a la familia Payment.
En clases anteriores te mencioné que otro punto de partida que puedes tomar para aplicar herencia es del hecho de que hay clases que lógicamente deberían estar en una familia, como es el caso de Payment.
Repasemos el diagrama de Payment
Notarás que a nivel de código parece inservible pero cuando estemos en el caso de uso Pagar un Viaje, probablemente en ese momento no sabremos cuál es el método de pago, y necesitemos ingresar un dato lo suficientemente genérico que conceptualmente nos dé la información que necesitamos, en este caso que es un Payment. Este es un tipo de Polimorfismo y uno de los principios SOLID del software que obedece a la Inyección de Dependencias. Lo veremos más adelante a detalle.
Ahora nos faltará crear las clases y aplicar su herencia.
<h3>Clase 29 Reto 4</h3>Nos queda la Jerarquía Account pendiente.
Tomando como referencia nuestros diagramas. Plásmala en tu lenguaje de programación favorito.
Compártenos tus resultados.
Ya casi estamos terminando nuestro proyecto y estamos dando los últimos detalles que claro no son menos importantes porque aquí estaremos viendo las restricciones que tendrá cada clase.
Recordemos nuestra clase Car ahora añadiremos para imprimir la cantidad de pasajeros:
Mientras que la archivo Main definimos una variable de tipo UberX:
RECUERDA: UberX hereda de Car, por lo que tiene todos sus métodos y atributos.
¿Qué pasa si ejecutamos el programa?
Pues no imprimirá la licencia, el nombre del conductor y la cantidad de pasajeros que puede llevar. ¿Y si cambiamos passenger de 4 a 3? Pues nada extraños, nos seguirá imprimiendo todo perfectamente.
Pero si lo meditamos un poco, las reglas de Uber dicen que los vehículo de categoría UberX deben tener al menos cuatro pasajeros sin contar al conductor, es decir, aceptan cinco personas dentro pero cuatro lugares deben quedar disponible para ese vehículo. Entonces, si colocamos que passenger sea igual a tres generara una inconsistencia en nuestros datos. Una inconsistencia que podemos prevenir al hacer que nadie pueda alterar ese parámetro.
Y precisamente de eso trata nuestra clase de hoy: no alterar un parámetro, que nadie más tenga acceso, y la única forma que tenemos en Java (y en la programación orientada a objetos) será escondiendo ese parámetro, dejarlo invisible o al menos invalidado para los demás implementando la encapsulación.
Encapsulamiento
Este concepto consiste en la ocultación del estado o de los datos miembro de un objeto, de forma que sólo es posible modificar los mismos mediante los métodos definidos para dicho objeto. Es decir, limitamos el acceso a las variables de nuestras clases.
Y es justamente eso lo que nosotros deseamos hacer, esconder el atributo passenger para que no pueda ser alterado o que al menos no le pongan datos que ni siquiera tengan que ver con la lógica de nuestro negocio.
¿Como se encapsulan los datos?
Cuando realizamos un abstracción en una clase para luego instanciarla y crear un objeto no se necesita conocer a fondo la implementación solo se necesita poder instanciar esa clase tampoco necesita conocer todas las propiedades de un objeto o acceder a ellas de forma directa, por ello podemos crear diferentes métodos y forzar a utilizar métodos definidos para modificar estas propiedades.
Para realizar el proceso anterior se necesita conocer los modificadores de acceso. Los modificadores de acceso permiten dar un nivel de seguridad mayor a nuestras aplicaciones restringiendo el acceso a diferentes atributos, métodos, constructores asegurándonos que el usuario deba seguir una “ruta” especificada por nosotros para acceder a la información.
Es muy posible que nuestras aplicaciones vayan a ser usadas por otros programadores o usuarios con cierto nivel de experiencia; haciendo uso de los modificadores de acceso podremos asegurarnos de que un valor no será modificado incorrectamente por parte de otro programador o usuario. Generalmente el acceso a los atributos se consigue por medio de los métodos get y set, pues es estrictamente necesario que los atributos de una clase sean privados.
Teniendo en cuenta la siguiente imagen:
Ahora que ya entendimos sobre encapsulamiento y cuáles son los datos a encapsular vamos a hacerlo en nuestras clases.
Estamos en nuestro proyecto y quedamos que passenger sea validado, el hecho de que tuviera 3 lugares disponibles no es algo que va con la regla de nuestro negocio. Para eso vamos a arreglar esto poniendo un modificador de acceso en la clase Car. ¿Por qué la clase Car? Porque ahí precisamente es donde se encuentra nuestro atributo passenger y actualmente se ve así:
El atributo passenger no tiene ningún modificador, es decir, su acceso es default. Podemos escribir public Integer passenger; para que sea de acceso público, pero nosotros queremos esconderlo y que sea accesible únicamente para la clase, por lo que su modificador será private.
Entonces quedara de este modo:
Pero si lo ejecutamos nos saldrá error:
Y eso se a que el atributo passenger ya no se encuentra visible para la clase Main, pero si continua siendo visible para la clase Car. Por eso, si nosotros queremos, podemos imprimir passenger desde el constructor de la siguiente forma:
Los que hicimos fue en la clase Car, dentro del constructor, darle al atributo passenger un valor de 3 (asientos disponibles) y debajo imprimimos en pantalla. Mientras que en la clase Main simplemente eliminamos o comentamos nuestro acceso al atributo passenger. Si esto lo ejecutamos podemos ver cómo nos corre perfectamente, sin embargo, si lo intentamos correr directamente a través de Main nos dará error porque el atributo passenger es privado y solo visible dentro de su clase.
Ahora vamos a darle un poco de forma y sentido. ¿Por qué lo dejamos accesible dentro de la clase? Porque necesitamos validarlo, lo que nosotros deseamos es que cuando alguien quiera ingresar los datos sobre la cantidad de asientos disponibles definitivamente debe ser de cuatro para los de tipo UberX.
Por eso, y para acceder a los datos privados, usaremos métodos especiales.
Los métodos get y set, son simples métodos que usamos en las clases para mostrar (get) o modificar (set) el valor de un atributo. El nombre del método siempre será get o set y a continuación el nombre del atributo, su modificador siempre es public ya que queremos mostrar o modificar desde fuera la clase.
Agregando los métodos get y set a nuestro programa quedaría así:
Creamos los métodos getPassenger y setPassenger.
Con esos dos métodos creados, en la clase Main podemos acceder a setPassenger y por parámetro enviar el valor para passenger. Y si este código lo ejecutamos nos seguirá corriendo perfectamente. Podríamos pensar que es exactamente lo mismo solo que cambiamos la variable por un método, pero no es así. Ahora seremos capaces de validar los valores enviados a passenger para que no permitir que ningún otro valor diferente a cuatro sea agregado o incluso enviar un parámetro vacío.
Dentro del método setPassenger validamos para que el valor del atributo passenger siempre sea cuatro, en caso de que se ingrese otro, entonces nos marcara error. Con esto ya todos los futuros conductores estarán obligados a poner cuatro asientos disponibles para que les funcione la aplicación.
Incluso podríamos validar los datos dentro de printDataCar para que todos atributo sean diferente a null, es decir, que siempre tengan un valor.
Polimorfismo: Muchas formas. Poli = muchas, morfismo = formas. NO es Poliformismo
Es construir métodos con el mismo nombre pero con comportamiento diferente
Estamos llegando casi al punto final de nuestro proyecto y es momento de ver una de las partes más importantes, una pieza fundamental para nuestro proyecto, que es el polimorfismo.
Polimorfismo
Viene de «Poli» que significa mucho y «Morfismo» que significa formas, es decir, muchas formas.
Observación: No es Poliformismo como algunos lo llaman.
El polimorfismo es la capacidad que tienen los objetos de una clase en ofrecer respuesta distinta e independiente en función de los parámetros (diferentes implementaciones) utilizados durante su invocación. Dicho de otro modo el objeto como entidad puede contener valores de diferentes tipos durante la ejecución del programa.
En JAVA el término polimorfismo también suele definirse como ‘Sobrecarga de parámetros’, que así de pronto no suena tan divertido pero como veremos más adelante induce a cierta confusión. En realidad suele confundirse con el tipo de poliformismo más común, pero no es del todo exacto usar esta denominación.
Por lo general diremos que existen 3 tipos de polimorfismo:
Un ejemplo clásico que podemos ver es esta:
En la que tenemos una jerarquía de clases donde nuestra clase padre se llama Forma y sus clases hijas son Circulo y Cuadrado.
Las clases pueden compartir atributos y métodos, como ya hemos aprendido en clases anteriores, pero en este caso particular el Circulo va a dibujar de una forma distinta al Cuadrado. Esto es un comportamiento diferente que tiene el Circulo con respecto al Cuadrado, pero ese método dibujar proviene de la clase padre por lo que puede que se le haya dado un comportamiento por defecto o incluso puede que solo se haya dejado en blanco para que cada quien implemente el comportamiento que así lo desea, esto específicamente es lo que llamamos polimorfismo es donde tenemos un método que se comparte entre clases y cada una de esas clases le da el comportamiento que necesita o que desea.
Ahora vamos a nuestro proyecto y analicemos un momento. Ya hemos encapsulado y validado la variable passenger para que acepte únicamente el valor de 4, y entendemos que el método setPassenger puede variar dependiendo de cada clase. En este caso las clases UberX, UberPool y UberBlack aceptaran cuatro como la cantidad de asientos disponibles, pero con UberVan nos surge la necesidad de validar el dato a seis lugares disponibles, esa es la condición que nos pone Uber para tener un auto de tipo UberVan en la plataforma. Entonces es ahí, en UberVan, donde tenemos un comportamiento diferente.
El comportamiento involucra la asignación del dato, la validación del dato para seis en lugar de cuatro, y aquí ya estamos viendo impregnado el polimorfismo.
Vamos en nuestra clase Car:
Encapsulamos todos nuestros demás datos, además de cambiar el modificador de acceso de Passenger y lo volvemos protected para que pueda ser usada por las subclases.
Después, con un click derecho, seleccionamos Source Action o Acción de Origen:
Y luego elegimos «Generate Getters and Setters»:
Esto nos trae automáticamente todos los getters y setters de todos los atributos que encapsulamos.
Entonces, la validación para que los asientos disponibles sea 4 ya queda heredado para las clases Car, UberX, UberPool y UberBlack, pero en el caso de UberVan es diferentes. Creemos un objeto de tipo UberVan y veamos que sucede.
Por el bien del ejemplo hemos cambiado el constructor de UberVan por uno más sencillo para poder manipular mejos los datos, pero igualmente podemos ver cuando enviamos 6 como la cantidad que debe tener passenger no nos imprime ningún resultando, incluso cuando imprime nuestro objeto UberX, y nos dice que debemos poner 4 pasajero lo cual no es el dato que nosotros queremos que tenga.
Para arreglar esto vamos a la clase UberVan:
¿Para qué sirve @Override?
Pues no sirve para nada. El uso de la anotación @Override es opcional.
Para lo único que realmente sirve es a modo de documentación:
En este caso nosotros estamos trayendo el método setPassenger y usamos el polimorfismo de sobrecarga para cambiar la validación, es decir, sobre escribimos sobre el método para que tenga un nuevo comportamiento. En este caso, nuestra validación pasa de aceptar 4 a aceptar 6.
También podemos usarlo en UberX:
En el caso de UberX nosotros traemos al método printDataCar y, aparte de decirle con super.printDataCar() que nos imprima lo normal (licencia, nombre y documento), también hacemos que nos imprima la marca y el modelo del vehículo.
Ahora, sí ejecutamos el código, podemos ver cómo nos imprime perfectamente tanto los datos del UberX como del UberVan:
Este es el diagrama que finalmente obtuvimos, aquí solo faltaría añadirle los atributos que posee cada clase.
Recopilemos todo lo que hemos aprendido para explicar los últimos detalles.
En primer lugar notarás que tenemos 3 tipos de flechas:
Asociación
Como su nombre lo dice, notarás que cada vez que esté referenciada este tipo de flecha significará que ese elemento contiene al otro en su definición. Si recuerdas la clase Car, este contenía una instancia de Driver. La flecha apuntará hacia la dependencia.
Herencia
Siempre que veamos este tipo de flecha se estará expresando la herencia.
En nuestro diagrama tuvimos al menos tres familias conviviendo. La dirección de la flecha irá desde el hijo hasta el padre.
Familia Car
Familia Account
Familia Payment
Composición
Pasemos a una de nuestras piezas claves, pues notarás en el centro del diagrama la clase Trip que está vinculada a User, Car, Route y Payment. La composición va a significar una asociación entre estas clases con la diferencia de que para que esta clase pueda vivir forzosamente necesita a las demás. Es decir que estas clases son obligatorias para que la clase Trip pueda existir, esta dependencia obligatoria podríamos expresarla en el método constructor de la clase Trip, pues para que un objeto pueda ser creado dependerá de que los demás existan.
Esta clase Trip poseerá la lógica más fuerte del negocio aquí será donde se concentrarán la mayor cantidad de clases.
Esto es todo nuestro diagrama de clases, que quedó totalmente expresado en nuestro proyecto.
<h3>Clase 34 Conclusiones</h3>Has llegado al final del curso de Programación Orientado a Objetos y fue un placer para todos ir en esta travesía de aprendizaje donde aprendimos primeramente a analizar un problema y no cualquier problema, sino que fue un problema de la vida real. Con nuestro proyecto Uber pudimos aprender el análisis que luego convertimos en gráficas y finalmente en un diagrama UML que nos permitió llevarlo directamente a la fase de programación. Y no solo fue un único lenguaje de programación, estuvimos aprendiendo cuatro lenguajes: Java, Python, PHP y JavaScript.
No olvides los pilares de la Programación Orientado a Objetos :
Y por supuesto no olvides las clases, los objetos y todo lo que hemos aprendido aquí.
<h3>Clase 35 Bonus: Qué es la Programación Orientada Objetos</h3>Imaginemos que tenemos un videojuego de fútbol en el cual debemos representar a los jugadores de cada equipo, con la Programación Orientada a Objetos podemos hacer una abstracción de todo esto.
La abstracción es cuando nosotros separamos los datos de un objeto para entonces generar un molde y ese molde se llama clase. La clase se compone de dos cosas: atributos y métodos. Los atributos son todas las características que corresponden al jugador, mientras que los métodos son todas las acciones que podrá hacer el jugador. Por ejemplo, los atributos de nuestro jugador pueden ser el nombre y apellido, y uno de los métodos podría ser la de correr.
A partir de clases podemos crear objetos. El objeto es la base de la Programación Orientada a Objetos y son las instancias de las clases, es decir, las clases son el molde de los objetos. Con las clases podemos crear tantos jugadores como queramos. Cada jugador tendrá características e incluso acciones diferentes, por ejemplo, podemos tener jugadores con diferentes colores de camisetas, shorts distintos o también puede que corran a velocidades diferentes.
Además, con la Programación Orientada a Objetos podemos usar otro concepto importante que es la herencia. La herencia nos ayudara a crear nuevas clases a partir de otras. Es probable que en nuestro videojuego debamos crear la clase arbitro, la clase jugador y la clase portero, estas clases pueden tener características que sean muy similares entre sí.
También tenemos otro concepto importante que es el encapsulamiento que puede significar esconder algo. En nuestro videojuego podría ser que a nuestros jugadores quisiéramos esconderle la velocidad a la cual corren, es decir, hacerlo invisible a los demás jugadores.
Por ultimo tenemos el polimorfismo que significa muchas formas y también es una base importante. Nuestro entrenador podría asignarle un mensaje a cada uno de nuestros jugadores y cada uno ira al campo a ejecutar lo que interpreto de ese mensaje.
<h1>Final:</h1>Si es que este resumen te gusto considera ver este repositorio donde tengo esta información mas ordenada y tambien subo la resolución de los ejercicios.
Esta contribución saca casi toda la información de este repositorio.