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.

13 de febrero de 2017

Ejemplos selectos de transición.

   Es imposible presentar, ya no digamos una entrada, sino en un blog completo un conjunto de ejemplos representativos para cualquier lenguaje de programación; sin embargo, en esta entrada se han seleccionado algunos ejemplos que pudieran ser de utilidad para la familiarización del lector con el lenguaje de programación Java, así como para comprender los ejemplos desarrollados en el blog.

Lectura de datos desde la terminal.
   Una de las tareas más comunes para cualquier programa es la lectura de datos desde la entrada estándar (teclado). Para los programas del blog que utilizan entrada de datos, se sugiere el enfoque que se presenta en el Ejemplo Lectura, el cual no hace uso de una interfaz gráfica de usuario (GUI) para centrar la atención en los aspectos relevantes (como la lectura de datos en este caso), y también para mantener más cortos los programas.

   El Ejemplo Lectura muestra en la línea 4 la importación de la clase Scanner del paquete java.util, el cual es un paquete con diversas utilerías; se recomienda en este momento echar un vistazo en el API de Java para tener una mejor idea de las clases que contiene el paquete java.util.

   Las instancias de la clase Scanner (como entrada) proporcionan diferentes servicios, entre ellos el método nextInt, el cual se encarga de obtener el siguiente número entero de la entrada estándar (líneas 17 y 19).

   Observe que el objeto entrada ha sido creado (línea 10) utilizando el objeto in de la clase System que, por decirlo de una manera simple, es la parte complementaria de System.out. Éste es el mecanismo usual para la entrada de datos. También note que han sido declarados tres objetos pertenecientes a la clase Integer (líneas 12-14).

   Java también maneja lo que se conoce como tipos de datos primitivos al estilo del lenguaje C; la clase Integer es en realidad una envoltura (wrapper) para el tipo de dato int. Una posible salida para el Ejemplo Lectura se muestra en la siguiente figura:

Una posible salida para el Ejemplo Lectura.

Estructuras de control.
   Java incorpora las estructuras de control tradicionales del enfoque estructurado, las cuales se asumen conocidas por el lector. Esta sección presenta un resumen necesariamente incompleto de las estructuras de control de selección y de repetición, con la única intención de tenerlas como una referencia inmediata.

   Estructuras de selección.
   El Ejemplo If muestra el uso de la estructuras de selección if y los operadores relacionales (líneas 20-31), así como el uso de la estructura de selección if-else (líneas 33-38).

   Los lectores familiarizados con el lenguaje de programación C notarán que tanto las estructuras de selección como los operadores relacionales son idénticos en Java, pero a diferencia de C, sí existe el tipo booleano, por lo que en Java es válido decir que una expresión se evalúa como verdadera o falsa según sea el caso.

   Note que las líneas 34 y 36 han hecho uso de una expresión de concatenación de cadenas de la forma:

objeto + cadena + objeto

lo cual es bastante común en Java. Dicha expresión lo que hace es precisamente concatenar las cadenas por medio del operador +. Note que aunque los objetos, como en el caso del ejemplo, no son cadenas, Java incorpora en la mayoría de sus clases el método toString, el cual se encarga de regresar una representación de cadena del objeto correspondiente.

   De hecho se recomienda que, en la medida de lo posible, las clases definidas por el usuario definan el método toString con la intención de mantener una compatibilidad con este tipo de situaciones como la que se acaba de describir. Tome en cuenta que aunque el método toString es heredado de la clase Object (la clase base en Java), es recomendable definir un comportamiento particular para una clase específica. Note también que no existe un llamado explícito del método sino un llamado implícito, mismo que se realiza a través del operador de concatenación de cadenas +.

   Una posible salida para el Ejemplo If se muestra en la siguiente figura:

Una posible salida para el Ejemplo If.

   Por otro lado, la siguiente tabla muestra la lista de operadores relacionales utilizados en Java:

Operador     Descripción
  ==            Igual que
    !=             Distinto de
                  <              Menor estricto que
                  >              Mayor estricto que
                <=             Menor o igual que
                >=             Mayor o igual que


   Estructuras de repetición.
   Las estructuras de repetición while, do-while y for se muestran respectivamente en los Ejemplos While, DoWhile y For.

   Los Ejemplos While, DoWhile y For se explican por sí mismos. Note que en los tres ejemplos se ha utilizado el tipo de dato primitivo int para la variable de control contador. La salida de los tres ejemplos es la misma, y se muestra en la siguiente figura:

Salida de los Ejemplos While, DoWhile y For.

Arreglos.
   El Ejemplo Arreglo muestra la creación, recorrido e impresión de un arreglo de enteros primitivos (int).

   La línea 7 define al objeto arreglo como un arreglo de enteros. Observe que el objeto es creado (new), con un tamaño específico (diez).

   Adicionalmente se definen también un par de variables:
  1. Un valor inicial: valor (línea 8).
  2. Un incremento: incremento (línea 9)
   Los arreglos en Java al ser creados y definidos como objetos, tienen definido el atributo público length, mismo que almacena la longitud del arreglo. Dicha propiedad es la que se utiliza como expresión condicional en los ciclos for de las líneas 12 y 17 respectivamente.

   El primer ciclo recorre el arreglo para inicializar y asignar los valores al arreglo en función de valor, incremento y la variable de control i.

   Por otro lado, el segundo ciclo realiza un recorrido tradicional para la impresión del arreglo en la salida estándar. La salida del Ejemplo Arreglo se muestra en la siguiente figura:

Salida del Ejemplo Arreglo.

Argumentos en la línea de comandos.
   El Ejemplo MainArgs muestra la forma de procesar los argumentos en la invocación de un programa, lo cual resultará útil y necesario en diversas ocasiones, como es el caso de algunos de los ejercicios planteados en el blog.

   El objeto args es un arreglo de cadenas (línea 6), por lo que en la línea 7 se verifica si se han proporcionado o no argumentos en la línea de comandos; en caso de que no, se reporta en la línea 8:

Salida del Ejemplo MainArgs sin argumentos.
 
    Si se proporcionaron argumentos, entonces se procesa la lista de argumentos con un ciclo (línea 10) y se imprime en la salida estándar dicha lista de argumentos, mismos que están almacenados en el arreglo de cadenas args:

Una posible salida del Ejemplo MainArgs con argumentos.

Excepciones.
   Las excepciones permiten una abstracción sobre el mecanismo de manejo de errores ligeros o condiciones que un programa pudiera estar interesado en atrapar y procesar.

   La idea subyacente en las excepciones es separar el manejo de este tipo de condiciones o errores de la lógica de funcionamiento inherente al programa.

   Una excepción es una situación anormal en la lógica de ejecución esperada por un programa, como el intentar clonar un objeto que no tiene implementado el mecanismo de clonación por ejemplo, manejar un formato de datos inadecuado para algún especificador, intentar realizar una operación de E/S en un canal cerrado, intentar acceder a elementos de una estructura de datos que no contiene elementos, entre muchísimas otras más.

   En la práctica, es posible definir en Java clases que gestionen excepciones o errores de una aplicación en particular. De hecho, una de las intenciones de las excepciones es la de, ante una problemática determinada, tratar de solucionarla en la medida de lo posible para continuar con el programa o aplicación, y no la de terminar con la primera dificultad que se presente.

   Un manejo completo y robusto de excepciones es una labor que, si bien su dominio no requiere de años, tampoco es una tarea trivial y queda fuera de los alcances de esta sección. Para obtener un poco más de información, refiérase a las entradas correspondientes relacionadas con las excepciones, en el contenido temático.

   Para muchos de los ejemplos desarrollados en el blog se hará uso (directo o indirecto) de la excepción definida por la clase RuntimeException del API, la cual maneja el conjunto de excepciones generadas durante la ejecución.

   La clase Throwable es la clase base de todas las excepciones que pueden ser lanzadas en Java, por lo que la revisión y estudio de esta jerarquía de clases del API es un punto inicial fundamental tanto para la comprensión de las excepciones, como para su referencia permanente. La relación de la jerarquía de clases en la que se encuentra la clase Exception en el contexto de Java, se expresa en un diagrama de clases de UML (Unified Modeling Language) de la siguiente figura:

Relación UML de la jerarquía de clases Exception de Java.

   La clase Throwable tiene dos derivaciones:
  1. Error: condiciones excepcionales que son externas al programa o la aplicación; usualmente no es posible anticiparlas o recuperarse de ellas (mal funcionamiento del hardware o del sistema). Los programas sencillos normalmente no atrapan o lanzan este tipo de excepciones.
  2. Excepción: indican que ocurrió algún problema pero, al menos en principio, no un problema serio. La mayoría de los programas que escriba atraparán o lanzarán este tipo de excepciones en lugar de los de la clase Error.
   La clase RuntimeException es un tipo especial de excepción reservada para indicar el uso incorrecto de un API por ejemplo. En este sentido, el Ejemplo ExcepcionEDVacia muestra la definición de una excepción bastante sencilla pero útil; de hecho, la clase mostrada en dicho ejemplo es la que se utiliza para las estructuras de datos desarrolladas en el blog.

   Note que la excepción ExcepcionEDVacia es una subclase de la clase RuntimeException. La clase RuntimeException es la super clase de las excepciones que pueden ser lanzadas durante la ejecución de la máquina virtual de Java.

Genéricos.
   La definición de jdk 5.0 introdujo nuevas modificaciones y extensiones a Java; una de ellas fue el aspecto relacionado con los genéricos (generics).

   Los genéricos son en sí mismos todo un tema de estudio, pero dado que se utilizan en la mayoría de los ejemplos del blog respecto a la definición de las estructuras de datos, aquí se presenta una exagerada y necesariamente incompleta y breve introducción.

Los genéricos permiten una abstracción sobre los tipos de datos o los objetos que se procesan, y dentro de sus objetivos se encuentran el eliminar la ambigüedad latente que existía en la conversión forzada de tipos (cast) y lo molesto de su realización, ya que usualmente un programador sabe cual es el tipo de dato que está procesando cuando utiliza una colección de datos por ejemplo.

   El siguiente fragmento de código, se utilizaba antes de los genéricos:

      List lista = new LinkedList();
      lista.add("Genericos en Java");
      String cadena = (String) lista.get(0);


   Con los genéricos el programador pone una marca (clase o tipo de datos en particular) por decirlo de alguna manera, para restringir los datos a almacenar y recuperar:

      List<String> lista = new LinkedList<String>();
      lista.add("Genericos en Java");
      String cadena = lista.get(0);


  El cambio es aparentemente simple pero significativo ya que evita los errores intencionales o accidentales en tiempo de ejecución, además de que permite al compilador hacer una verificación sobre los tipos de datos que se gestionan. Note que en el segundo fragmento de código, el cast ha sido eliminado.

Programación orientada a objetos.

   En entradas anteriores se discurrió en los elementos fundamentales de la orientación a objetos:
   La intención de dichas entradas es la de proporcionar al lector un panorama general del paradigma orientado a objetos sin asociarlo necesariamente con la programación, y mucho menos con algún lenguaje de programación en particular.

   Un lenguaje de programación es sólo un medio para el paradigma, no el paradigma en sí. Por otro lado, el ejercicio o labor de la programación es la forma de aplicar los conceptos asociados al paradigma.

   Las entradas subsecuentes irán detallando al lector los conceptos presentados pero desde la perspectiva de la programación y su aplicación en algún lenguaje de programación. Sin embargo, es importante aclarar que el objetivo del blog no es enseñar los lenguajes de programación, sino el de utilizarlos como un medio para hacer tangibles los conceptos de orientación a objetos.

   El blog proporciona entradas particulares que introducen a algunos de los aspectos de los lenguajes de programación Java y C++ que podrían ser de utilidad para el lector, pero de ninguna manera pretende cubrir los elementos completos de dichos lenguajes, sino solamente presentar un panorama general:
   Además de lo anterior el blog presenta las bases del paradigma orientado a objetos en el contexto de su aplicación a la programación utilizando al lenguaje de programación Java y C++.

   La intención principal de las entradas que siguen este enfoque que se acaba de mencionar, es la de concretizar en algún lenguaje de programación los conceptos más distintivos del paradigma orientado a objetos, para que en entradas posteriores se puedan aplicar al desarrollo de las estructuras de datos, y mejorar así tanto la comprensión de los conceptos, como la experiencia del lector.


10 de febrero de 2017

