Introducción a la Programación Orientada a Objetos
La Programación Orientada a Objetos (POO u OOP según sus siglas en inglés) es un paradigma de programación que usa objetos y sus interacciones para diseñar aplicaciones y programas de computadora. Una de las ventajas de este paradigma de programación radica en que utiliza abstracciones con las que están acostumbradas a trabajar las personas (que también piensan y se manejan en términos de objetos).
Una de las ventajas de la programación a objetos es que utiliza abstracciones próximas a la forma de pensar de las personas: los objetos, que tienen propiedades y comportamiento.
El principal objetivo de la orientación a objetos es reducir la complejidad del desarrollo y mantenimiento del software, ya que en la decada de los 60, con la aparición de la denominada "crisis del software" los programas comenzaron a alcanzar una gran complejidad. Los conceptos de orientación a objetos datan de los años 60, pero dado que la tecnología no estaba acorde con su implementación, se mantuvo sólo como concepto hasta su gran expansión a finales de los 80.
La programación orientada a objetos no supone una ruptura radical frente al paradigma de la programación estructurada / imperativa predominante hasta su aparición, sino que supone una evolución. Frente a la programación estructurada, cuyos programas separan datos y estructuras de datos de funciones y procedimientos, la OOP encapsula en una misma abstracción estos dos elementos clásicos de los sistemas de información: datos y los procesos. Esto lo hace a través de una nueva abstracción:el objeto.
Frente a la programación estructurada, que mantiene una separación clara entre datos y funciones/procedimientos, la programación orientada a objetos encapsula en una misma entidad datos (propiedades) y procedimientos (métodos): los objetos
La orientación a objetos consiste en una visión de los objetos como entidades activas que ejecutan acciones sobre sus propios datos en respuesta a peticiones externas. No considera a unos y a otros (datos y procesos) como realidades aisladas susceptibles de analizarse e implantarse por separado. Los objetos tratan de abstraer características comunes que podrán compartirse entre varias aplicaciones y reutilizarse todo lo posible. La creación de un nuevo sistema consiste esencialmente en una labor de ensamblado de objetos preexistentes, completada con el desarrollo de un porcentaje reducido de nuevos objetos, que a su vez alimentaran las correspondientes librerías para poder ser utilizados en los próximos sistemas.
A lo largo de los años han surgido numerosos lenguajes de programación que implementaban las ideas de la orientación objetos, tales como Simula (1967), SmallTalk (1972), Oberon (1985), Modula-3 (1990), Eiffel (1985), C++ (años 80), Java (mediados de los 90). En el siguiente cuadro puede verse la evolución de estos lenguajes y la relación entre ellos.
Evolución histórica de la OOP.
Los orígenes: Simula-67
Las ideas básicas de la orientación a objetos nacen a principios de los años 60 en la universidad de Noruega. Un equipo dirigido por el Dr. Nygaard se dedicaba a desarrollar sistemas informáticos para realizar simulaciones de sistemas físicos como simular el funcionamiento y obtener el rendimiento de un motor. La dificultad en la que se encontraban era doble. Por un lado los programas eran muy complejos y, por otro, forzosamente tenían que ser muy modificados. Este segundo punto era especialmente problemático, ya que la razón de ser de los programas era el cambio y no sólo se requerían varias iteraciones para obtener un producto con el rendimiento deseado, sino
que muchas veces se quería obtener diversas alternativas viables cada una con sus ventajas e inconvenientes. La solución que idearon fue diseñar el programa paralelamente al objeto físico. Es decir, si el objeto físico tenía cien componentes, el programa también tendría cien módulos, uno por cada pieza. Partiendo el programa de esta manera, había una total correspondencia entre el sistema físico y el sistema informático. Así, cada pieza física tenía su abstracción informática en un módulo. De la misma manera que los sistemas físicos se comunican
enviándose señales, los módulos informáticos se comunicarían enviándose mensajes. Este enfoque resolvió los dos problemas planteados. Primeramente, ofrecía una forma natural de partir un programa muy complejo y, en segundo lugar, el mantenimiento pasaba a ser controlable. Para implementar estas ideas lo que se hizo fue crear un lenguaje para darle soporte, Simula-67, que continua utilizándose actualmente.
que muchas veces se quería obtener diversas alternativas viables cada una con sus ventajas e inconvenientes. La solución que idearon fue diseñar el programa paralelamente al objeto físico. Es decir, si el objeto físico tenía cien componentes, el programa también tendría cien módulos, uno por cada pieza. Partiendo el programa de esta manera, había una total correspondencia entre el sistema físico y el sistema informático. Así, cada pieza física tenía su abstracción informática en un módulo. De la misma manera que los sistemas físicos se comunican
enviándose señales, los módulos informáticos se comunicarían enviándose mensajes. Este enfoque resolvió los dos problemas planteados. Primeramente, ofrecía una forma natural de partir un programa muy complejo y, en segundo lugar, el mantenimiento pasaba a ser controlable. Para implementar estas ideas lo que se hizo fue crear un lenguaje para darle soporte, Simula-67, que continua utilizándose actualmente.
SmallTalk
Pero, poco a poco, fue obteniéndose otro beneficio muy importante, que es la razón principal por la que la industria informática se ha abocado a la orientación a objetos. Se trata de la reusabilidad. En el proceso de construcción de un programa se obtienen piezas para futuros programas. El siguiente paso se da en los años 70 en los Estados Unidos. Xerox tiene un centro de investigación en Palo Alto, donde trabajan en conceptos que puedan convertirse en productos industriales al cabo de 10 a 20 años. Así pues, en aquellos años contrataron a un joven llamado Alan Kay para que llevase a término las ideas que proponía en su tesis doctoral, la propuesta de construcción de un ordenador llamado Dynabook, adecuado para ser utilizado por niños. El ordenador no tenía teclado, la pantalla era sensible al tacto y la mayor parte de la
comunicación era gráfica. Al desarrollar este proyecto se inventó el 'mouse' y los entornos gráficos. Al volver a encontrarse con una programación compleja y experimental, como en el caso de Nygaard, decidieron crear un entorno y lenguaje llamado Smalltalk.
comunicación era gráfica. Al desarrollar este proyecto se inventó el 'mouse' y los entornos gráficos. Al volver a encontrarse con una programación compleja y experimental, como en el caso de Nygaard, decidieron crear un entorno y lenguaje llamado Smalltalk.
SmallTalk, aunque inspirado por Simula, introdujo algunas novedades. Una de ellas fue que estaba diseñado para ser un sistema completamente dinámico, en el que las clases podían ser creadas y modificadas en tiempo de ejecución, en vez de hacerlo de forma estática en tiempo de compilación.
SmallTalk influyó a otros lenguajes de programación, como Lisp, que introdujo técnicas orientadas a objetos a través de la máquina Lisp, o incluso a nivel de hardware, apareciendo diseños de arquitecturas de procesadores con soporte para la gestión de objetos en memoria como el procesador Intel_iAPX_432 o el procesador Linn_Smart_Rekursiv, que no tuvieron éxito.
C++
Smalltalk tuvo una gran difusión y cuando en los años 80 en ATT-Bell quisieron crear un sucesor al lenguaje C, incorporaron las principales ideas de Smalltalk y de Simula, creando el lenguaje C++. La aparición de este último lenguaje supuso un espaldarazo tal que puede afirmarse que se debe al lenguaje C++ la gran extensión de los conceptos de la orientación a objetos.
A finales de los 90, el paradigma de la OOP pasó a ser el dominante, y su dominio se vió potenciado por la aparición de las interfaces gráficas de usuario y los entornos de desarrollo rápido de dichas interfaces, para los que la OOP estaba mejor preparada. Este tipo de herramientas también impulsó el avance de laprogramacion-orientada-a-eventos programación orientada a eventos, aunque este concepto también es compatible con otros paradigmas de programación.
Modula-2
Modula-2 y su sucesor, el lenguaje Oberon, siguieron avanzando en conceptos como el de abstracción de datos y programación modular. La aproximación de estos lenguajes a la orientación a objetos era muy diferente a la de C++ (que no es un lenguaje orientado a objetos puro, pues permite hacer programación estructurada) difiriendo también de la de SmallTalk.
Extensiones orientadas a objetos de lenguajes estructurados.
La popularidad de la OOP hizo que se incluyesen conceptos de OOP a lenguajes como Ada, BASIC, Fortran o Pascal, que no eran orientados a objetos. Esto a menudo trajo problemas de compatibilidad y mantenibilidad del código, al no estar plenamente preparados estos lenguajes para muchos de los conceptos y principios de diseño de la OOP.
Java.
Java, que sí es un lenguaje OOP puro, se ha convertido en el lenguaje de programación de uso más extendido, en parte por su parecido a C y C++, en parte a que palia algunos problemas de complejidad de estos lenguajes (como por ejemplo la gestión de la memoria, que no debe ser realizada por el programador) y fundamentalmente por no ser solamente un lenguaje de programación, sino un entorno de ejecución de aplicaciones independiente de la plataforma hardware subyacente (la máquina virtual Java), que no es más que un interprete que traduce a instrucciones del procesador un código compilado de bajo nivel (el bytecode Java).
.NET y otros lenguajes basados en máquinas virtuales.
La independencia de la plataforma de JAVA fue una característica que se hizo muy atractiva para el resto de competidores en el desarrollo de lenguajes de programación, por lo que pronto fue emulada. La suite Microsoft .NET introdujo objetivos y características similares, a través del .NET Runtime, con la novedad de incluir soporte para múltiples lenguajes (en Java todo circulaba alrededor de un lenguaje, .NET ofrecía la posibilidad de traducir mútiples lenguajes a un mismo código neutro intermedio, interpretado por el .NET Runtime). Los principales lenguajes de .NET fueron C#, Visual Basic .NET.
Además, Microsoft (impulsor de .NET), frente a su costumbre habitual, publicó la expecificación del .NET Runtime, .NET código intermedio (conocido como CIL) y su biblioteca de objetos soportada por la plataforma, con lo que pronto surgió un proyecto libre que diera soporte al código escrito en los lenguajes de .NET: el proyecto MONO. Este proyecto permite la ejecución de programas .NET en plataformas Linux. La especificación .NET fue aprobada como estándar por la ECMA e ISO
Modernos lenguajes orientados a objetos en entornos de script.
La orientación a objetos ha llegado a todos los ámbitos, de forma que empezaron a surgir lenguajes de script interpretados que incorporan las características de la OOP, aunque conservando la posibilidad de programar según el paradigma estructurado:
- Python, y su equivalente para la plataforma Java Groovy.
- Ruby.
- Javascript.
- VBScript.
Elementos característicos de la OOP.
La OOP se basa en una serie de conceptos que aportan una visión diferente a la aportada por la programación tradicional, por lo que su comprensión es fundamental para la comprensión en conjunto de la programación orientada a objetos. Asimilar estas ideas implica aprender las diferencias y similitudes entre este método y la programación estructurada, y es lo que lleva a producir un código con un nivel superior de abstracción (lo que implica futura reutilización).
Los principios del modelo orientado a objetos son abstracción, encapsulación, modularidad y jerarquía o herencia, fundamentalmente, y en menor grado concurrencia, persistencia. Según Booch en 1986 “si un modelo que se dice orientado a objetos no contiene alguno de los primeros cuatro elementos, entonces no es orientado a objetos.”
Estos principios pueden aparecer (todos ellos o de forma aislada) en lenguajes de programación orientados a objetos. No obstante, los conceptos de clase, objeto o instancia, propiedades y métodos y paso de mensajes entre objetos deben ser comunes a todos los lenguajes.
Clases y objetos.
Clases y objetos pueden parecer conceptos similares pero existe una clara diferencia conceptual entre ellos. Las clases son un concepto estático definido en el programa fuente, son una abstracción de la esencia de un objeto, mientras que los objetos son entes dinámicos que existen en tiempo y espacio y que ocupan memoria en la ejecución de un programa.
Un objeto no es una clase, sin embargo una clase puede ser un objeto. Los objetos se crean cuando se recibe un mensaje solicitando creación por la clase padre. El nuevo objeto toma sus métodos y propiedades de su clase padre. Los datos pueden ser de dos tipos: variables de clase, que tiene valores almacenados en una clase (también conocidos como propiedades estáticas) y variables de instancia que tiene valores asociados únicamente con cada instancia u objeto creado a partir de una clase.
Las clases proporcionan modularidad y estructuracíon de los datos a los programas orientados a objetos. Aunan por tanto datos y funciones en una misma abstracción. No obstante, y frente a la programación estructurada, una clase debería ser entendible para no programadores que estén familiarizados con el dominio del problema (expertos de negocio).
A cada uno de los objetos individuales pertenecientes a una clase se le denomina instancia. De aquí nace otra definición de objeto: es un modelo o Instancia de una clase. No todas las clases tienen por qué tener instancias; a una clase sin instancias se te denomina clase abstracta.
Breve apunte sobre modelado de datos en OOP (análisis de clases).
Un problema fundamental que aparece ante cualquier persona que afronta el problema de analizar una aplicación orientada a objetos es la identificación y elección de las clases apropiadas para la misma. El diseño de clases es un tema de la Ingeniería del Software muy estudiado, aunque de forma muy resumida se pueden adelantar algunas técnicas de modelado de datos orientado a objetos.
Análisis léxico
Partiendo de un enunciado lo más preciso posible del problema, sobre él se estudian los nombres que aparecen. Los mismos constituyen posibles clases del modelo (parecen entidades/abstracciones existentes en el problema). Los verbos son operaciones candidatas de las clases con que aparecen o posibles relaciones de éstas con otras. Este método es una adaptación de idéntico método propuesto para el análisis estrúcturado (como se cita por ejemplo en Pressman: Ingeniería del Software, un enfoque práctico).
Diseño por contrato.
Técnica de modelado también conocida como "tarjetas CRC" o diseño dirigido por responsabilidades. Intenta definir claramente las responsabilidades (qué información posee, qué operaciones se le pueden solicitar) de cada clase, así como cuáles son las otras clases a las que puede pedir colaboración para cumplir su misión (Collaboration Responsibilities Cards).
Mediante esta técnica pueden hacerse revisiones de completitud (¿falta algún colaborador?) y coherencia (¿hay alguna clase cuyo concepto no está bien definido?) del sistema, a la vez que van surgiendo, de forma natural, las operaciones e informaciones que ha de soportar una cierta clase.
Mediante esta técnica pueden hacerse revisiones de completitud (¿falta algún colaborador?) y coherencia (¿hay alguna clase cuyo concepto no está bien definido?) del sistema, a la vez que van surgiendo, de forma natural, las operaciones e informaciones que ha de soportar una cierta clase.
Propiedades o atributos de clases.
Los objetos contienen atributos. Éstos son los elementos que definen el estado de un objeto. Son los equivalentes a los datos (simples, compuestos o estructurados) de la programación estructurada.
Existen dos tipos de atributos: atributos de clase y atributos de instancia (o de objeto). Los atributos de clase existen independientemente de que se haya creado o no el objeto de una determinada clase. Sin embargo los atributos de instancia tienen su ciclo de vida asociado al del objeto al que pertenecen, es decir, se crean con el objeto, se pueden usar mientras se referencia el objeto y desaparecen cuando el objeto desaparece.
Existen dos tipos de atributos: atributos de clase y atributos de instancia (o de objeto). Los atributos de clase existen independientemente de que se haya creado o no el objeto de una determinada clase. Sin embargo los atributos de instancia tienen su ciclo de vida asociado al del objeto al que pertenecen, es decir, se crean con el objeto, se pueden usar mientras se referencia el objeto y desaparecen cuando el objeto desaparece.
Métodos.
Los objetos contienen un conjunto de procedimientos compartidos llamados métodos. Son funciones que determinan el comportamiento de los objetos. Un objeto tendrá un comportamiento u otro dependiendo de los métodos de que disponga. Los métodos se definen y declaran en las clases. Así, cualquier objeto de esa clase tendrá disponibles esos métodos y podrán invocarlos. Se pueden catalogar los métodos en:
- Métodos de consulta (getters): sirven para extraer información de los objetos
- Métodos Modificadores (setters): se usan para cambiar uno o varios de los atributos de los objetos
- Operaciones: comportamiento de los objetos
Los métodos de una clase se pueden identificar en fase de análisis a partir de verbos del análisis léxico. Pueden existir métodos de clase (estáticos) y métodos de instancia, que solamente afectan a un objeto.
Paso de mensajes.
Los objetos se comunican e interaccionen entre sí por medio de mensajes. Si un objeto desea que otro objeto ejecute un método le envía un
mensaje que puede tener información adicional en forma de parámetros. La estructura de mensaje es siempre la misma: nombre del objeto receptor. nombre del método a ejecutar, y una lista de argumentos o parámetros del mensaje.
mensaje que puede tener información adicional en forma de parámetros. La estructura de mensaje es siempre la misma: nombre del objeto receptor. nombre del método a ejecutar, y una lista de argumentos o parámetros del mensaje.
Pudiera parecer que el envío de mensajes entre objetos es similar a la llamada a un procedimiento o función en la programación estructurada. En el enfoque tradicional el programa que llama controla en cierta medida la ejecución del que es llamado, mientras que en la orientación a objetos es el propio objeto receptor el responsable de la ejecución del método y depende de la definición de su clase para encontrar las acciones a ejecutar.
Herencia
La programación orientada a objetos introduce la posibilidad de extender clases, produciendo nuevas definiciones de clases que heredan todo el comportamiento y código de la clase extendida. La clase original se denomina clase padre, base o superclase. La nueva clase que se define como una extensión se denomina clase hija, derivada o subclase. La extensión de una clase se denomina herencia, porque la nueva clase hija hereda todos los métodos y atributos de la clase padre que se extiende.Cada subclase estaría formada por un grupo de objetos más especializados con características comunes que compartirían datos y operaciones.
La herencia es un mecanismo importante de extensión que permite la reutilización de código, pero tal extensión jamás provocará la modificación de la clase base. La relación de herencia es transitiva y define una jerarquía de herencia. Hablamos de herencia simple cuando un tipo derivado hereda de una sola clase, y herencia múltiple cuando un tipo hereda de varias clases. La herencia múltiple no está soportada por todos los lenguajes OOP (C++ sí la soporta, Java o C#, por ejemplo, no). La razón es que puede ser difícil tanto de implementar por el propio lenguaje como de utilizar por parte de los programadores. Asociado a la herencia múltiple está el problema de la //en.wikipedia.org/wiki/Diamond_problem herencia en diamante, cuando una clase hereda de dos clases, que a su vez tienen un ancestro común.
Polimorfismo.
Los objetos actúan en respuesta a los mensajes que reciben. El polimorfismo es la propiedad por la cuál un mismo mensaje puede originar conductas completamente diferentes al ser recibido por diferentes objetos. De un modo más preciso: dos instancias u objetos, pertenecientes a distintas clases, pueden responder a la llamada a métodos del mismo nombre, cada uno de ellos con distinto comportamiento encapsulado, pero que responden a una interfaz común (marcada a través del mecanismo de la herencia).
El polimorfismo está fomentado por el mecanismo de herencia. Las funciones de una clase padre o superclase pueden ser sustituidas en una subclase si se realiza una duplicación de su declaración en esta última clase hija. Es por ello que los objetos de las dos clases, tanto de la superclase como de la subclase, puedan reaccionar a los mismos mensajes, pero lo harán de diferentes maneras. El comportamiento del objeto viene determinado por la clase que se instancia, que es independiente de la referencia usada.
Hablamos de "polimorfismo de sobreescritura" cuando el polimorfismo se implementa a través del mecanismo de la herancia. Si tenemos una clase abstracta "Animal", con el método "habla()"; y creamos dos subclases "Gato" y "Perro" que sobreescriban este método, la llamada al método "habla()" se comporta de forma polimórfica para los clientes de estas clases (que de cualquier forma interaccionan con la clase "Animal"; desconociendo los detalles encapsulados de la implementación
Existe otro tipo de polimorfismo que es el "polimorfismo de sobrecarga". Esta característica no la contemplan todos los lenguajes de programación. Consiste en poder sobreescribir operadores o sintaxis de métodos del lenguaje.
Un último tipo de polimorfismo es el "Polimorfismo paramétrico", en el que el código puede ser escrito sin ningún tipo de mención a los tipos de datos con los que opera. El uso de punterios del lenguaje C constituye un ejemplo de polimorfismo paramétrico (cuando un método en vez de recibir un parámetro determinado recibe un puntero)
Encapsulación.
La encapsulación es el proceso de ocultar todos los detalles de un objeto que no contribuyen a sus características esenciales, es decir, separar el aspecto externo del objeto accesible por otros objetos, del aspecto interno del mismo que será inaccesible para los demás.
Es el término formal que se utiliza para expresar que los datos de un objeto solamente pueden ser manipulados vía los mensajes y métodos predefinidos. Dicho de otra forma, los datos relativos a algún objeto o concepto del mundo real están almacenados junto con el proceso que crea y manipula estos datos. La comunicación entre objetos se realiza exclusivamente por medio de mensajes explícitos, no pudiendo operar ninguna otra parte del programa orientado a objetos sobre los datos del objeto. De esta forma, quedan escondidos los detalles de implementación de un objeto que no contribuyen a definir sus características esenciales.
La encapsulación oculta los detalles de implementación de un objeto que no contribuyen a definir sus características esenciales.
Una de las principales ventajas de la encapsulación es que evita la corrupción de los datos. Si un mismo dato es accesible desde numerosos módulos es mucho más probable que alguno de ellos pueda alterarlo inadecuadamente. El encapsulado preserva a los datos de un uso arbitrario y no pretendido. Existen algunos lenguajes que permiten violar, parcialmente, el principio de encapsulación, permitiendo que
algunos objetos o módulos puedan acceder a los datos de otro objeto al margen del interfaz de mensaje definido sobre él. Esto se consigue con las propiedades de ámbito público o "friendly" (propiedades visibles para objetos del mismo paquete).
algunos objetos o módulos puedan acceder a los datos de otro objeto al margen del interfaz de mensaje definido sobre él. Esto se consigue con las propiedades de ámbito público o "friendly" (propiedades visibles para objetos del mismo paquete).
Asociado al concepto de encapsulación está el de ocultación. El concepto de ocultación de la información se refiere a que cada componente sabe lo mínimo posible de los otros y proporciona la mínima información posible sobre sí mismo.
Bajo acoplamiento y alta cohesión.
Las aplicaciones de orientación a objetos tienen un bajo nivel de acoplamiento, debido a la propiedad de encapsulación que tiene los objetos. Ello hace que las modificaciones de un objeto no afecten a los demás. Además, los objetos presentan una alta cohesión, justamente porque integran una colección de datos muy relacionados y los procesos susceptibles de acometerse sobre ellos.
En los temas sobre diseño estructurado y diseño orientado a objetos se produndiza más en estos conceptos.
Referencias bibliográficas.
- Python_(programming_language)
- Ingeniería del Software. Un Enfoque Práctico. Ed. McGraw Hill. 4ª y 6º Ed. 1997. Pressman, Roger. S.
- Programación orientada a objetos. 2ª Ed. Prentice-Hall, 1998. Joyanes, L.
Si te ha gustado esta entrada, suscríbete para recibir las próximas entradas por correo electrónico. Por favor, apoya este blog.
propiedad de encapsulación que tiene los objetos. Ello hace que las modificaciones de un objeto no afecten a los demás. Además, los objetos presentan una alta cohesión, justamente porque integran una colección de datos muy relacionados y los procesos susceptibles de acometerse sobre ellos. coaching-mastery.com/juegos-2d-para-pc/
ResponderEliminar