Mostrando las entradas con la etiqueta Polimorfismo. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Polimorfismo. Mostrar todas las entradas

20 de marzo de 2020

Implementación de Herencia (Java).

   El Ejemplo Persona muestra la implementación en Java de la clase Persona mostrada en el diagrama de clases de la entrada POO (herencia). Todos los detalles de la clase Persona de dicho ejemplo ya han sido expuestos ahí, por lo que se recomienda encarecidamente al lector los revise y analice, y que los compare con lo descrito para diagrama UML correspondiente.

   Por otro lado, el Ejemplo Cientifico contiene la implementación de la clase Científico mostrada también en el diagrama de clases mencionado anteriormente. Observe que la herencia es implementada en Java a través del uso de la cláusula extends (línea 6), seguida de la clase de la que se hereda (Persona) en la definición de la clase Científico.

   Es importante que el lector note el uso de la cláusula super en los constructores (líneas 11 y 18). El uso en Java de dicha cláusula sólo puede hacerse en el contexto de constructores y sirve para delegarle al constructor correspondiente de la clase padre, los detalles de inicialización del objeto en cuestión que, para el caso de la clase Científico, corresponden a los constructores de dos y tres argumentos de la clase Persona respectivamente. Asegúrese de comprender también ésto antes de continuar.

   Al igual que antes, se deja como ejercicio de análisis para el lector tanto los detalles restantes de implementación de la clase Científico, como el asegurarse de comprender la relación del diagrama UML con el código del Ejemplo Científico.

   En este punto, es importante también que el lector ponga especial atención en los métodos mensaje del Ejemplo Persona (líneas 61-63), y del Ejemplo Científico (líneas 33-35), ya que serán fundamentales para comprender la sobre escritura (override) de métodos que se discute más adelante en la clase de prueba (Ejemplo PruebaHerencia).

   La clase de prueba para la herencia se presenta en el Ejemplo PruebaHerencia y se describe brevemente a continuación. La línea 10 define el objeto persona y genera una instancia de la clase Persona con características específicas; note que el constructor que está siendo utilizado es el definido en las líneas 24-28 del Ejemplo Persona.

   Observe cómo en las líneas 13, 15, 19 y 21 del Ejemplo PruebaHerencia se envían mensajes específicos al objeto persona para:
  1. Obtener su nombre.
  2. Cambiarle el nombre.
  3. Volver a obtener su nombre.
  4. Solicitarle comer.
  5. Solicitarle un mensaje.
   La salida de lo anterior se ve expresada en las primeras cuatro líneas de la siguiente figura:

Salida del Ejemplo PruebaHerencia.
 
    Por otro lado, la línea 26 define y crea el objeto cientifico como una instancia de la clase Cientifico. Al igual que antes, note cómo el constructor utilizado es el definido en las líneas 17-20 del Ejemplo Cientifico.

   A su vez, las líneas 28 y 30 del Ejemplo PruebaHerencia realizan algo análogo a lo que se hizo con el objeto persona, es decir, envían mensajes al objeto cientifico para obtener y cambiar el nombre del científico.

   La línea 32 por su parte, cambia la especialidad del científico a través de un mensaje de tipo set.

   Finalmente, las líneas 34, 36, 38 y 40 envían mensajes específicos al objeto cientifico para:
  1. Obtener su nombre.
  2. Solicitarle comer.
  3. Solicitarle un mensaje.
  4. Solicitarle un mensaje especial.
   Note cómo para el mensaje mensaje las respuestas del objeto persona y el objeto cientifico difieren por completo, debido a que aunque el mensaje es el mismo, el objeto que los recibe responde de manera distinta a dicho mensaje; esto último fue lo que en su momento se definió como polimorfismo. En este ejemplo el polimorfismo está expresado en su forma de sobre escritura (override) de métodos.

11 de mayo de 2018