Ejercicios selectos (Orientación a objetos).

  1. Investigue más acerca de la historia y desarrollo de la programación orientada a objetos.
  2. Xerox es actualmente una compañía que desarrolla equipo de foto copiado e impresión entre otras cosas. Investigue cuál era el sistema operativo que utilizaban en los años de Xerox PARC (Palo Alto-California Research Center) que por cierto, ya incluía una GUI (Interfaz Gráfica de Usuario) pionera de las GUI que actualmente se utilizan.
  3. Investigue la historia y la ideología del lenguaje de programación Smalltalk. Aproveche para conocer el nombre de su(s) creador(es).
  4. Investigue la historia y la ideología del lenguaje de programación Eiffel. Aproveche para conocer el nombre de su(s) creador(es).
  5. Investigue la historia del lenguaje de programación C++. Aproveche para conocer el nombre de su(s) creador(es).
  6. Investigue el papel del Dr. Alan Curtis Kay en la concepción y desarrollo de la programación orientada a objetos.
  7. Investigue qué otros paradigmas de programación existen.
  8. Investigue qué lenguajes de programación actuales soportan el paradigma orientado a objetos, cuáles lo soportan de manera nativa y cuáles como una extensión.


8 de febrero de 2017

Orientación a objetos y modularidad.

   La modularidad no está exclusivamente relacionada con los procedimientos o funciones de la programación estructurada, sino con el grado en el que los componentes de un sistema pueden ser separados y reutilizados.

   En función de lo anterior, tanto los métodos como los objetos son en sí mismos módulos de una aplicación determinada y en consecuencia, las clases de las que se derivan constituyen los módulos del sistema, por lo que de aquí en adelante se hará referencia a la modularidad de manera indistinta tanto para clases, como para los métodos de las clases.

   La modularidad ayuda también a hacer el código más comprensible, y esto a su vez hace que en consecuencia, al menos en principio, el código sea más fácil de mantener. Sin embargo, sin las debidas y pertinentes consideraciones, la modularidad tiene también sus consecuencias negativas, mismas que están en función directa de dos conceptos fundamentales en el desarrollo de software en general, y en el paradigma orientado a objetos en particular:
  1. Cohesión.
  2. Acoplamiento.
Cohesión y Acoplamiento.
   La cohesión está relacionada con la integridad interna de un módulo. Es el grado o nivel de relación o integridad entre los elementos que componen un módulo.

   El nivel de cohesión determina qué tan fuerte están relacionados cada unos de los elementos de funcionalidad expresados en el código fuente de un módulo.

   Por otro lado, el acoplamiento describe qué tan fuerte un módulo está relacionado con otros, es decir, es el grado en que un módulo depende de cada uno de los otros módulos que componen un sistema.

   El acoplamiento también puede ser referido o entendido como dependencia, lo cual ayuda a recordar que lo que se desea es mantener un bajo nivel de dependencia entre los módulos, es decir un bajo acoplamiento.

   En general, se desea que los módulos de un programa o sistema tengan, un alto nivel de cohesión y un bajo nivel de acoplamiento. El paradigma orientado a objetos persigue y enfatiza dichos objetivos.


7 de febrero de 2017

Orientación a Objetos (conceptos).

Objetos.
   Los objetos son esencialmente abstracciones. Son entidades que tienen un determinado estado, un comportamiento (determinado por sus responsabilidades), y una identidad.
  • El estado está representado por los datos o los valores que contienen los atributos del objeto, los cuales son a su vez otros objetos o variables que representan las características inherentes del objeto.
  • El comportamiento está determinado por las responsabilidades o servicios del objeto los cuales son definidos por los métodos, mismos que se solicitan a través de mensajes a los que dicho objeto sabe responder.
  • La identidad es la propiedad que tiene un objeto que lo distingue o hace diferente de los demás. La identidad está representada por un identificador.
   Un objeto es una entidad que contiene en sí mismo, al menos en principio, toda la información necesaria que permite definirlo, identificarlo, y accederlo respecto a otros objetos pertenecientes a otras clases, e incluso respecto a objetos de su misma clase. La forma de acceder a un objeto es a través de su interfaz. La interfaz de un objeto es el conjunto de servicios públicos que ofrece el objeto, mismos que se solicitan a través de mensajes o solicitudes realizadas a dicho objeto.

   Los objetos se valen de mecanismos de interacción llamados métodos que favorecen la comunicación entre ellos. Dicha comunicación favorece a su vez el cambio de estado en los propios objetos. Esta característica define a los objetos como unidades indivisibles en las que no se separa el estado del comportamiento.

   Orientación a objetos vs. enfoque estructurado.
   La orientación a objetos difiere del enfoque estructurado básicamente en que en la programación estructurada los datos y los procedimientos están separados y sin relación explícita, ya que lo único que se busca en la programación estructurada es el procesamiento de los datos de entrada para obtener los datos de salida.

   La programación estructurada utiliza en primera instancia un enfoque basado en procedimientos o funciones, y en segunda instancia, las estructuras de datos que dichos procedimientos o funciones manejan, cumpliendo así con la ecuación planteada por Niklaus Wirth:

Algoritmos + Estructuras de Datos = Programas

   Por otro lado, un programa en un enfoque OO solicita estructuras de datos (las cuales son otros objetos) para llevar a cabo un servicio.

   La perspectiva OO también define programas compuestos por algoritmos y estructuras de datos esencialmente; sin embargo, lo hace desde un enfoque diferente. En la orientación a objetos la descripción del objeto se da en términos de responsabilidades y características; y así, al analizar un problema en dichos términos, se eleva el nivel de abstracción.

   Lo anterior permite una mayor independencia entre los objetos, lo cual es un factor crítico en la solución de problemas complejos. Cabe mencionar por último en este sentido, que al conjunto completo de responsabilidades asociadas a un objeto se le refiere comúnmente como protocolo.

Objetos y clases.
   Todos los objetos son instancias de una clase (categoría). Esta relación de un objeto con una clase, hace que los objetos tengan las siguientes características:
  • El método invocado por un objeto en respuesta a un mensaje es determinado por la clase del objeto receptor.
  • Todos los objetos de una clase determinada utilizan el mismo método en respuesta a mensajes similares.
  • Las clases pueden ser organizadas en una estructura jerárquica de herencia como la que se muestra en la figura de abajo.
  • Una clase hija o subclase heredará todas las características de la clase de la que deriva (clase padre clase madre o súper clase).
  • Una clase abstracta es una clase de la que no se derivan instancias directamente, sino que es utilizada únicamente para crear subclases.
  • La búsqueda del método a invocar en respuesta a un mensaje determinado inicia en la clase del receptor. Si no se encuentra el método apropiado, la búsqueda se realiza en la clase padre, si no se encuentra ahí, se busca en la clase padre de la clase padre y así sucesivamente hasta encontrar el método correspondiente.
  • Si se encuentran métodos con el mismo nombre dentro de la jerarquía de clases, se dice que el método procesado sobre escribe (override) el comportamiento heredado.

Diagrama de ejemplo de jerarquía de clases.

   La figura anterior presenta una posible jerarquía de clases para un Ser Vivo. Más que una clasificación o taxonomía completa, la figura muestra el concepto de herencia a través de un árbol. En la figura puede observarse que los elementos que se derivan comparten características (atributos) y comportamientos (métodos) semejantes.

   Así por ejemplo, es posible decir que Flipper es una instancia particular de todos los posibles delfines que podrían existir. A su vez, un Delfín comparte características comunes con una Ballena en cuanto a que ambos son Cetáceos pero difieren en otras (si un delfín y una ballena coincidieran en todo (características y comportamiento) serían de la misma clase).

   Un Delfín es un Cetáceo, y un Cetáceo es un Mamífero. En muchas ocasiones a éste tipo de relaciones se le denomina "es un" (is a), y es una característica útil para identificar herencia pero no es la única.

   Existe otro tipo de relación y se denomina "tiene" (has-a). Estas relaciones son dos formas importantes de abstracción en la orientación a objetos:
  1. La idea de división en partes (has-a): un automóvil tiene un motor, tiene una transmisión, tiene un sistema eléctrico, etc.
  2. La idea de división en especializaciones (is-a): un automóvil es un medio de transporte, es un objeto de cuatro ruedas, es un objeto que se dirige con un volante, etc.
   Finalmente, la figura anteriormente presentada muestra que Flipper es un Delfín, que un Delfín es un Cetáceo, y que éste a su vez es un Mamífero; que un Mamífero pertenece al Reino Animal, y que el Reino Animal es parte de los seres vivos representados por la súper clase de todas las clases o clase base Ser Vivo.

3 de febrero de 2017

