epespad

Programacion orientada a objetos [parte 2]






PARADIGMA ORIENTADO A OBJETOS

Programación II









David Funes


ABSTRACCION

Objetivos: Explicar qué es el concepto de abstracción en computación y como ayuda a resolver problemas complejos.
Temas:

El concepto de abstracción.
Problemas tipo.

El concepto de abstracción

La complejidad de un procedimiento es aproximadamente proporcional a su tamaño (número de líneas) multiplicado por el número de variables que se declaran en él. Esto significa que la complejidad de un procedimiento puede crecer rápidamente hasta niveles inmanejables por una persona. A modo de ejemplo, una solución para la tarea 4 de este curso consiste en un procedimiento de unas 50 líneas con 14 variables. Un procedimiento con el doble de la líneas y el doble de variables sería 4 veces más complejo.
De hecho, pocas personas son capaces de manejar la complejidad de un procedimiento de más de 200 líneas. Esto se traduce en que a pesar de lograr escribir el procedimiento, nunca se llega a hacerlo a andar correctamente (no se logra eliminar todos los errores de programación) ¿Significa esto que existe un cota para el tamaño de los programas que puede escribir una persona? Afortunadamente no. En la práctica esta cota existe para el tamaño de los procedimientos, pero no los programas. Se pueden escribir programas de millones de líneas, en base a funciones o procedimientos de 20 a 50 líneas.

La clave para poder escribir programas complejos es descomponer el problema a resolver en subproblemas más pequeños y luego resolverlos en forma independiente. Esto significa que hay que elegir uno de estos subproblemas y programar una solución de él abstrayéndose completamente de los detalles sobre como programar una solución para el resto de los subproblemas. Luego, se elige otro subproblema y se resuelve abstrayéndose de los detalles de implementación del primer subproblema u otros subproblemas aún no resueltos. Así, hasta resolver todos los subproblemas.

Diseño:

El proceso de descomposición de un problema en partes más simples se denomina diseño. El diseño comienza con un problema que puede ser complejo de resolver. Su resultado es un conjunto de subproblemas que constituyen las partes del problema. Estas partes se pueden resolver independientemente unas de otras.

Abstracción:

La abstracción es una estrategia de resolución de problemas en la cual el programador se concentra en resolver una parte del problema ignorando completamente los detalles sobre cómo se resuelven el resto de las partes. En este proceso de abstracción se considera que el resto de las partes ya han sido resueltas y por lo tanto pueden servir de apoyo para resolver la parte que recibe la atención.

Una vez hecha la descomposición en subproblemas, resulta natural resolver cada uno de ellos en una función o un procedimiento. Como los subproblemas son más sencillos que el problema original, su complejidad (dada por su tamaño y número de variables) será inferior a la complejidad del mismo problema resuelto por medio de un solo procedimiento.

La abstracción es la estrategia de programación más importante en computación. Sin abstracción las personas serían incapaces de abordar los problemas complejos. La pericia de un programador no está en ser veloz para escribir líneas de programa, si no que en saber descubrir, en el proceso de diseño, cuáles son las partes del problema, y luego resolver cada una de ellas abstrayéndose de las otras.

Un ejemplo de abstracción es el hecho de que uno pueda conducir un automóvil sin ser un mecánico (lo cual probablemente no era cierto con los primeros vehículos). Al conducir, uno se abstrae de cómo funciona la combustión en el motor. Sólo se requiere saber cómo se maneja el volante y los pedales, y cuales son las reglas del tránsito.

Ejemplo:

Supongamos que se necesita un programa que calcule los impuestos que deben pagar un grupo de personas. El impuesto varía de acuerdo al sueldo que percibe cada persona. Para ello se establecen varios tramos de sueldo. Estos tramos se encuentran en un archivo de nombre impuestos.txt. Su contenido podría ser por ejemplo:


10000 30
2000 20
500 10
0 0
El primer campo (columnas 0 a 9 ) es el monto de sueldo y el segundo (columnas 10 a 15) es la tasa de impuestos que debe pagar una persona por cada peso que perciba por sobre ese sueldo. Es así como una persona que gane 5000, percibe 3000 pesos por sobre el tramo 2000 y por lo tanto debe pagar el 20% de 3000. Además percibe 1500 pesos por sobre los 500 por lo que debe pagar el 10% de 1500. Por lo tanto esa persona debe pagar 750 pesos en impuestos.
Solución:

Este problema lo podemos resolver con un solo procedimiento, pero el programa resultante será menos complejo si descomponemos el problema en 2 subproblemas y luego resolvemos ambos subproblemas en forma independiente.

1er. problema: dados un arreglo con los límites de sueldo de cada tramo, un arreglo que contiene las tasas de impuesto que corresponde pagar en cada tramo y un sueldo, calcular el monto a pagar en impuestos por ese sueldo.

2do. problema: construir dos arreglos con los valores contenidos en el archivo impuestos.txt y luego establecer un diálogo para pedir sucesivamente varios sueldos y responder cuanto se debe pagar por cada uno de ellos, suponiendo que existe una función que calcula el impuesto a partir de los dos arreglos y el sueldo.

La idea es resolver cada uno de estos problemas en una función o un procedimiento independiente. El primero se puede resolver definiendo una función impuestos que reciba como parámetros los dos arreglos y el sueldo, y entregue como resultado el impuesto a pagar. Esta función tendrá la siguiente forma:


int impuestos(Array tramos, Array tasas, int sueldo) {
... calcular el impuesto en función de tramos, tasas y sueldo ...
return el impuesto calculado
}
Al resolver este subproblema, el programador debe abstraerse de que los tramos están en un archivo y que hay que realizar un diálogo con el usuario, porque estos detalles son parte del 2do problema. Tampoco debe preocuparse de cómo se construyen los arreglos. Ellos son parámetros del problema.
El segundo problema es resuelto en el procedimiento run(). En él se lee el archivo, se construyen los arreglos, se dialoga con el usuario y se invoca la función impuestos. La forma del código será entonces:


void run() {
- tramos= new Array(...);
- tasas= new Array(...);
- Inicializar ambos arreglos con los valores leídos del archivo
impuestos.txt
- Hacer un ciclo de diálogo con el usuario
- Por cada sueldo ingresado por el usuario
se invoca la función impuestos:
monto= impuestos(tramos, tasas, sueldo);
- desplegar monto.
}
Al resolver este subproblema, el programador se abstrae acerca del detalle de cómo se calcula el impuesto, porque esto es parte del primer subproblema.
El beneficio que trae la abstracción consiste en que resolver estos dos subproblemas en forma independiente resulta menos complejo que resolver el problema original considerando todos sus detalles en forma simultánea.

Conclusión: resulta conveniente descomponer un problema en subproblemas simples de resolver, porque la suma de las complejidades de las partes es menor que la complejidad del todo. Mientras más grande es el programa final, mayor será la diferencia en complejidad.

En el ejemplo anterior, cada uno de los subproblemas se resolvió por medio de una función o procedimiento. Esta es la forma de resolver los problemas en los lenguajes tradicionales (Pascal, C, Visual Basic y otros). En los lenguajes orientados a objetos (Java, C++, Smalltalk y otros) también existe la posibilidad de resolver cada uno de los subproblemas en clases.

Problemas tipo

Aprender a descomponer convenientemente un problema en sus partes toma bastante tiempo. No elegir adecuadamente las partes puede originar un problema que es aún más difícil que el original. Por esta razón, en este curso en la mayoría de los problemas la descomposición de los problemas está dada en el enunciado.
Los típicos problemas que Ud. verá en los controles, tareas y ejercicios serán:

Problemas de uso de funciones: Se enuncia cuál es el problema que resuelven una o más funciones (o procedimientos). Se indica el tipo de los parámetros que reciben y el del valor retornado. Se presenta además un problema complejo no resuelto. Ud. debe resolver el problema complejo usando (invocando) las funciones dadas, abstrayéndose de cómo están implementadas.
Ejemplo: a lo largo del curso se han usado funciones y procedimientos como sqrt, print, max, sin saber cómo están implementadas.

Problemas de definición de funciones: Se enuncia cuál es el problema que debe resolver una función (o procedimiento). Ud. debe definirla, es decir implementar esta función. Debe tener especial cuidado en identificar cuál es el número y tipo de los parámetros que recibe la función, y el tipo del resultado (void si es un procedimiento).
Ejemplo: escribir la función que calcula el máximo valor en un arreglo.

Problemas completos: Una combinación de los dos problemas anteriores. Se plantea un problema complejo. Se pide que Ud. defina una función que resuelve un subproblema. Ud. la implementa abstrayéndose del problema más complejo. Luego resuelve el problema complejo en términos de esta función.
Ejemplo: desplegar los números primos entre 200 y 300.