Características fundamentales de la POO.

   Alan Curtis Kay, quien es considerado por muchos (yo entre ellos) como el padre de la POO, definió un conjunto de características fundamentales para el paradigma orientado a objetos.

   Con base en lo propuesto por Kay, en la Programación Orientada a Objetos:
  1. Todo es un objeto.
  2. El procesamiento es llevado a cabo por objetos:
    1. Los objetos se comunican unos con otros solicitando que se lleven a cabo determinadas acciones.
    2. Los objetos se comunican enviando y recibiendo mensajes.
    3. Un mensaje es la solicitud de una acción, la cual incluye los argumentos que son necesarios para completar la tarea.
  3. Cada objeto tiene su propia memoria, misma que está compuesta de otros objetos.
  4. Cada objeto es una instancia de una clase. Una clase representa un grupo de objetos similares.
  5. La clase es el repositorio del comportamiento asociado con un objeto.
    1. Todos los objetos que son instancias de la misma clase llevan a cabo las mismas acciones.
  6. Las clases están organizadas en una estructura jerárquica de árbol denominada jerarquía de herencia.
    1. La memoria y el comportamiento asociados con las instancias de una clase, están automáticamente disponibles para cualquier clase asociada con la descendencia dentro de la estructura jerárquica de árbol.
   En un intento de complementar la visión de Alan Kay, se presenta a continuación un compendio de conceptos que definen también y refuerzan las características principales de la POO:
  • La abstracción denota las características esenciales de un objeto.
    • El proceso de abstracción permite seleccionar las características relevantes del objeto dentro de un conjunto e identificar comportamientos comunes para definir nuevos tipos de entidades.
    • La abstracción es la consideración aislada de las cualidades esenciales de un objeto en su pura esencia o noción.
  • La modularidad es la propiedad que permite subdividir una aplicación en partes más pequeñas (llamadas módulos), cada una de las cuales debe ser tan independiente como sea posible de la aplicación en sí, y de las partes restantes.
    • La modularidad es el grado en el que los componentes de un sistema pueden ser separados y reutilizados.
  • El encapsulamiento tiene que ver con reunir todos los elementos que pueden considerarse pertenecientes a una misma entidad al mismo nivel de abstracción. Esto permite aumentar la cohesión de los módulos o componentes del sistema. La encapsulación es quizá el concepto más importante del paradigma, ya que permite agrupar las funcionalidades (métodos) y el estado (datos) de los objetos de forma cohesiva. Los métodos proporcionarán los mecanismos adecuados para modificar el estado, y en algunos casos también serán la puerta de acceso a éste.
  • El principio de ocultación de información (information hiding) se refiere a que cada objeto está aislado del exterior; es un módulo independiente y cada tipo de objeto presenta una interfaz a otros objetos, la cual especifica cómo es que pueden interactuar con él.
    • El aislamiento protege a las propiedades de un objeto contra su modificación por quien no tenga derecho a acceder a ellas; solamente los propios métodos internos del objeto pueden acceder a su estado. Lo anterior asegura que otros objetos no puedan cambiar el estado interno de un objeto de manera accidental o intencionada, eliminando así efectos secundarios e interacciones inesperadas.
  • El polimorfismo está relacionado con el aspecto referente al de qué tipo de comportamientos diferentes asociados a objetos distintos, pueden compartir el mismo nombre.
    • El polimorfismo es la capacidad que tienen los objetos de naturaleza heterogénea, de responder de manera diferente a un mismo mensaje en función de las características y responsabilidades del objeto que recibe dicho mensaje.
  • La herencia organiza y facilita el polimorfismo y el encapsulamiento, permitiendo a los objetos ser definidos y creados como tipos especializados de objetos preexistentes.
    • Las clases no están aisladas, sino que se relacionan entre sí formando una jerarquía de clasificación.
    • Los objetos heredan las propiedades y el comportamiento de todas las clases a las que pertenecen. Así, los objetos pueden compartir y extender su comportamiento sin tener que volver a implementarlo.
    • La herencia múltiple se da cuando un objeto hereda de más de una clase.
   Es sumamente importante en lo subsecuente, tener todos estos conceptos vigentes y presentes, estudiarlos, analizarlos y comprenderlos; la memorización no es recomendable, ya que el memorizar conceptos no implica necesariamente su asimilación y mucho menos su comprensión. Por otro lado, si un concepto es comprendido, es posible entonces el poder explicarlo y deducir en consecuencia su definición.

   Mi deseo es que el lector reflexione sobre este aspecto y que, con un poco de paciencia, sume a su repertorio de conocimientos el conjunto de conceptos descritos hasta aquí, los cuales se pondrán en práctica eventual y progresivamente en entradas subsecuentes del blog.


15 de marzo de 2018

Ejercicios selectos (POO).