Paradigma.

   El concepto de paradigma resulta fundamental en la comprensión del paradigma (valga la redundancia) orientado a objetos.

   Antes de proporcionar la definición que se adoptará en el blog, se describirán algunas de las definiciones que existen de paradigma [wordreference]:
  • paradigma m. Ejemplo o ejemplar: esa chica es el paradigma de la paciencia.
  • paradigma ling. Cada uno de los esquemas formales a los que se ajustan las palabras, según sus respectivas flexiones: paradigma de la conjugación verbal.
  • paradigma ling. Conjunto de elementos de una misma clase gramatical que pueden aparecer en un mismo contexto: paradigma de las preposiciones.
  • paradigma ejemplo o modelo. En todo el ámbito científico, religioso u otro contexto epistemológico, el término paradigma puede indicar el concepto de esquema formal de organización, y ser utilizado como sinónimo de marco teórico o conjunto de teorías.
   Pero entonces, ¿qué entender por paradigma de programación?

   La palabra paradigma irrumpió en la ciencia y el vocabulario moderno a través del influyente libro "The Structure of Scientific Revolutions" del historiador de la ciencia Thomas Samuel Kuhn.

   Thomas Kuhn utilizó el término en la forma de la última definición: un paradigma es un modelo para describir un conjunto de teorías, estándares y métodos que en conjunto representan una forma de organizar el conocimiento, esto es, una forma de ver el mundo.
 
   Con base en a lo anterior, a lo largo del blog se entenderá como paradigma de programación al modelo de programación utilizado, el cual está descrito y definido por un conjunto de teorías, estándares y métodos que en conjunto, representan una propuesta de solución por software hacia una problemática determinada.

   Kuhn utilizó la ilusión óptica de la siguiente figura para ilustrar el concepto de paradigma. En dicha figura puede verse un conejo o un pato dependiendo de la perspectiva que se utilice:

Ilusión óptica del conejo-pato creada por Joseph Jastrow.
 
    El paradigma orientado a objetos cambió la perspectiva respecto del enfoque estructurado, el cual era el paradigma dominante hasta entonces.

Una perspectiva diferente.
   El concepto sobre el que subyace la esencia de la orientación a objetos es la abstracción. Por lo que, al pensar en este paradigma, se debería tener en mente una perspectiva basada en los siguientes conceptos:
  1. Entidades: agentes u objetos en interacción, donde cada uno de ellos tiene un rol y características denotadas por atributos.
  2. Responsabilidades: cada objeto proporciona un conjunto de servicios o lleva a cabo acciones que son utilizadas por otras entidades u objetos. Las responsabilidades determinan el comportamiento del objeto.
  3. Mensajes: en la POO la acción es iniciada por la transmisión de un mensaje a un objeto responsable de dicha acción. En respuesta al mensaje, el objeto receptor llevará a cabo un método para satisfacer la solicitud que le fue realizada.
   Los tres elementos anteriormente mencionados constituyen los fundamentos primordiales de la orientación a objetos. A lo largo del blog se desarrollarán de manera progresiva y se ejemplificarán con programas.
 
   Mensajes, procedimientos/funciones y métodos.
   Los mensajes son solicitudes específicas de alguno de los servicios o responsabilidades asignadas a un objeto. Los mensajes tienen un receptor específico, por lo que son enviados a un objeto en particular y pueden contener lista de argumentos.

   Los mensajes son llevados a cabo por métodos, los cuales son algoritmos asociados a un objeto (o a una clase de objetos), cuya ejecución se desencadena tras la recepción de un mensaje. En este sentido, tanto los métodos como los procedimientos o funciones son un conjunto de pasos bien definidos que llevan a cabo una acción; sin embargo, los mensajes y los procedimientos o funciones se distinguen esencialmente por dos aspectos:
  1. En un mensaje hay un receptor designado para dicho mensaje. Las funciones o procedimientos son generales, no hay un receptor específico.
  2. La interpretación o método utilizado para responder al mensaje es determinado por el receptor, y puede variar en función del receptor. Las funciones o procedimientos son únicos tanto en sus identificadores como en sus listas de parámetros.

2 de febrero de 2017