Problemas de uso de objetos: Se enuncia el problema que resuelve una clase de objetos (como Fecha). Ud. debe resolver un problema complejo usando objetos de esa clase, abstrayéndose de cómo está implementada la clase.

CLASE

En la programación orientada a objetos, una clase es una construcción que se utiliza como un modelo (o plantilla) para crear objetos de ese tipo. El modelo describe el estado y el comportamiento que todos los objetos de la clase comparten. Un objeto de una determinada clase se denomina una instancia de la clase. La clase que contiene (y se utilizó para crear) esa instancia se puede considerar como del tipo de ese objeto, por ejemplo, una instancia del objeto de la clase "Personas" sería del tipo "Personas".
Una clase por lo general representa un sustantivo, como una persona, lugar o (posiblemente bastante abstracta) cosa - es el modelo de un concepto dentro de un programa de computadora. Fundamentalmente, encapsula el estado y el comportamiento del concepto que representa. Encapsula el estado a través de marcadores de datos llamados atributos (o variables miembro o variables de instancia), encapsula el comportamiento a través de secciones de código reutilizables llamados métodos.
Más técnicamente, una clase es un conjunto coherente que consiste en un tipo particular de metadatos. Una clase tiene tanto una interfaz y una estructura. La interfaz describe cómo interactuar con la clase y sus instancias con métodos, mientras que la estructura describe cómo los datos se dividen en atributos dentro de una instancia. Una clase también puede tener una representación (metaobjeto) en tiempo de ejecución, que proporciona apoyo en tiempo de ejecución para la manipulación de los metadatos relacionados con la clase. En el diseño orientado a objetos, una clase es el tipo más específico de un objeto en relación con una capa específica.
Los lenguajes de programación que soportan clases difieren sutilmente en su soporte para diversas características relacionadas con clases. La mayoría soportan diversas formas de herencia. Muchos lenguajes también soportan características para proporcionar encapsulación, como especificadores de acceso.
Una clase es un contenedor de uno o más datos (variables o propiedades miembro) junto a las operaciones de manipulación de dichos datos (métodos). Las clases pueden definirse como estructuras (struct), uniones (union) o clases (class) pudiendo existir diferencias entre cada una de las definiciones según el lenguaje. Además las clases son agrupaciones de objetos que describen su comportamiento.
La sintaxis típica de una clase es:
class Nombre {
// Variables miembro (habitualmente privadas)
miembro_1; //lista de miembros
miembro_2;
miembro_3;

// Funciones o métodos (habitualmente públicas)
funcion_miembro_1( ); // funciones miembro conocidas
funcion_miembro_2 ( ); // funciones como métodos

// Propiedades (habitualmente públicas)
propiedad_1;
propiedad_2;
propiedad_3;
propiedad_4;
}
Programacion orientada a objetos [parte 2]
Las clases habitualmente se denotan con nombres abstractos como Animal, Factura... aunque también pueden representar procesos o acciones como DarAlta