Parte I.
  1. En el Ejemplo Parvulo3 se hizo referencia a la invocación del mensaje obtenNombre (línea 17) dentro del método mensaje. Cambie el método obtenNombre por el atributo nombre y compruebe lo descrito en el blog.
  2. Considere el Ejemplo PruebaParvulo3 ¿Qué sucede si en lugar de acceder al atributo nombre por medio del método obtenNombre (líneas 9 y 11) se intenta acceder directamente al atributo a través del operador punto (.) como se hace para enviar mensajes a los objetos? Para probar lo anterior cambie la expresión: parvulo.obtenNombre() por la expresión: parvulo.nombre recompile y analice lo que sucede.
  3. Modifique el Ejemplo PruebaParvulo3 para que genere más de un objeto de la clase Parvulo3 del Ejemplo Parvulo3, de tal forma que tenga un conjunto de al menos tres (objetos) párvulos. Para los objetos creados definales una identidad a cada uno de los (objetos) párvulos instanciados por medio de la asignación de un nombre distinto a cada uno de ellos, envíeles mensajes, experimente y juegue con ellos; recuerde que sus objetos son, al fin y al cabo, niñ@s pequeñ@s.
  4. Para el Ejemplo Parvulo4 defina y añada un constructor sin argumentos, de tal forma que la labor del constructor sea definir su propio nombre (el nombre del lector) al atributo nombre. Modifique en consecuencia la clase del Ejemplo PruebaParvulo4 para que haga uso del constructor recién definido, y pueda así comprobar su funcionamiento.
  5. La entrada POO (mensajes y métodos) abordó el tema de la sobrecarga y ejemplificó el concepto utilizando sobrecarga de constructores. La sobrecarga de métodos es análoga a la de constructores. Modifique el Ejemplo Parvulo5 para que implemente la sobrecarga de métodos de la siguiente manera:
    1. Defina un nuevo método con la siguiente firma: public void mensaje(String m).
    2. La responsabilidad del nuevo método mensaje será la de imprimir en la salida estándar la leyenda "Mensaje recibido" seguido de la cadena m.
    3. Realice una clase de prueba (puede basarse en la del Ejemplo PruebaParvulo5) para comprobar el funcionamiento de todos los métodos, especialmente el método sobrecargado mensaje.
  6. Considere el Ejemplo Parvulo5 y modifíquelo para que, en lugar de uno, defina tres atributos de la clase (String): nombre, apellido1, apellido2 y en base a lo anterior:
    1. Agregue el método set correspondiente para cada uno de los atributos en base a lo descrito en el blog.
    2. Agregue el método get correspondiente para cada uno de los atributos en base a lo descrito en el blog.
    3. Agregue un constructor para que sea posible construir un objeto (párvulo) utilizando únicamente el nombre y el primer apellido.
    4. Modifique el método mensaje para que, en caso de que alguno de los atributos del objeto en cuestión sea null, dicho atributo sea ignorado y en consecuencia, no se imprima en la salida estándar.
    5. Construya una clase de prueba que demuestre tanto el funcionamiento de todas las posibles opciones de construcción de objetos, como el adecuado funcionamiento de los métodos de tipo setget, así como el método mensaje.
    6. Construya una nueva clase de prueba que pida los datos (nombre, apellido1 y apellido2) desde la entrada estándar, y que con éstos se modifique los datos de un objeto determinado.
  7. El Ejemplo PruebaHerencia tiene la línea 42 comentada, por lo que el compilador y la JVM la ignoran. Si la descomenta ¿qué piensa que sucederá?, ¿compilará?, ¿se ejecutará?, ¿imprimirá algo en la salida estándar?, ¿fallará la ejecución? Después de analizar el programa responda dichas preguntas y corrobore su respuesta con la experimentación.
  8. Modifique el Ejemplo Persona para que contenga más atributos; es decir, para que las características de una persona estén más completas (por ejemplo el RFC). No olvide agregar métodos de tipo set y get por cada uno de los atributos que añada. En este sentido, agregue también los siguientes cambios:
    1. Cambie el atributo nombre por una distribución más convencional: nombre, primer apellido, y segundo apellido.
    2. Agregue un atributo que represente la dirección o domicilio.
    3. Agregue un atributo que represente el sexo: femenino o masculino, según sea el caso.
    4. Después de lo anterior:
      1. Compruebe el adecuado funcionamiento de la clase Persona respecto de las modificaciones recién hechas. Asegúrese de comprobar que los métodos de tipo set y get trabajan de la manera esperada.
      2. Compruebe que con los cambios realizados a la clase Persona los aspectos relacionados con la herencia de la clase Científico del Ejemplo Cientifico, siguen funcionando sin ningún tipo de problema.
      3. Modifique la clase Científico de manera análoga, y compruebe también que el mecanismo de la herencia permanece inalterado. Para la clase Científico agregue dos atributos (y sus correspondientes métodos de acceso):
        1. Institución o lugar donde labora.
        2. Grado de estudios.
      4. Agregue nuevas acciones, comportamientos o responsabilidades a las clases Persona y Cientifico (como el método toString( ) por ejemplo), y asegúrese de probar su adecuado funcionamiento. No olvide experimentar con los conceptos de sobrecarga y sobre escritura.
    5. Considere el atributo RFC. Este atributo no debería ser modificado con cualquier información, por lo que un método del tipo set no sería recomendable. Para este tributo considere más bien:
      1. Armarlo en base a la información de una persona (investigar cómo):
        1. Primer apellido.
        2. Segundo apellido
        3. Nombre (s).
        4. Fecha de nacimiento
      2. Solicitar la homoclave desde la entrada estándar.
  9. Defina la clase Fecha. Para este ejercicio:
    1. Determine los atributos que debe tener una fecha.
    2. Escriba los métodos del tipo set y get para modificar y acceder respectivamente a los atributos de la clase.
    3. Piense también en el comportamiento mínimo que debería tener dicha clase e impleméntelo mediante los métodos correspondientes, como por ejemplo el método public String obtenerFecha( ) ó public String toString( ).
    4. Escriba también un método (public void diaSiguiente( )), de tal forma que cuando un objeto de la clase Fecha (por ejemplo fecha) reciba el mensaje correspondiente (fecha.diaSiguiente( )), el método deberá modificar los atributos pertinentes para generar la fecha del día siguiente al que actualmente se refiere el objeto fecha. Tome en consideración que hay meses con 31 días (enero, marzo, mayo, julio, agosto, octubre y diciembre) y otros con 30 (abril, junio, septiembre y noviembre). No olvide tomar en cuenta los años bisiestos para el mes de febrero.
    5. Construya también la clase que permita probar el funcionamiento correspondiente de los métodos descritos, así como el comportamiento adicional que haya usted definido para los objetos de la clase Fecha.