Orientación a Objetos (orígenes).

   Las características principales de lo que actualmente se denomina Programación Orientada a Objetos (POO) surgen en el siglo XX alrededor de 1960; y aunque algunos autores difieren en sus orígenes, comparto la idea de que los conceptos de la POO tienen su inicio en Simula 67, un lenguaje diseñado en el centro de cómputo noruego en Oslo. Simula --dicho sea de paso-- es un lenguaje para simulaciones creado por Ole-Johan Dahl y Kristen Nygaard.

   Posteriormente, en Agosto de 1981, se publica en la revista Byte la descripción del lenguaje de programación Smalltalk, el cual refinó algunos de los conceptos originados con el lenguaje Simula. Smalltalk fue desarrollado en Xerox PARC (Palo Alto-California Research Center).

   Lo anterior dio pie a que en la década de 1980 los lenguajes de programación Orientados a Objetos (OO) tuvieran un rápido auge y expansión, por lo que la POO se fue convirtiendo en el estilo de programación dominante a mediados de los años ochenta del siglo pasado. Con todo, este modelo de programación continúa vigente hasta nuestros días.

   La POO fue una de las primeras propuestas de solución para ayudar a resolver la denominada, aunque no generalmente aceptada, "crisis del software". En este sentido es importante decir que, si bien las técnicas OO pueden facilitar la creación de complejos sistemas de software a través de mecanismos alternativos de abstracción, no son la panacea universal ni las "balas de plata".

   Programar una computadora sigue siendo una de las tareas más difíciles jamás realizadas por un ser humano. Volverse experto en programación requiere, no sólo de saber manejar herramientas y conocer técnicas de programación, sino que además es preciso contar también con:
  • Talento.
  • Creatividad.
  • Ingenio.
  • Lógica.
  • Habilidad para construir y utilizar abstracciones.
  • Experiencia
  • Etcétera.
   Por lo anterior, hacer un uso efectivo de los principios OO requiere de una visión del mundo desde una perspectiva distinta; sobre todo si se parte de la base de resolución de problemas a través de un enfoque estructurado.

   Es importante señalar y tener presente desde este momento, que el uso de un lenguaje de POO no hace, por sí mismo, que se programe OO, ya que se podría tener en el mejor de los casos, un programa o sistema implementado con un enfoque estructurado pero programado en un lenguaje orientado a objetos.
 
   La POO requiere en primera instancia de la comprensión del paradigma orientado a objetos, por lo que se debería iniciar el aprendizaje con un recorrido conceptual partiendo por el concepto de paradigma que se utilizará a lo largo del blog.


1 de febrero de 2017

Referencias sugeridas.

  1. Bracha, Gilad, "Generics", Oracle Corporation, The Java Tutorials, (último acceso febrero 2017).
  2. Budd, Timothy A., "An Introduction to Object-Oriented Programming", Addison Wesley.
  3. Byte Magazine, Smalltalk, Volume 06 Number 08, A McGraw Hill Publication.
  4. Deitel, H. M. y Deitel, P. J., "Cómo Programar en Java", Prentice Hall.
  5. Goldberg Adele and Robson David, "Smalltalk-80 The Language and its Implementation", Addison-Wesley Publishing Company.
  6. Gökel Canol, Computer Programming using GNU Smalltalk, GNU Free Documentation License 1.3.
  7. Kuhn, Thomas Samuel., "The Structure of Scientific Revolutions", University of Chicago Press.
  8. Langsam, Yedidyah, Augenstein, M. J. and Tenenbaum, A. M., "Estructuras de Datos con C y C++", Prentice Hall Hispanoamericana.
  9. Mark Allen Weiss, "Estructuras de Datos en Java", Addison Wesley.
  10. McConnell, Steve, "Code Complete", Microsoft Corporation.
  11. Oracle, The Java Tutorials, Java Documentation.
  12. Shalloway, Alan and Trott James R., "Design Patterns Explained A New Perspective on Object Oriented Design", Addison Wesley.
  13. Sierra, Kathy and Bates, Bert, "Sun Certified Programmer & Developer for Java 2", Mc Graw Hill/Osborne.
  14. Schildt, Herbert, "C++: The Complete Reference", Mc Graw Hill Osborne.
  15. Stroustrup, Bjarne, "El Lenguaje de Programación C++", Addison Wesley.
  16. Unified Modeling Language Web Site, (último acceso febrero 2017).
  17. Wirth, Niklaus, "Algoritmos y Estructuras de Datos", Prentice Hall.