Variables miembro
Las propiedades o atributos son características de los objetos. Cuando definimos una propiedad normalmente especificamos su nombre y su tipo. Nos podemos hacer a la idea de que las variables son algo así como el almacén de los datos de estado relacionados con los objetos.
Habitualmente, las variables miembro son privadas al objeto (siguiendo las directrices de diseño del Principio de ocultación) y su acceso se realiza mediante propiedades o métodos que realizan comprobaciones adicionales.
Suelen denominarse con nombres.
Métodos en las clases
Implementan la funcionalidad asociada al objeto. Los métodos son el equivalente a las funciones en los lenguajes estructurados. Se diferencian de ellos en que es posible acceder a las variables de la clase de forma implícita.
Cuando se desea realizar una acción sobre un objeto, se dice que se le manda un mensaje invocando a un método que realizará la acción.
Habitualmente, los métodos suelen ser verbos.
Propiedades
Las propiedades son un tipo especial de métodos. Debido a que suele ser común que las variables miembro sean privadas para controlar el acceso y mantener la coherencia, surge la necesidad de permitir consultar o modificar su valor mediante pares de métodos: GetVariable y SetVariable.
Los lenguajes orientados a objetos más modernos (por ejemplo Java, C#) añaden la construcción de propiedad que es una sintaxis simplificada para dichos métodos:
tipo Propiedad {
get {
}
set {
}
}
Las propiedades se denominan con nombres como las variables.

OBJETO

En el paradigma de programación orientada a objetos (POO, o bien OOP en inglés), un objeto se define como la unidad que en tiempo de ejecución realiza las tareas de un programa. También a un nivel más básico se define como la instancia de una clase.
Estos objetos interactúan unos con otros, en contraposición a la visión tradicional en la cual un programa es una colección de subrutinas (funciones o procedimientos), o simplemente una lista de instrucciones para el computador. Cada objeto es capaz de recibir mensajes, procesar datos y enviar mensajes a otros objetos de manera similar a un servicio.
En el mundo de la programación orientada a objetos (POO), un objeto es el resultado de la instanciación de una clase. Una clase es el anteproyecto que ofrece la funcionalidad en ella definida, pero ésta queda implementada sólo al crear una instancia de la clase, en la forma de un objeto. Por ejemplo: dado un plano para construir sillas (una clase de nombre clase_silla), entonces una silla concreta, en la que podemos sentarnos, construida a partir de este plano, sería un objeto de clase_silla. Es posible crear (construir) múltiples objetos (sillas) utilizando la definición de la clase (plano) anterior. Los conceptos de clase y objetos son análogos a los de tipo de datos y variable, es decir, definida una clase podemos crear objetos de esa clase, igual que disponiendo de un determinado tipo de dato (por ejemplo el tipo entero), podemos definir variables de dicho tipo:
int a,b;
( 'int' es un tipo de dato y 'a' y 'b' son variables de tipo entero con las que podemos operar)
Para utilizar la funcionalidad definida en una clase en particular (salvo en las clases abstractas), primeramente es necesario crear un objeto de esa clase. De la misma manera para una persona que desea sentarse, las especificaciones para construir una silla serán de poca utilidad; lo que se necesita es una silla real construida a partir de esas especificaciones. Siguiendo con la analogía anterior, también se puede decir que para hacer operaciones aritméticas, de nada sirve por sí solo el tipo entero (int); para ello necesitamos variables (o constantes) con las que operar.

Definición de Objeto

En filosofía un objeto es aquello que puede ser observado, estudiado y aprendido, en contraposición a la representación abstracta de ese objeto que se crea en la mente a través del proceso de generalización. Un objeto en POO representa alguna entidad de la vida real, es decir, alguno de los objetos que pertenecen al negocio con que estamos trabajando o al problema con el que nos estamos enfrentando, y con los que podemos interactuar. A través del estudio de ellos se adquiere el conocimiento necesario para, mediante la abstracción y la generalización, agruparlos según sus características en conjuntos, estos conjuntos determinan las clases de objetos con las que estamos trabajando. Primero existen los objetos, luego aparecen las clases en función de la solución que estemos buscando. Ésta es la forma más común de adquirir conocimiento aunque no es la única. En ocasiones cuando el observador es un experto del negocio (o del problema), el proceso puede ser a la inversa y comenzar el análisis en una base teórica abstracta, sustentada por el conocimiento previo que da lugar primeramente a clases de objetos que satisfagan las necesidades de la solución.
Estos conceptos son parte de la base teórica de la idea de objeto y clase utilizados en la POO. Los objetos tienen características fundamentales que nos permiten conocerlos mediante la observación, identificación y el estudio posterior de su comportamiento; estas características son:
Identidad
Comportamiento
Estado

En las ramas de las ciencias de la computación más estrictamente matemáticas, el término objeto es usado en sentido puramente matemático para referirse a cualquier "cosa". Esta interpretación resulta útil para discutir sobre teorías abstractas, pero no es suficientemente concreta para servir como definición de un tipo primitivo en discusiones de ramas más específicas como en la programación, que está más cerca de cálculos reales y el procesamiento de información.
Identidad
La identidad es la propiedad que permite a un objeto diferenciarse de otros. Generalmente esta propiedad es tal, que da nombre al objeto. Tomemos por ejemplo el "verde" como un objeto concreto de una clase color; la propiedad que da identidad única a este objeto es precisamente su "color" verde. Tanto es así que para nosotros no tiene sentido usar otro nombre para el objeto que no sea el valor de la propiedad que lo identifica.
En programación la identidad de los objetos sirve para comparar si dos objetos son iguales o no. No es raro encontrar que en muchos lenguajes de programación la identidad de un objeto esté determinada por la dirección de memoria de la computadora en la que se encuentra el objeto, pero este comportamiento puede ser variado redefiniendo la identidad del objeto a otra propiedad.

Comportamiento
El comportamiento de un objeto está directamente relacionado con su funcionalidad y determina las operaciones que este puede realizar o a las que puede responder ante mensajes enviados por otros objetos. La funcionalidad de un objeto está determinada, primariamente, por su responsabilidad. Una de las ventajas fundamentales de la POO es la reusabilidad del código; un objeto es más fácil de reutilizarse en tanto su responsabilidad sea mejor definida y más concreta.
Una tarea fundamental a la hora de diseñar una aplicación informática es definir el comportamiento que tendrán los objetos de las clases involucradas en la aplicación, asociando la funcionalidad requerida por la aplicación a las clases adecuadas.

Estado
El estado de un objeto se refiere al conjunto de los valores de sus atributos en un instante de tiempo dado. El comportamiento de un objeto puede modificar el estado de este. Cuando una operación de un objeto modifica su estado se dice que esta tiene "efecto colateral".
Esto tiene especial importancia en aplicaciones que crean varios hilos de ejecución. Si un objeto es compartido por varios hilos y en el transcurso de sus operaciones estas modifican el estado del objeto, es posible que se deriven errores del hecho de que alguno de los hilos asuma que el estado del objeto no cambiará.

Objetos en la programación orientada a objetos

En programación orientada a objetos (POO), una instancia de programa (por ejemplo un programa ejecutándose en una computadora) es tratado como un conjunto dinámico de objetos interactuando entre sí. Los objetos en la POO extienden la noción más general descrita en secciones anteriores para modelar un tipo muy específico que está definido fundamentalmente por:
atributos que representan los datos asociados al objeto, o lo que es lo mismo sus propiedades o características. Los atributos y sus valores en un momento dado, determinan el estado de un objeto.
métodos que acceden a los atributos de una manera predefinida e implementan el comportamiento del objeto.
Los atributos y métodos de un objetos están definidos por su clase, aunque (en un lenguaje dinámico como Python o Ruby) una instancia puede poseer atributos que no fueron definidos en su clase. Algo similar ocurre con los métodos, una instancia puede contener métodos que no estén definidos en su clase de la misma manera una clase puede declarar ciertos métodos como "métodos de clase", y estos (en dependencia del lenguaje) podrán estar o no presentes en la instancia.
En el caso de la mayoría de los objetos, los atributos solo pueden ser accedidos a través de los métodos, de esta manera es más fácil garantizar que los datos permanecerán siempre en un estado bien definido (Invariante de Clase).
En un lenguaje en el que cada objeto es creado a partir de una clase, un objeto es llamado una instancia de esa clase. Cada objeto pertenece a un tipo y dos objetos que pertenezcan a la misma clase tendrán el mismo tipo de dato. Crear una instancia de una clase es entonces referido como instanciar la clase.
En casi todos los lenguajes de programación orientados a objeto, el operador "punto" (.) es usado para referirse o "llamar" a un método particular de un objeto. Un ejemplo de lenguaje que no siempre usa este operador el C++ ya que para referirse a los métodos de un objeto a través de un puntero al objeto se utiliza el operador (->.
Consideremos como ejemplo una clase aritmética llamada Aritmética. Esta clase contiene métodos como "sumar", "restar", "multiplicar", "dividir", etc. que calculan el resultado de realizar estas operaciones sobre dos números.
Un objeto de esta clase puede ser utilizado para calcular el producto de dos números, pero primeramente sería necesario definir dicha clase y crear un objeto. En las secciones a continuación se muestra como hacer esto utilizando dos lenguajes de programación: C++ y Python.

Declaración de una clase
Esta clase podría ser definida de la siguiente manera en C++:
class Aritmetica
{
public:
inline int sumar (int a, int b) const
{
return a + b;
}

inline int restar (int a, int b) const
{
return a - b;
}

inline float multiplicar (int a, int b) const
{
return a * b;
}

inline float dividir (int a, int b) const
{
return a / b;
}
};

o como sigue en Python:
class Aritmetica:
def sumar(self, a, b):
return a + b

def restar(self, a, b):
return a - b

def multiplicar(self, a, b):
return a * b

def dividir(self, a, b):
return a / b


Instanciación de una clase en un objeto
Para crear un objeto de tipo 'Aritmetica' (instanciar a Aritmetica) en C++ se haría de la siguiente forma:
Aritmetica calculador = Aritmetica();
#Otra manera usando punteros
Aritmetica* calculador1 = new Aritmetica();
la misma operación usando python sería así:
calculador = Aritmetica()

Operando con un objeto
Una vez que tenemos un objeto de 'Aritmetica', podemos usarlo para realizar cálculos sobre dos números. En C++ contamos con dos objetos de ejemplo: "calculador" y "calculador1", en esta última variable en realidad hemos almacenado la dirección de memoria del objeto creado. En este lenguaje esto sienta diferencias a la hora de utilizar el objeto.
Para calcular la suma entre 78 y 69 usando un objeto "calculador" necesitaríamos un código como el siguiente en C++:
int resultado = 0;
resultado = calculador.sumar(78, 69);

#Otra manera usando punteros
resultado = calculador1->sumar(78, 69);
ahora usando Python para sumar dos números con el objeto calculador:
resultado = calculador.sumar(78, 69)

Otro ejemplo del mundo real de un objeto podría ser "mi perro", el cual es una instancia de un tipo (una clase) llamada "perro", la que es una subclase de la clase "animal". En el caso de un objeto polimórfico, algunos detalles de su tipo pueden ser ignorados, por ejemplo el objeto "mi perro" puede ser usado en un método que espera recibir un "animal". También podría usarse un objeto "gato", puesto que esta también pertenece a la clase "animal". Pero mientras es accedido como un "animal", algunos atributos de un "perro" o un "gato" permanecerán no disponibles, como la "cola", porque no todos los animales tienen colas.

Relaciones entre objetos

Como ya se ha dicho antes, un sistema orientado a objetos está caracterizado por objetos que interactúan entre si. Estas interacciones supone ciertos tipos de relaciones entre los objetos del sistema. La semántica que expresa un objeto en el sistema está determinado en primer lugar, por las relaciones que éste establece con otros objetos o conjunto de objetos. Tomemos como ejemplo un objeto fecha, del que sin establecer ningún tipo de relación, podría decirse que significa un día del año particular. Pero si relacionamos ese objeto fecha con un objeto Persona de manera que represente la fecha en que esa persona nació, en ese contexto dado, el mismo objeto fecha adoptaría un significado diferente, el de un cumpleaños, aunque sigue siendo una fecha, ahora tiene otra idea asociada. Las relaciones entre objetos no solo están definidas por los objetos que participan y la circunstancia que los relaciona, sino también por la cantidad de objetos (cardinalidad de la relación) y la dirección de la misma. Una relación puede tener cardinalidad:
uno a uno, ejemplo: un auto tiene un motor
uno a muchos, ejemplo: un auto tiene muchas ruedas
muchos a muchos, ejemplo: un auto se puede servir en muchas gasolineras y una gasolinera puede servir a muchos autos.
y direccionalidad:
unidireccional, ejemplo: un auto tiene cuatro ruedas.
bidireccional

ENCAPSULAMIENTO

Se denomina encapsulamiento al ocultamiento del estado, es decir, de los datos miembro, de un objeto de manera que sólo se puede cambiar mediante las operaciones definidas para ese objeto.
Cada objeto está aislado del exterior, es un módulo natural, y la aplicación entera se reduce a un agregado o rompecabezas de objetos. El aislamiento protege a los datos asociados a un objeto contra su modificación por quien no tenga derecho a acceder a ellos, eliminando efectos secundarios e interacciones.
De esta forma el usuario de la clase puede obviar la implementación de los métodos y propiedades para concentrarse sólo en cómo usarlos. Por otro lado se evita que el usuario pueda cambiar su estado de maneras imprevistas e incontroladas.

Se dice que es el empaquetado de métodos y atributos dentro de un objeto, mediante una interfaz grafica. La clave está precisamente en el envoltorio del Como se puede observar de los diagramas, las variables del objeto se localizan en el centro o núcleo del objeto. Los métodos rodean y esconden el núcleo del objeto de otros objetos en el programa. Al empaquetamiento de las variables de un objeto con la protección de sus métodos se le llama encapsulamiento. Típicamente, el encapsulamiento es utilizado para esconder detalles de la puesta en práctica no importantes de otros objetos. Entonces, los detalles de la puesta en práctica pueden cambiar en cualquier tiempo sin afectar otras partes del programa.
El encapsulamiento de variables y métodos en un componente de software ordenado es, todavía, una simple idea poderosa que provee dos principales beneficios a los desarrolladores de software: El encapsulamiento consiste en unir en la Clase las características y comportamientos, esto es, las variables y métodos. Es tener todo esto en una sola entidad. En los lenguajes estructurados esto era imposible. Es evidente que el encapsulamiento se logra gracias a la abstracción y el ocultamiento que veremos a continuación. La utilidad del encapsulamiento va por la facilidad para manejar la complejidad, ya que tendremos a las Clases como cajas negras donde sólo se conoce el comportamiento pero no los detalles internos, y esto es conveniente porque lo que nos interesará será conocer qué hace la Clase pero no será necesario saber cómo lo hace.
La encapsulación da lugar a que las clases se dividan en dos partes:
Interface: captura la visión externa de una clase, abarcando la abstracción del comportamiento común a los ejemplos de esa clase.
Implementación: comprende la representación de la abstracción, así como los mecanismos que conducen al comportamiento deseado.

Formas de Encapsular

1.Abierto : Hace que el miembro de la clase pueda ser accedido desde el exterior de la Clase y 2.cualquier parte del programa.
3.Protegido : Solo es accesible desde la Clase y las clases que heredan (a cualquier nivel).
4.Cerrado : Solo es accesible desde la Clases.
En el encapsulamiento hay analizadores que pueden ser semánticos y sintácticos.

-ejemplos:

Objetos: encapsulamiento

§ Porción visible: interfaz (protocolo)
– Contrato público de comportamiento
– Descripción de operaciones: información de entrada y de salida

§ Porción oculta: implementación
– Estructura de datos para almacenar la información
– Código que se ejecuta para realizar las operaciones

Encapsulamiento de los atributos de una clase

Antes de nada, debe quedar claro que el encapsulamiento, igual que cualquier buen hábito de programación (como no poner goto, comentar, etc) es útil para código que más adelante se puede querer reutilizar o modificar, por otras personas o por uno mismo. Si yo hago un programa de marcianos y nunca jamas pienso volver a tocarlo, da igual que lo haga con gotos y sin comentar mientras me entere yo mismo mientras lo estoy haciendo y funcione. Pagaré este "pecado" si dentro de dos meses se me ocurre mejorarlo o quiero reaprovechar algo de su código para otro programa.

Comento esto porque el encapsulamiento, llevado a su extremo, como es el caso del punto final de interfaces, hace la programación un poco más complicada (hay que hacer más clases). Este esfuerzo sólo se ve recompensado si el código es muy grande (evitando recompilados innecesarios) o se va a reutilizar en un futuro (podremos extraer clases con menos dependencias de otras clases). Dicho esto, vamos al tema.

Cualquier curso de orientación a objetos nos dice que es mejor poner los atributos de una clase protegidos o privados (nunca públicos) y acceder a ellos a través de métodos públicos que pongamos en la clase. Veamos el motivo. Supongamos, por ejemplo, que nos piden un programa que permita llevar una lista de gente con sus fechas de nacimiento. Entre otras cosas, decidimos hacernos nuestra clase Fecha con varios métodos maravillosos de la siguiente manera.

class Fecha
{
public:
int anho; // El anho con cuatro cifras, ej. 2004
int mes; // El mes, de 1 a 12
int dia; // El dia, de 1 a 31
void metodoMaravilloso1();
void metodoMaravilloso2();

};
Ya hemos hecho la clase. Ahora hacemos el resto del código y en unos varios miles de líneas de código usamos directamente cosas como esta.

Fecha unaFecha;
unaFecha.anho = 2004;
unaFecha.mes = 1;
unaFecha.dia = 25;

Finalmente acabamos nuestro programa y todo funciona de maravilla. Unos días después nos dicen que el programa va a guardar tropecientas mil personas y que ocupan mucho los ficheros, que a ver si podemos hacer algo para remediarlo. ¡Vaya!, almacenamos una fecha con tres enteros. Si usamos el formato de la mayoría de los ordenadores, en el que la fecha es el número de segundos transcurridos desde el 1 de Enero de 1970 (lo que nos devuelve la función time()), basta con un entero.

Total, que manos a la obra, cambiamos nuestra clase para que tenga lo siguiente:

class Fecha
{
public:
/* Comentado por ineficiente
int anho;
int mes;
int dia; */
long numeroSegundos;

void metodoMaravilloso1();
void metodoMaravilloso2();
};

Ya está hecho lo fácil. Ahora sólo hay que ir por las tropecientas mil líneas de código cambiando nuestras asignaciones y lecturas a los tres enteros anteriores por el nuevo long.

Hubiera sido mucho mejor si hubieramos hecho estos tres enteros protegidos y unos métodos para acceder a ellos. Algo como esto

class Fecha
{
public:
void tomaFecha (int anho, int mes, int dia);
int dameAnho ();
int dameMes ();
int dameDia ();
void metodoMaravilloso1();
void metodoMaravilloso2();
protected:
int anho; // El anho con cuatro cifras, ej. 2004
int mes; // El mes, de 1 a 12
int dia; // El dia, de 1 a 31
};

Si ahora tenemos que hacer el mismo cambio, basta con cambiar los atributos protegidos. Los métodos tomaXX) y dameXX) se mantienen en cuanto a parámetros y valor devuelto, pero se modifica su código interno para que conviertan el año,mes y dia en un long de segundos y al revés. El resto del código no hay que tocarlo en absoluto.

