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

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.




3 de abril de 2017

Ejercicios selectos (pilas).

  1. En el Ejemplo PilaPrimitiva el método imprime tiene las líneas 50 y 52 como comentarios ¿Qué sucede si sustituye la línea 51 por la línea 50 y la línea 53 por la línea 52? ¿Compilará? ¿Si sí por qué, y si no por qué? Si compila ¿Cuál será el resultado de la ejecución? Determine sus respuestas y después corrobore las mismas con la experimentación.
  2.  Con base en el Ejemplo PilaPrimitiva, haga una implementación completa de una pila que almacene char primitivos y pruébela Cuando termine, considere lo siguiente:
    1. ¿Puede visualizar las implicaciones de este ejercicio?
    2. ¿Qué pasa por ejemplo si ahora se necesita almacenar datos de tipo double?
  3. En el Ejemplo PruebaPila se creó una pila utilizando un constructor sin argumentos. Modifique dicho ejemplo para asignar un nuevo nombre a la pila por medio del constructor correspondiente; asígnele a la pila el nombre de "Mi primera pila", recompile y pruebe su funcionamiento.
  4. Modifique el Ejemplo PruebaPila para que permita leer n números enteros desde la entrada estándar y los almacene en la Pila.
  5. En el blog, durante la explicación del Ejemplo PruebaPila, se menciona la cláusula try-catch-finally; sin embargo, en dicho ejemplo sólo se muestra el uso de la cláusula try-catch. Investigue y documéntese acerca del uso y funcionamiento de la cláusula completa try-catch-finally. También investigue más acerca del uso y manejo de excepciones, así como la documentación del API respecto al método printStackTrace.
  6. Modifique el Ejemplo Pila para que:
    1. Agregue el método peek a la implementación. Recuerde que dicha operación hecha un vistazo al elemento que se encuentra en el tope de la pila y lo regresa, pero no lo elimina.
    2. Incorpore un atributo privado numérico (n) que lleve el control del número de elementos insertados en la pila. Al respecto no olvide:
      1. Inicializar explícitamente dicho atributo a cero en el constructor.
      2. Proporcionar únicamente el método de tipo get para el atributo n: size( ).
  7. Tomando como referencia el Ejemplo PruebaPilaGenerica, modifíquelo para que la pila genérica del Ejemplo Pila almacene otro tipo de objetos además de los de la clase Integer. Pruebe con al menos las siguientes clases:
    1. Double.
    2. String.
    3. Persona (del Ejemplo Persona).
    4. Cientifico (del Ejemplo Cientifico).
  8. Utilice una pila para verificar si, dada una expresión, ésta es o no un palíndromo. Un palíndromo es una frase o expresión que se lee o interpreta igual procesándola de izquierda a derecha, que de derecha a izquierda. Algunos ejemplos de palíndromos son (Nota: Simplifique el problema y no considere acentos ni la letra ñ en su implementación):
    1. 1991.
    2. 2002.
    3. Hannah.
    4. Reconocer.
    5. Se van sus naves.
    6. Ateo por Arabia iba raro poeta.
    7. Dábale arroz a la zorra el abad.
    8. Anita lava la tina.
    9. La ruta nos aportó otro paso natural.
    10. Las Nemocón no comen sal.
    11. No di mi decoro, cedí mi don.
    12. A la catalana banal, atácala.
  9. Realice la implementación del Algoritmo de verificación de balanceo presentado en la entrada Algunas aplicaciones de las pilas. Para lo anterior, construya una clase cuyo nombre sea Expresion, e implemente dicho algoritmo como uno de los servicios o acciones de la clase, identifique al método como verificaBalance de tal forma que su firma sea: public boolean verificaBalance( );.
    1. No olvide realizar las pruebas correspondientes para validar y verificar la funcionalidad de su propuesta.
    2. Sugerencia: implemente la verificación de equivalencia de símbolos como un método privado; es decir, un método que proporcione servicio a otros métodos de la misma clase (servicio interno), pero no a los objetos instanciados (servicios públicos).
  10. Convierta paso a paso utilizando papel y lápiz, las siguientes expresiones en su representación en interfija a su correspondiente notación postfija. Nota: el símbolo @ representa la potencia y es el de mayor precedencia. Note que se han proporcionado las soluciones correspondientes:
    1. A + B.
      1. A B +.
    2. A + B - C.
      1. A B + C -.
    3. (A + B) * (C - D).
      1. A B + C D - *.
    4. A @ B * C – D + E / F / (G + H).
      1. A B @ C * D - E F / G H + / +.
    5. ((A + B) * C – (D - E)) @ ( F + G).
      1. A B + C * D E - - F G + @.
    6. A – B / (C * D @ E).
      1. A B C D E @ * / -.
  11. Continuando con la idea planteada en el ejercicio anterior, convierta paso a paso las expresiones pero ahora a su representación en notación prefija. Se proporcionan a continuación las soluciones correspondientes:
    1. + A B.
    2. - + A B C.
    3. * + A B - C D.
    4. + - * @ A B C D / / E F + G H.
    5. @ - * + A B C - D E + F G.
    6. - A / B * C @ D E.
  12. Diseñe un algoritmo, así como su correspondiente implementación, para convertir una expresión en notación interfija a su correspondiente notación postfija y prefija respectivamente. Agregue los métodos siguientes a la clase Expresion iniciada en el Ejercicio 9. Asegúrese de probar sus implementaciones con al menos, los dos ejercicios anteriores:
    1. Postfija (firma del método public String obtenPostfija( )).
    2. Prefija (método public String obtenPrefija( )).
  13. Realice la implementación del Algoritmo de evaluación de una expresión en notación postfija presentado en la entrada Algunas aplicaciones de las pilas. Implemente dicho algoritmo como uno de los servicios de la clase Expresion iniciada en el Ejercicio 9. Proponga una firma de método ad hoc a la responsabilidad de éste pero utilice el identificador evaluaPostfija.
    1. No olvide realizar las pruebas correspondientes para validar y verificar la funcionalidad de su propuesta.
    2. Sugerencia: implemente la realización de la operación representada por símbolo como un método privado; es decir, un método que proporcione servicio a otros métodos de la misma clase (servicio interno), pero no a los objetos instanciados (servicios públicos).
  14. Repita el Ejercicio 9 con la clase Stack del API de Java:
    1. Desde el punto de vista de la ejecución, ¿nota alguna diferencia?
    2. ¿Tendría alguna ventaja o desventaja utilizar el API?
    3. Si la clase Pila del Ejemplo Pila fuera parte del API de Java, ¿cuál utilizaría y por qué?



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.