Parte II.
    1. Construya un ejemplo completamente diferente al visto en el blog, en donde ponga de relieve los conceptos de envío de mensajes, herencia, polimorfismo, encapsulamiento y ocultación de información. Puede apoyarse de alguna jerarquía de clases que sea de su interés o preferencia.
    2. Muchos de los "supervillanos" humanos de los comics son científicos excepcionales (Dr. Doom, Lex Luthor, Dr. Octupus, Norman Osborn, etc.) ¿Qué comportamiento común podría identificar en este tipo de personajes que le permita extender y/o redefinir el comportamiento de un Científico? Siguiendo la idea plasmada para las clases Persona y Cientifico, genere la nueva clase Supervillano y añada, redefina y pruebe el comportamiento que defina.
    3. Considere el Ejemplo PruebaPolimorfismo. En la línea 28, a una referencia de la clase Persona le es asignado un objeto de la clase Cientifico sin problemas; de hecho el programa funciona, lo cual sugiere que es posible instanciar objetos de subclases (Cientifico) a referencias de súper clases (Persona). ¿Será posible hacer lo mismo al revés?, es decir, ¿será posible que a una referencia de la clase Cientifico se le pueda asignar un objeto de la clase Persona? Haga un programa que implemente esto último y determine sus conclusiones.
    4. El formato internacional de fecha definido por IS0 8601, intenta estandarizar los distintos problemas y variaciones para los formatos de las fechas definiendo un sistema numérico como se muestra a continuación:  AAAA-MM-DD, donde: AAAA es el año [todos los dígitos, p.ej. 2032] MM es el mes [01 (Enero) hasta 12 (Diciembre)] DD es el día [01 hasta 31, de acuerdo al mes]. Así por ejemplo, la fecha "20 de Septiembre de 2059", en este formato internacional se escribe como: 2059-09-20.
      1. Defina una clase con todos los elementos necesarios para que maneje apropiadamente una fecha del formato ISO 8601.
      2. Considere el manejo de errores correspondiente (Fechas no válidas. Deberá tomar en consideración también los años bisiestos).
      3. Derivaciones de fecha: formato europeo (utilizado en México) y formato EE.UU.:
        1. Fecha en formato europeo: 20-09-2059
        2. Fecha en formato EE.UU: 09-20-2059
        3. Polimorfismo en la impresión de la fecha (ISO, europeo y EE.UU.). Ejercite el concepto de enlazado dinámico (dynamic binding).
      4. Para este ejercicio resultaría de utilidad, aprovechando la herencia, lo realizado en el Ejercicio 9 de la Parte I.
    5. En el Ejemplo figuras geométricas (dynamic binding), la línea 14 de la declaración de la interfaz FiguraGeometricaPropiedades aparece como comentario. Borre los símbolos del comentario de tal forma que se añada a la declaración de la interfaz el método obtenPerimetro( ), y en consecuencia, se deba definir (escribir el código) dicho método en las clases que implementan la interfaz. Realice todos los cambios necesarios y pruebe su funcionamiento.

4 de julio de 2017

POO (Consideraciones adicionales).

Respecto al envío de mensajes.
   La sintaxis general en Java para el envío de mensajes a un objeto es la siguiente:

objeto.mensaje(lista_de_argumentos);

donde objeto es un objeto de una clase previamente definida, y mensaje es uno de los métodos públicos definidos para dicha clase. La lista_de_argumentos es una lista de argumentos separada por comas, en donde cada argumento puede ser un objeto, o un tipo de dato primitivo.

Respecto a la sobrecarga de operadores.
   Algunos lenguajes de programación soportan un concepto relacionado con sobrecarga de operadores. La idea general del concepto de sobrecarga se ha planteado en la entrada POO (mensajes y métodos). El lenguaje de programación Java no soporta la sobrecarga de operadores, y por consiguiente, se ha omitido su descripción; sin embargo, es importante que el lector conozca que el concepto de sobrecarga no es exclusivo de los métodos o constructores, ni mucho menos de un lenguaje de programación en particular.
 
    La sobrecarga de operadores en C++ sí existe. En la sección correspondiente a las consideraciones adicionales para las colas de espera, se proporcionan un par de ejemplos al respecto. Para comprenderlos se requiere tener claros los aspectos relacionados con la implementación de la herencia, el funcionamiento de las colas de espera, y su respectiva especialización en la forma de colas de prioridad.

Respecto al paradigma.
   El establecimiento de niveles de acceso como private para los atributos de una clase, así como el uso de métodos de tipo set y get están directamente relacionados con el principio de ocultación de información (information hiding).

   Ahora bien, es probable que el lector haya notado que en la descripción del Ejemplo PruebaHerencia, en distintas ocasiones se hizo referencia a los objetos personacientífico como si fueran en sí mismos personas o entidades existentes. Lo anterior se hizo de manera deliberada, ya que como se comentó en la entrada referente al paradigma orientado a objetos, ésto eleva el nivel de abstracción y permite que se haga referencia a las entidades fundamentales del paradigma (los objetos), como elementos comunes de nuestro lenguaje natural, lo cual permite que los problemas se puedan expresar, al menos en principio, de una manera más natural e intuitiva.

   En este sentido, al dotar a los objetos de una personalidad propia con características y responsabilidades, en lugar de pensar en términos de datos, variables, y funciones o procedimientos que operen sobre dichos datos, se eleva el nivel de abstracción, facilitando con ello el análisis y la comprensión, ya que el problema y su solución pueden ser expresados y analizados en términos de su propio dominio, y no en el del medio (lenguaje de programación) de la solución. Ésta es una de las formas en la que las personas abstraemos, procesamos y utilizamos la información.

   Es sumamente importante que el lector tenga presente que en el paradigma orientado a objetos no se piensa en términos de datos, sino en términos de entidades con características y responsabilidades específicas, por lo que, cuando defina una clase, puede resultar útil el plantearse al menos un par de preguntas que le permitan determinar si los objetos derivados de su clase tienen o no sentido. Adicionalmente, las preguntas pueden ayudar también a establecer o coadyuvar en la meta de mantener una alta cohesión como parte del proceso de diseño e implementación:
  1. ¿Los atributos representan características o propiedades, o definen un estado para los objetos que serán instanciados?
  2. ¿La clase representa en sus métodos servicios, comportamiento, acciones o responsabilidades inherentes a los objetos que deriven de ella?
   Cabe mencionar que las preguntas propuestas son sólo una guía y una sugerencia al lector, no pretenden ser de ninguna manera una lista completa y absoluta. Con estas dos sencillas preguntas, además de validar y verificar su diseño de clases, estará reforzando también el concepto de encapsulamiento.