Es incluso mejor hacer los atributos privados que protegidos. Haciéndolos protegidos, las clases hijas (las que heredan de Fecha) pueden acceder directamente a estos atributos. Cuando hagamos el cambio por un long, debemos cambiar también el código de las clases hijas. Si los atributos son privados y obligamos a las clases hijas a acceder a ellos a través de métodos, tampoco tendremos que cambiar el código de estas clases hijas.

El acceso a través de métodos es menos eficiente que hacerlo directamente, así que aunque siguiendo el principio de ocultación es mejor hacer atributos privados, por eficiencia en algunos casos quizás sea mejor hacerlos protegidos (o incluso públicos) a riesgo de tener que cambiar más líneas de código en caso de cambio.

POLIMORFISMO

Introducción
Polimorfismo es la capacidad de que diferentes objetos de una misma jerarquía tengan
comportamientos

distintos aunque reciban el mismo mensaje

La idea detrás del polimorfismo es poder diseñar nuestro código teniendo en cuenta que
todos las clases que derivan de una clase base heredan los métodos de la clase base.
Consideremos el siguiente ejemplo

, que define una clase abstracta Animal. Animal tiene
un método habla() que es reemplazado (override) en las subclases Perro y Gato.
abstract public class Animal {
private String nombre;
public Animal(String nombre) { this.nombre = nombre; }
public String getNombre() { return nombre; }
abstract public void habla();
{
public class Perro extends Animal {
public Perro(String nombre) { super(nombre); }
public void habla() {
System.out.println(getNombre()+": Warf, Guau, etc.";
{
{
public class Gato extends Animal {
public Gato(String nombre) { super(nombre); }
public void habla() {
System.out.println(getNombre()+": Miau, Pffff, etc.";
{
{
public class TestAnimal {
public static void main(String[] args) {
// un array de Animal
Animal[] animales = {
new Perro("Kujo",
new Perro("Fido",

new Gato("Garfield",
new Gato("Patán"
;{
for( Animal a : animales)
a.habla();
} // end main
} // end class

La salida:
Kujo: Warf, Guau, etc.
Fido: Warf, Guau, etc.
Garfield: Miau, Pffff, etc.
Patán: Miau, Pffff, etc.

El método main de la clase TestAnimal crea varios objetos Perro y Gato, y los guarda en
un arreglo
Luego el for itera los elementos del arreglo, y llama al método habla() de cada .
elemento.
En este caso, el for se ha diseñado teniendo en cuenta solamente que la clase base tiene un
método abstracto habla() y que, por tanto, todas las clases que deriven de ella tendrán un método
habla(). TestAnimal funciona con Perro y Gato, pero también funciona con cualquier objeto
que derive de Animal.
Cuando decimos a.habla(), estamos enviando el mismo mensaje a objetos distintos de la
misma jeraquía de clases (Perro, Gato: depende de que objeto sea a en esa iteración), y cada
objeto manifiesta su comportamiento de modo distinto (ladrando o mauyando, siguiendo la
analogía).


ESPERO QUE LES SIRVA Y CUALQUIER COSA QUE NECESITEN NO DUDEN EN PEDIRLA QUE HAGO UN NUEVO POST

gRACIAS

11 comentarios - Programacion orientada a objetos [parte 2]

KBjose
yo se programar con visual foxpro soy experto
he hecho 2 sistemas de informacion y voy por el tercero. buen post ademas::
massiel207
Amigo Excelente Explicacion !!!!muchas Gracias!!! +10
k0w0
+5 me ayudo mucho
rhada89
Te lo agradesco maestro seguro m sirve
alanharper
te hago una consulta sobre cardinalidad, si tengo por ejemplo esto


0...3 1
objeto1 ------------------------------------- objeto 2



como se interpreta? que el objeto2 puede tener de 0 a 3 objetos1 ? o al reves?
alanharper
me quedo mal, quise poner q la cardinalidad del objeto1 es 0...3 , y la del objeto 2 es 1