Respecto al polimorfismo.
   El polimorfismo es la cualidad de objetos heterogéneos de responder de distinta manera a un mismo mensaje. El tipo de polimorfismo más común se da en la herencia, pero no es el único.

   El Ejemplo Heterogéneo es un conjunto de clases (Automóvil, Motocicleta, Perro, Planta, Plomero, Profesor) heterogéneas que contienen un método de servicios. La idea es que, al menos en principio, cada una de dichas entidades ofrecen servicios distintos en función de su constitución y comportamiento general.

   Por otro lado, el Ejemplo Composición contiene tres clases: Libro, Publicación y Revista, las cuales presentan un comportamiento de polimorfismo en la forma en que se auto describen (imprimen), a través del método toString( ).

   En directa relación con el párrafo anterior, el tipo de polimorfismo más común se presenta en el Ejemplo Herencia, el cual contiene también las tres clases Libro, Publicación y Revista pero con un enfoque de polimorfismo basado en la herencia para la forma en que se auto describen (imprimen) a través del método toString( ).

   Existe un concepto en programación denominado latebinding, dynamic bindig o dynamic linkage, el cual se refiere a un mecanismo de programación en el cual el método que responderá al mensaje (el método que será invocado) es determinado en tiempo de ejecución. Como un ejemplo muy simple de esto, tómese el tiempo de comparar y analizar el Ejemplo PruebaHerencia (descrito en la entrada POO (Herencia)) y el Ejemplo PruebaPolimorfismo, en donde se muestra dicho concepto de programación, así mismo, es importante comprender tanto las diferencias de dichos ejemplos como los comentarios que aparecen en el código del Ejemplo PruebaPolimorfismo.

   Considere ahora el siguiente Ejemplo de late binding y tómese el tiempo que considere necesario para comprenderlo con base en el concepto de polimorfismo y  la comprensión de todos y cada uno de los ejemplos anteriores.
 
    Finalmente, una vez que se tengan madurados y comprendidos los ejemplos enunciados y su relación con el polimorfismo, se recomienda revisar también el tema de clases abstractas e interfaces (Java) para complementar dicho concepto.




21 de marzo de 2017

Herencia / Generalización

   Uno de los conceptos del paradigma orientado a objetos más importantes es el de herencia, ya que dicho mecanismo de abstracción permite la re utilización de código de una manera sumamente conveniente, además de habilitar las capacidades del polimorfismo a través de la sobre escritura (override) de métodos.

   Abstracción.
   La ejemplificación del concepto de herencia/generalización estará basada en los Ejemplo Persona y el Ejemplo Cientifico para Java, y en el Ejemplo Herencia para C++; pero antes de poder describirlos, considero pertinente presentar primero en un diagrama de clases los detalles de la relación de generalización que se quiere mostrar con la finalidad de elevar el nivel de abstracción. Con el diagrama de clases propuesto, se pretende llevar el concepto de herencia/generalización a la forma en que la mayoría de las personas comprendemos y analizamos las cosas, para posteriormente profundizar con más conocimiento de causa en los detalles de la implementación de dicho concepto en un lenguaje de programación.

   El diagrama de clases UML (Unified Modeling Language) del que partirá el análisis se muestra en la siguiente figura:
 
Diagrama de clases UML para mostrar la relación de generalización / herencia entre Científico y Persona.

    Los detalles completos de la explicación de un diagrama de clases UML quedan fuera de los alcances de este blog, por lo que sólo se describirán los aspectos más relevantes que ayuden al lector a visualizar de mejor manera, en caso de que el lector no cuente con experiencia en UML, la relación de generalización y herencia.

   Un diagrama de clases UML está compuesto, grosso modo, por clases y las relaciones entre dichas clases. En este sentido, cada clase se representa con un recuadro dividido en tres partes:
  1. Identificador de la clase.
  2. Listado de atributos con la especificación de su clase (tipo) y sus niveles de acceso correspondientes.
  3. Listado de métodos con la especificación de la clase (tipo) de sus argumentos y valor o clase de retorno, así como los niveles de acceso correspondientes para los métodos.
   Tanto para el caso de los atributos como para el de los métodos, los niveles de acceso están representados por un signo de más para un acceso público (+), un signo de menos para un acceso privado (-), y un signo de gato para un nivel de acceso protegido (#).

   Con base en lo anterior, puede observarse de nuestro diagrama que la clase Persona, y por lo tanto las instancias (objetos) que se deriven de ella, tendrán las siguientes características (atributos) comunes a una persona: un nombre, una edad, y una nacionalidad (podrían definirse mucho más características u otras diferentes a las descritas aquí, pero no es la intención del ejemplo representar todas las características completas y comunes a una persona). Observe también que se ha definido un conjunto de operaciones, acciones, responsabilidades o comportamiento comunes a una persona, mismas que se encuentran definidas por los métodos establecidos (al igual que para los atributos, no se ha pretendido modelar por completo el comportamiento o las responsabilidades representadas en los métodos, sino sólo una representación muy general).

   Con base en las aclaraciones previas y lo descrito hasta aquí, es posible decir que una persona promedio está representada de manera muy general por la clase Persona.

   Ahora bien, un científico es (recuerde la idea de división en especializaciones: relación is-a presentada en la entrada Orientación a objetos (conceptos)) una persona, y por lo tanto comparte las características o atributos así como las acciones o el comportamiento inherentes a una persona; y es precisamente este tipo de relación de compartir la que se refiere a la herencia, ya que se dice que en la herencia una clase hereda (comparte) los atributos (características) y métodos (acciones) a otra.

   La herencia en UML se representa por medio de una flecha como la de la figura anterior (diagrama de clases). Es importante señalar que el sentido de la flecha es sumamente significativo, ya que tal y como aparece en nuestra figura, el diagrama indica que la clase Científico hereda las características y el comportamiento de la clase Persona (y no al revés). Otra forma de verlo es que la clase Persona es una generalización de la clase Científico en el sentido de que esta última agrega cierto nivel de especificación respecto de la primera.

   Note también que la clase Científico define un atributo adicional (especialidad), mismo que se añade a todos los atributos que implícitamente ya tiene, mismos que fueron heredados de la clase Persona. Observe también que se han definido cuatro métodos para la clase Científico los cuales tienen las siguientes características:
  1. estableceEspecialidad: es un método de tipo set para el atributo especialidad definido en la clase Científico.
  2. obtenEspecialidad: es un método de tipo get para el atributo especialidad definido en la clase Científico.
  3. mensaje: este método ya estaba definido en la clase Persona, pero al ser redefinido en la clase Científico, se dice que sobre escribe (override) al primero, lo cual significa que un objeto instanciado de la clase Científico, responderá al mensaje mensaje con la definición de su propio método y no con la definición del método mensaje definido en la clase Persona.
  4. mensajeEspecial: este es un nuevo método particular y específico a las instancias derivadas de la clase Científico.
   Es fundamental que el lector se asegure de comprender la descripción hasta aquí realizada respecto a las clases PersonaCientífico. Es también muy importante entender la relación existente entre estas clases antes de continuar a la siguiente sección, en donde entre otras cosas, se abordarán los aspectos relacionados con la implementación de ellas en el lenguaje de programación correspondiente.

14 de febrero de 2017

POO (mensajes y métodos).

   Una de las principales inquietudes que expresan los estudiantes acerca del paradigma orientado a objetos está relacionada con los mensajes y los métodos. En este sentido, se iniciará con un ejemplo sumamente sencillo el cual irá evolucionando progresivamente con la finalidad de ilustrar dichos conceptos.

   Métodos sin argumentos.
   El Ejemplo Parvulo1 muestra la definición de la clase Parvulo1, misma que no contiene atributos pero sí un método cuyo identificador o nombre es mensaje.

   El método mensaje tiene como única responsabilidad la impresión en la salida estándar de una cadena (los detalles generales del funcionamiento de println son presentados en la entrada Un vistazo al lenguaje Java).

   En base a lo anterior, la clase Parvulo1 es una especie de plantilla capaz de generar objetos con una única responsabilidad o servicio, y no puede ser instanciada ni ejecutada por sí misma. Para poder instanciar objetos de la clase Parvulo1 y poder visualizar su funcionamiento, se requiere de una clase de prueba que permita generar una instancia de ella.

   La clase de prueba para el Ejemplo Parvulo1 es la clase PruebaParvulo1 que se presenta en el Ejemplo PruebaParvulo1.

   La clase PruebaParvulo1 tiene la estructura de la mayoría de las clases de prueba que se utilizarán en el blog, y está basada en la definición del método main.

   En la línea 7 se define el objeto parvulo cuya clase (tipo) de la que deriva es Parvulo1; así mismo, observe que se genera una instancia por medio de la cláusula new, misma que, entre otras cosas, construye el objeto. Si la clase Parvulo1 tuviera un constructor explícito, sería precisamente aquí en donde se invocaría. Más adelante en esta misma entrada, se profundizará un poco más al respecto.

   Una vez que el objeto existe, es decir, una vez que el objeto ha sido instanciado, es posible entonces interactuar con él por medio de mensajes para solicitarle acciones que correspondan con las responsabilidades o servicios definidos para dicho objeto que, para el caso del objeto parvulo, es sólo una.

   La solicitud del único servicio que puede proporcionar el objeto parvulo se realiza a través del mensaje mensaje, el cual es enviado (invocado) al objeto en la línea 9:

parvulo.mensaje();

   La expresión anterior se interpreta como: "se envía el mensaje mensaje al objeto parvulo". El envío de mensajes no es otra cosa más que la invocación explícita de un método a través de su identificador, es utilizar el método correspondiente para realizar una acción, misma que está relacionada con el comportamiento o las responsabilidades del objeto en cuestión.

   La salida del Ejemplo PruebaParvulo1 se muestra en la siguiente figura. Asegúrese de comprender lo descrito hasta aquí antes de continuar.

Salida del Ejemplo PruebaParvulo1.

   Métodos con argumentos.
   En esta sección se presenta una versión ligeramente distinta del Ejemplo Parvulo1, en el cual se presentó el envío de mensajes sin argumentos. Tómese el tiempo necesario para comparar el Ejemplo Parvulo2 de esta sección con el Ejemplo Parvulo1 de la sección anterior, y compruebe que son esencialmente iguales.

   El parámetro nombre en el método mensaje constituye la diferencia de los ejemplos anteriormente mencionados. En el Ejemplo Parvulo2 el método mensaje (línea 5) define ahora la capacidad de recibir un argumento de tipo cadena (un objeto de la clase String) referido por el objeto nombre. El método mensaje imprime en la salida estándar un cadena conformada por un texto predefinido (línea 6) y la cadena referida por nombre.

   Por otro lado, el Ejemplo PruebaParvulo2 muestra la clase de prueba para el Ejemplo Parvulo2, la cual es también similar a la del Ejemplo PruebaParvulo1 excepto en la forma en que se envía el mensaje al objeto parvulo (línea 10 del Ejemplo PruebaParvulo2). Observe que el mensaje enviado tiene ahora una cadena como argumento, la cual es referida por el objeto nombre del método mensaje (línea 5 del Ejemplo Parvulo2) en el momento en que se le envía el mensaje mensaje al objeto parvulo.

   Asegúrese de realizar una labor analítica al comparar línea a línea tanto las clases Parvulo1 y Parvulo2, como las clases PruebaParvulo1 y PruebaParvulo2 así como de comprender sus diferencias en base a lo que se ha descrito hasta ahora.

   La salida del Ejemplo PruebaParvulo2 se muestra en la siguiente figura:

Salida del Ejemplo PruebaParvulo2.

   Métodos y atributos.
   Por el principio de ocultación de información es conveniente que únicamente se tenga acceso a los atributos de una clase a través de su interfaz. La interfaz de un objeto está representada por sus métodos públicos (public).

   El Ejemplo Parvulo3 hace uso de dicho principio al definir, con un acceso restringido o privado (private), el atributo nombre (línea 6) para la clase Parvulo3. Observe que dicha clase define también tres métodos públicos, los cuales establecen la interfaz de los objetos que sean instanciados:
  1. estableceNombre: este tipo de métodos son utilizados comúnmente para ajustar o establecer el valor de un atributo; y al menos en principio, debería haber un método de este tipo por cada atributo que contenga la clase y que se requiera manipular desde el exterior. Este tipo de métodos son comúnmente referidos como métodos de tipo set.
  2. obtenNombre: este tipo de métodos son utilizados comúnmente para recuperar u obtener el valor de un atributo, y al igual que antes, debería haber, al menos en principio, un método de este tipo por cada atributo que contenga la clase y que se requiera visualizar desde el exterior. Este tipo de métodos son comúnmente referidos como métodos de tipo get.
  3. mensaje: este ha sido descrito con anterioridad. Note que el método está definido como el del Ejemplo Parvulo1 (sin argumentos), pero funciona como el del Ejemplo Parvulo2. Asegúrese de comprender ésto antes de continuar.
   Observe que el método mensaje (líneas 16-18) se vale del método obtenNombre (línea 17) para acceder al atributo nombre pero no necesariamente tiene que ser así, ya que un método puede acceder directamente a los atributos de la clase, siempre y cuando ambos estén definidos dentro de la misma clase (encapsulamiento). Por otro lado, si el atributo tiene un nivel de acceso protegido (protected), los métodos de las clases derivadas por herencia también podrían acceder a los atributos de la clase de la que derivan (clase padre o super clase).

   Los métodos de tipo set sólo deben trabajar sobre un atributo, por lo que habitualmente sólo reciben un argumento, mismo que se corresponde con la clase (tipo) del atributo a modificar (String para el caso del Ejemplo Parvulo3). De manera análoga, los métodos de tipo get no reciben ningún tipo de argumento, y la clase de objetos que regresan está directamente relacionada con la clase del atributo al que accederán (String para el caso del Ejemplo Parvulo3).

   Es importante hacer notar también que la clase Parvulo3, a diferencia de las anteriores, establece ya una característica representada y definida por el atributo nombre, de tal forma que los objetos derivados de ella (párvulos) compartirán dicha característica (un párvulo es un niño pequeño en edad preescolar), aunque cada uno poseerá su propia identidad (nombre).

   El Ejemplo PruebaParvulo3 muestra la clase de prueba para la clase Parvulo3. Al igual que en los ejemplos anteriores para las clases de prueba, observe que en la línea 7 se define y crea el objeto parvulo.

   Las líneas 9 y 11 hacen uso del método obtenNombre a través del envío del mensaje correspondiente, mientras que la línea 10 envía el mensaje estableceNombre con un argumento específico. Finalmente, la línea 12 muestra el envío del mensaje mensaje, el cual debería resultarle ya familiar al lector.

   La salida del Ejemplo PruebaParvulo3 se muestra en la siguiente figura. Observe que inicialmente el atributo nombre del objeto parvulo no está definido, de ahí que se imprima null en la salida estándar, el cual es el valor por omisión en Java para las referencias a objetos que no han sido instanciados, es decir, cuando los objetos todavía no existen y por consiguiente no han sido inicializados.

Salida del Ejemplo PruebaParvulo3.

   Métodos y constructores.
   Un constructor es un método especial que se invoca implícitamente cuando se crea o instancia un objeto por medio de la cláusula new.

   La cláusula new genera la memoria necesaria para representar al objeto correspondiente y lo inicializa por medio de un constructor. En este sentido, puede haber más de una forma de inicializar un objeto y, en consecuencia, más de un constructor.

   El identificador o nombre de los métodos constructores debe coincidir con el identificador o nombre de la clase que los define; en base a lo anterior se tiene que puede haber más de un constructor compartiendo el mismo identificador. El mecanismo que tienen los constructores para distinguirse entre sí, es a través del número y clase o tipo de los parámetros que definen, constituyendo con ello una forma de polimorfismo comúnmente conocida como sobrecarga (la sobrecarga se trata con un poco más de detalle en la siguiente sección). En la creación del objeto, se invoca el constructor que coincida con el número y tipo de argumentos proporcionados a la cláusula new.

   Las líneas 9-11 del Ejemplo Parvulo4 muestran la definición del constructor Parvulo4, observe cómo el nombre del constructor coincide exactamente con el nombre de la clase. Dicho constructor define un único parámetro n el cual es un objeto de la clase String.

   La inicialización que hace el constructor Parvulo4 consiste únicamente de la asignación del objeto n al atributo representado por el objeto nombre.

   Es importante mencionar que la labor de inicialización de un constructor en particular puede ser un proceso mucho más elaborado que el descrito hasta ahora, y que estará en función directa de las responsabilidades de inicialización con que se quiera dotar al constructor y de la problemática en particular que se esté resolviendo por medio del objeto en cuestión.

   Los elementos restantes de la clase Parvulo4 han sido previamente abordados en las secciones anteriores por lo que no se repetirán aquí.

   El Ejemplo PruebaParvulo4 presenta la clase de prueba para el Ejemplo Parvulo4. Observe que a diferencia de los ejemplos anteriores, en la línea 8 se proporciona un argumento al constructor Parvulo4, lo cual hace que desde la creación del objeto parvulo se le esté definiendo un nombre.

   Observe también cómo la secuencia de mensajes subsecuentes coincide con las del Ejemplo PruebaParvulo3, excepto que en la línea 11 del Ejemplo PruebaParvulo4, se envía el mensaje estableceNombre al objeto parvulo para asignarle el nombre completo (con apellidos) al párvulo.

   Asegúrese de comprender antes de continuar, que la siguiente figura muestra la salida correspondiente a la ejecución del Ejemplo PruebaParvulo4:

Salida del Ejemplo PruebaParvulo4.

   Sobrecarga.
   La sobrecarga (overload) es un tipo de polimorfismo que se caracteriza por la capacidad de poder definir más de un método o constructor con el mismo nombre (identificador), siendo distinguidos entre sí por el número y la clase (tipo) de los argumentos que se definen.

   El Ejemplo Parvulo5 muestra la sobrecarga de constructores. Note que las líneas 8-10 definen el mismo constructor que el del Ejemplo Parvulo4 excepto por el nombre, y que se ha añadido o sobrecargado un nuevo constructor (líneas 12-14), el cual recibe tres argumentos que representan el nombre (n), el primer apellido (a1), y el segundo apellido (a2) de un párvulo.

   La sobrecarga de constructores se da porque ambos constructores tiene el mismo identificador (Parvulo5) pero distinto número de parámetros.

   No puede existir sobrecarga para constructores o métodos con el mismo identificador y el mismo número o clase (tipo) de parámetros; tiene que haber algo que los distinga entre sí, ya que en otro caso habría ambigüedad.

   Los métodos restantes del Ejemplo Parvulo5 ya ha sido comentados con anterioridad en otras secciones de esta misma entrada.

   La clase de prueba para el Ejemplo Parvulo5 se muestra en el Ejemplo PruebaParvulo5, la cual es también muy similar a las clases de prueba anteriormente explicadas. Únicamente cabe resaltar la creación del objeto parvulo en la línea 7. Note que ahora se le proporcionan tres argumentos al constructor, lo cual hace que el constructor utilizado sea el definido en las líneas 12-14 del Ejemplo Parvulo5.

   La salida del Ejemplo PruebaParvulo5 aparece en la siguiente figura. Compare dicha salida con la de la figura anterior y asegúrese de comprender la diferencia.

Salida del Ejemplo PruebaParvulo5.