Logo Passei Direto

UPSAM _ Grado en Ingeniería a Informática _ Programación Orientada a Objetos y Aplicaciones _ Programaci

User badge image
Diego Pereira

en

Herramientas de estudio

Material

Esta es una vista previa del archivo. Inicie sesión para ver el archivo original

Programacion Orientada a Objetos 2015-2016 Campus/Tema_4_Herencia_y_polimorfirmo_v1_1.pdf
1 
 
Tema 4 
 
 
Herencia ,clases 
abstractas y 
polimorfismo. 
 
 
 
<<cambios pag 505 libro Meyer>< 
 
 
 
 
 
 
UPSAM 2010-2011 
 
 
 
2 
 
 
Índice de contenidos 
1. Herencia, clases abstractas y polimorfismo. ......................................................................... 3 
1.1. Generalización ............................................................................................................... 3 
2. Herencia ................................................................................................................................ 6 
2.1. Reescritura de métodos ................................................................................................. 6 
2.2. Clases abstractas ............................................................................................................ 7 
3. Polimorfismo ......................................................................................................................... 9 
3.1. Ejemplo de polimorfismo ............................................................................................ 10 
4. Tipos de herencia ................................................................................................................ 11 
5. Ligadura .............................................................................................................................. 12 
5.1. Ventajas de la ligadura dinámica................................................................................. 13 
5.2. Polimorfismo con ligadura dinámica .......................................................................... 14 
 
 
3 
 
 
 
1. Herencia, clases abstractas y polimorfismo. 
 
1.1. Generalización 
 
Según la DRAE el significado de generalizar es el siguiente: 
Abstraer lo que es común y esencial a muchas cosas, para formar un concepto 
general que las comprenda todas. 
 
El proceso de generalizar es fundamental en ciencia, constituye la base de la creación de 
reglas Universales. 
La generalización se constituye así como el proceso de abstraer y representar lo común. 
En este proceso siempre diferencia dos roles: lo que es general , y lo que es específico y 
puede ser representado por lo general. Veremos más adelante como la generalización y 
especialización crea una nueva relación entre clases .Además la generalización es una 
relación mucho más fuerte que la asociación, de hecho, la generalización implica el 
nivel más alto de dependencia entre dos clases (y por consiguiente de acoplamiento). 
Generalización de clases 
Conceptualmente la generalización es una idea muy simple. Pensando en cosas 
generales encontramos conceptos como por ejemplo el de asignatura , árbol ,etc.., y en 
algo más concreto encontramos la asignatura de programación, el abeto, el pino ,etc. 
Son conceptos relacionados entre sí , pero a distinto nivel de detalle. Si pensemos en los 
razonamientos y propiedades aplicados al concepto general serian aplicables a los 
conceptos más específicos. 
En la Figura 1, tenemos una clase general denominada Forma , que como se puede 
comprobar es un concepto bastante general. A partir de este concepto derivamos otros 
más específicos, que son variantes de la idea general Forma. 
4 
 
Forma
Cuadrado Circulo Triangulo
Elemento más general
Elementos más especificos
“Es un tipo de..”
Subclase
Subtipo
Hijo
Descendiente
Superclase
Supertipo
Padre
Antecesor
Clase base
 
Figura 1 Generalización 
 
Existen dos procesos por los que podríamos llegar a esta jerarquía: a partir de un 
proceso de especialización o por medio de la generalización. En la especialización, 
identificaríamos primero el concepto general Forma y posteriormente veríamos la 
necesidad de buscar o definir conceptos más específicos. En la generalización, 
podríamos identificar una serie de clases como Cuadrado, Circulo y Triangulo y 
encontrar que comparten atributos y comportamiento, lo que daría lugar a una clase más 
general. 
De manera general, podemos definir la relación de generalización como un tipo de 
relación en la que una clase (clase general) generaliza el comportamiento y las 
propiedades de otras clases (subclases). De otra manera, podemos decir que las 
subclases especializan el comportamiento de la clase general. 
 
 
Clase general
Subclase 1 Subclase 2 Subclase N
 
 
Figura 2. Generalización/Especialización en notación UML 
Algunos ejemplo de relaciones Generalización/especialización. 
5 
 
Publicacion
Revista Libro Acta
 
Lista
Lista 
enlazada
Lista doblemente 
enlazada
Lista 
circular
 
 
6 
 
 
 
2. Herencia 
 
Cuando se crea una jerarquía como la de la Figura 1, implícitamente existe una 
relación de herencia entre los participantes, por que las subclases heredan todas las 
características de sus superclases. Para ser más específicos en relación con la 
programación orientada a objetos, las subclases heredan: 
 Atributos 
 Operaciones 
 Relaciones 
 Restricciones 
Las subclases también pueden añadir nuevas características o reescribir las 
operaciones de las superclases. 
 
2.1. Reescritura de métodos 
 
En el ejemplo mostrado en Figura 2 las subclases Cuadrado y Círculo heredan todos 
los atributos, operaciones y restricciones de la superclase Forma. Esto significa que 
aunque no podamos observar estas características en las subclases, estas están 
implícitas. A nivel conceptual podemos decir entonces que un Círculo y un Cuadrado 
son un tipo de Forma. 
Forma
origen:Punto
ancho:int
alto:int
dibujar(g:Grafico)
obtenerArea():int
Cuadrado Circulo
radio: int { ancho/2}
ancho=alto
 
Figura 3. Jerarquía y herencia 
En este ejemplo (Figura 3) la clase Forma define dos métodos denominados dibujar(…) 
y obtenerArea(). Estos métodos podrían no ser apropiados para la clase Círculo o 
Cuadrado. Es de esperar que se dibuje un cuadrado cuando se envié el mensaje 
7 
 
dibujar( ) a un objeto de la clase Cuadrado y un Circulo cuando se envíe este mismo 
mensaje a un objeto de la clase Círculo. Por tanto la operación dibujar() definida en la 
clase base y que ambas subclases han heredado no sirve, de hecho, esta operación 
podría no dibujar nada en absoluto ¿qué aspecto tiene una forma?. Los mismos 
argumentos son aplicables a la operación obtenerArea() . ¿Cómo podemos calcular el 
área de una forma no definida? 
Estos problemas indican claramente la necesidad de permitir que las subclases sean 
capaces de cambiar el comportamiento de superclase. La subclase Cuadrado y la 
subclase Circulo necesitan implementar sus propio comportamiento para las 
operaciones dibujar(…) y obtenerArea(…) y sobrescribir el comportamiento por defecto 
heredado de la clase base para proporcionar un comportamiento más especifico y 
apropiado. 
La Figura 4 muestra esta acción: las subclases Circulo y Cuadrado proporcionan sus 
propias operaciones para dibujar() y obtenerArea() . 
 
 
Forma
dibujar(g:Grafico)
obtenerArea():int
Cuadrado Circulo
dibujar(g:Grafico)
obtenerArea():int
dibujar(g:Grafico)
obtenerArea():int
 
Figura 4 Reescritura de métodos heredados 
Para reescribir una operación de la clase base, una subclase debe proporcionar una 
operación con exactamente el mismo prototipo de la operación de la clase base. 
 
2.2. Clases abstractas 
 
Las relaciones de generalización, introducen a nivel conceptual clases, que más que 
clases de implementación son elementos o conceptos de organización. En muchas 
generalizaciones, será necesario delegar la implementación de las operaciones a las 
subclases. En nuestro ejemplo la operación dibujar (…) de Forma es un buen ejemplo,
porque no sabríamos como pintar una Forma sin determinar. El concepto de dibujar una 
forma es demasiado abstracto para tener una implementación. 
8 
 
Se dice entonces que una clase tienen operaciones abstractas o un comportamiento 
abstracto. Podemos indicar esto haciendo que el método sea abstracto. 
Debido a que las clases generales con métodos abstractos no tienen un 
comportamiento definido(es decir no tienen una implementación para sus 
métodos), estas clases no pueden ser instanciadas. A este tipo de clases se las 
denomina clases abstractas. 
En la Figura 5, tenemos una clase abstracta llamada Forma, con dos métodos 
abstractos dibujar(…) y obtenerArea(). La implementación para estas operaciones las 
proporcionan tanto la subclase Circulo como la subclase Cuadrado. Aunque la clase 
abstracta Forma es incompleta, y no puede ser instanciada, sus subclases si son 
completas cuando son instanciadas. 
Existe una serie de ventajas en la utilización de clases abstractas: 
 Se pueden definir un conjunto de operaciones abstractas en la clase base clase 
abstracta de forma que deben ser implementadas por las subclases. Se puede ver 
esto como “un contrato” que todas las subclases deben cumplir (o 
implementar). 
 Se puede escribir partes de código que manipulen Formas y de acuerdo al 
principio de substitución esta parte de código se podrá usar con cualquier 
subclase. 
 
Forma
dibujar(g:Grafico)
obtenerArea():int
Cuadrado Circulo
dibujar(g:Grafico)
obtenerArea():int
dibujar(g:Grafico)
obtenerArea():int
Clase abstracta
Operaciones abstractas
Operaciones concretas
Clases concretas
 
 
Figura 5. Clases abstracta y clases concretas 
 
9 
 
 
 
3. Polimorfismo 
 
El termino polimorfismo deriva del griego ( poly=”muchos”; 
morphos=”forma”).Polimorfismo significa “varias formas”. En la POO una operación 
polimórfica es aquella que tiene varias implementaciones. 
Hemos visto en el ejemplo anterior dos operaciones polimórficas. Las operaciones 
abstractas dibujar(…) y obtenerArea(…) de la clase Forma tienen dos 
implementaciones diferentes, una implementación en la subclase Cuadrado y otra en la 
subclase Círculo. 
La Figura 6 ilustra perfectamente el polimorfismo 
Forma
dibujar(g:Grafico)
obtenerArea():int
Cuadrado Circulo
dibujar(g:Grafico)
obtenerArea():int
dibujar(g:Grafico)
obtenerArea():int
Clase abstracta
Operaciones polimórficas
implementaciones
Clases concretas
 
 
Figura 6. Operaciones polimórficas 
La subclase Círculo y Cuadrado heredan de la superclase Forma y proporcionan una 
implementación para las operaciones polimórficas dibujar(…) y obtenarArea(). Todas 
las subclases de Forma deben proporcionar una implementación concreta para las 
operaciones dibujar(…) y obtenarArea(), esto significa que desde el punto de vista de 
estas operaciones se puede tratar todas las subclases de Forma de la misma 
manera. Un conjunto de operaciones abstractas es por consiguiente una forma de 
definir un conjunto de operaciones que todas las subclases deben implementar. Esto se 
conoce como contrato. 
Claramente la implementación de dibujar() y obtenerArea() diferirán para la subclase 
Cuadrado y la subclase Círculo. Así por ejemplo el método obtenerArea() de Circulo 
devolverá el resultado de la operación π*r*r mientras que la subclase Cuadrado 
devolverá el resultado de la operación alto*ancho. Esta es la esencia del polimorfismo, 
objetos de diferentes clases tienen operaciones con el mismo prototipo (igual 
signatura) pero diferentes implementaciones. 
10 
 
Encapsulación, herencia y polimorfismo son los “tres pilares” de la orientación a 
objetos. El polimorfismo nos permite diseñar sistemas más simples, que se adaptan 
mejor a los cambios por que permite tratar objetos diferentes de la misma forma. 
De hecho lo que hace al polimorfismo un aspecto esencial de la orientación a objetos 
es que permite enviar el mismo mensaje a objetos de diferentes clases. 
 
3.1. Ejemplo de polimorfismo 
 
Vamos a ver un ejemplo del uso del polimorfismo. Suponemos que tenemos una clase 
VentanaGráfica que mantiene y utiliza una colección de Formas. El modelo de clases 
es el mostrado en la Figura 7. 
 
Forma
dibujar(g:Grafico)
obtenerArea():int
Cuadrado Circulo
dibujar(g:Grafico)
obtenerArea():int
dibujar(g:Grafico)
obtenerArea():int
VentanaGráfica
1 *
 
 
Figura 7 Uso del polimorfismo 
Hemos comentado que una clase abstracta no puede ser instancia, luego no podemos 
instanciar la clase Forma, pero de acuerdo con el principio de substitución, se 
pueden crear instancias de las subclases concretas de Forma en cualquier lugar 
donde aparezca la clase base. 
Así, aunque se puede observar que un objeto de la clase VentanaGráfica puede contener 
una colección de varios objetos Forma, los únicos objetos que realmente se pueden usar 
son instancias de las subclases, por qué Forma es abstracta, y no puede ser instanciada. 
En este caso particular hay dos subclases concretas, la subclase Circulo y Cuadrado. 
Así, Ventana gráfica contendrá colecciones de objeto de tipo Círculo y Cuadrado. 
En la Figura 8 se muestra un modelo de objetos instancia del diagrama de la Figura 7. 
Este modelo muestra como un objeto VentanaGráfica soporta una colección de cuatro 
objetos s1,s2,s3 y s4 donde s1, s2 y s4 son objetos de la clase Circulo y s3 es un objeto 
de la clase Cuadrado. La cuestión es ¿Qué ocurre cuando VentanaGráfica interactúa 
con la colección de formas, y envía a cada objeto de la colección el mensaje dibujar(…). 
La respuesta es que cada objeto se comportará de la forma esperada: los Cuadrados se 
11 
 
dibujarán como cuadrados y los Círculos como círculos. Es la clase del objeto la que 
determina lo que el objeto debe dibujar. 
 
s1:Circulo
vg:VentanaGráfica
s2:Cuadrado
s3:Circulo
s4:Circulo
dib
uja
r()
dibu
jar()
dibujar()
dibujar()
 
 
Figura 8 .Uso de operaciones polimórfica. 
El punto clave en todo esto es que cada objeto responde a un mensaje por medio de la 
invocación de la operación correspondiente especificada por su clase. 
La construcción del lenguaje que hace posible el polimorfismo es la ligadura 
dinámica ( visto más adelante) conocida también como ligadura tardía o postergada 
entre llamadas a funciones y los cuerpos reales de dichas funciones. 
4. Tipos de herencia 
 
En las relaciones de herencia, se podría dar el caso de que una clase podría heredar las 
propiedades de más de una clase. Esto da lugar a que tengamos dos tipos de herencia: 
 
 La herencia simple .Es aquella en la que cada clase hereda de una única clase. 
 La herencia múltiple. Es la transmisión de métodos y datos de más de una clase 
base a la clase derivada. (Figura 9). 
 
-inversion
-interes
ProductoFinanciero
-plazo
Deposito
-Nacciones
Acciones
Combinado
 
 
Figura 9.Herencia múltiple 
Se pueden presentar dos problemas cuando se diseñan clases con herencia múltiple: 
12 
 
 
 Colisiones de nombres de diferentes clases base. 
 Herencia repetida de una misma clase base. 
 
Las jerarquías de herencia múltiple pueden ser complejas de gestionar. De hecho, no 
todos los lenguajes OO la implementan. Por ejemplo en Java, C# y SmallTalk no existe 
la herencia múltiple Aunque en Java se puede simular o implementar a través del 
concepto de Interfaz. Sin embargo en C++ si se admiten herencia múltiple. 
 
En el ejemplo anterior existe el problema de la repetición de elementos heredados. La 
clase Combinado hereda desde dos clases diferentes los mismos atributos inversión e 
interés, produciéndose una colisión. 
 
5. Ligadura 
 
La ligadura representa, generalmente una conexión entre una entidad y sus 
propiedades. Si la propiedad se limita a funciones, la ligadura es la
conexión entre la 
llamada a una función y el código que se ejecuta tras la llamada. 
 
funcion1(){
 //...
}
main(){
 funcion1();
}
xFF000234
El compiador sustituye 
esta instrucción por un 
salto a la zona de 
implementación de la 
fucnion1
Ejemplo
JUMP FF000234
 
 
El momento de tiempo en el que un atributo o función se asocia con sus valores o 
funciones se denomina tiempo de ligadura. La ligadura se clasifica según sea el tiempo 
o momento de la ligadura, y esta puede ser: estática o dinámica. 
 
La ligadura estática se produce antes de la ejecución (durante la compilación), 
mientras que la ligadura dinámica se produce durante la ejecución. 
 
En un lenguaje de programación con ligadura estática, todas las referencias se 
determinan en tiempo de compilación. La mayoría de los lenguajes procedimentales son 
de ligadura estática; el compilador y el enlazador (linker) definen directamente la 
posición fija del código que se va a ejecutar en cada llamada a la función. 
 
La ligadura dinámica supone que el código a ejecutar en respuesta a un mensaje no 
se determinará hasta el momento de la ejecución. Únicamente la ejecución del 
programa (normalmente un puntero a la clase base) determinará la ligadura efectiva 
entre las diversas que son posibles (una para cada clase derivada). 
 
Vamos a ver y a estudiar estos conceptos a través de un ejemplo. Para ello vamos a 
considerar que la notación virtual sobre un método de una clase indica al compilador 
que esa función que será implementada en una subclase no en la clase donde se define. 
13 
 
Vamos a suponer, que sólo se puede usar esta opción cuando exista la posibilidad de 
que una subclase implemente los métodos denominados virtuales. 
 
Vamos a imaginarnos que un instante de tiempo dado en la memoria hay tres objetos 
instancia de tres clases diferentes: 
Class Circulo
dibujar()
{
//Código para dibujar circulo
}
obtenerArea(){
Return PI*radio*radio; 
}
Class Cuadrado
dibujar()
{
//Código para dibujar cuadrado
}
obtenerArea(){
Return ancho*alto; 
}
Class Forma
 virtual dibujar()
Virtual obtenerArea()
 
Int x,y;
Mapa de memoria
Class VentanaGráfica
DibujarFiguras()
{
Int numFormas=10;
For (int i=0;i<numFormas;i++)
figuras[i]->dibujar();
}
Forma *figuras[10];
x000FF01
¿?
En tiempo de compilación no se 
puede determinar cual es la 
implementación( es decir la 
dirección) especifica de 
dibujar()
x000FE00
 
 
Figura 10 Mapa de memoria, funciones virtuales y polimorfismo 
 
La cuestión es ¿Como resuelve el compilador la siguiente instrucción? 
 
figuras[i]->dibujar(); 
 
Donde figuras[i] es una array de Figuras. 
 
El compilador tiene que enlazar la llamada a la función dibujar() con alguna zona de 
memoria , pero en este caso, como vemos en la Figura 10, no tiene ninguna 
implementación. Pero como está indicado con la palabra reservada virtual. El 
compilador “sabe” que debe buscar esta implementación en alguna clase derivada. 
¿Cuál de ellas? La que en ese momento este en la posición indexada 
 
5.1. Ventajas de la ligadura dinámica 
 
La principal ventaja de la ligadura dinámica frente a la ligadura estática es que la 
dinámica ofrece un alto grado de flexibilidad y diversas ventajas prácticas para manejar 
14 
 
jerarquías de clases de un modo muy simpe. Entre las desventajas de la ligadura 
dinámica se encuentra que en principio es menos eficiente que la ligadura estática. 
 
Los lenguajes OO que si siguen estrictamente el paradigma ofrecen sólo ligadura 
dinámica. Los lenguajes híbridos ( C++,Simula) ofrecen los dos tipos de ligadura. 
 
 
5.2. Polimorfismo con ligadura dinámica 
 
Con la ligadura dinámica, el tipo de objeto no es preciso decidirlo hasta el momento de 
la ejecución. El ejemplo anterior en la sección de la clase VentanaGrafica : 
 
DibujarFiguras() 
{ 
Int numFormas=10; 
For (int i=0;i<numFormas;i++) 
figuras[i]->dibujar(); 
 
} 
 
La instrucción figuras[i]->dibujar(); envía al programa al bloque apropiado de código 
basado en el tipo de objeto . En concreto pasará el mensaje dibujar() a la figura 
apuntada por figuras[i]. La indicación virtual en el método dibujar() de la clase Figura 
ha indicado al compilador que esta función se puede llamar por medio de un puntero. 
Mediante la ligadura dinámica, el programa determina el tipo de objeto en tiempo de 
ejecución. 
 
Programacion Orientada a Objetos 2015-2016 Campus/Tema 3 Practicas guiadas c__y java 2012_2013.pdf
1 
 
 
Laboratorio de Programación UPSAM 
Tema 3: Práctica guiadas 
 
 
Objetivos 
-Implementar asociaciones entre clases. 
-Utilización de colecciones para implementar relaciones. Se usarán los tipos array 
(C`++) ,List (C++)y LinkedList (Java). 
Nota: Este documento constituye material de apoyo y de estudio para las prácticas 
obligatorias que posteriormente se deberán entregar. 
 
Práctica guiada 1 C++ 
 
(Profesora Elisa García) 
 
Un juego de luces está formado por un número variable de focos (entre 4 y 6), que se determina 
exactamente cuando se fabrica. El técnico de luces puede realizar varias operaciones sobre el juego de 
luces: encenderlo completamente, encender una luz en particular, apagarlo completamente o apagar un 
foco concreto. Cada foco puede ser encendido y apagado por el operario de manera independiente. 
 
 
Se pide: 
 
Realizar un programa principal que realice las siguientes acciones: 
 Crear un juego de luces con 5 luces. 
 Encender todas las luces. 
 Apagar la primera. 
 Imprimir el estado de todas las luces del juego de luces. 
 
Estudio de la solución: 
 
 Las dos abstracciones necesarias son Juego de Luces y Foco. Y tienen una asociación 
 
 Juego de luces juega el rol de agregado respecto a Foco. Pues el juego de luces se puede 
concebir como un conjunto de Focos. Identificamos que la relación es de agregación. La 
agregación es fuerte (Composición) ya que el ciclo de vida de las instancias del tipo Foco no son 
controladas completamente dentro del agregado(en este caso Juego de Luces), luego 
tenemos una composición entre Juego de Luces y Foco. 
 
 La multiplicidad de la relación es 4..6 en la parte del agregado. Es una multiplicidad variable con 
un valor máximo. Necesitamos un parámetro en el constructor que “informe “del número de 
focos exactos que tiene el juego de luces. 
 
 Las responsabilidades individuales de cada clase son: 
 
o Clase JuegoDeLuces: tiene que permitir al operador encender cada una de los Focos, 
encender un Foco en particular, apagar todos y apagar uno en concreto. 
2 
 
 
o Clase Foco: debe poder ser apagado y encendido. 
 
 
Tomando en cuenta todas estas consideraciones tenemos el siguiente diseño: 
 
Diagrama de clases UML propuesto para la solución: 
 
+JuegoDeLuces(entrada n : int)
+encender(entrada n : int)
+encenderTodos()
+apagar(entrada n : int)
+apagarTodos()
-Focos
-nFocos : int
JuegoDeLuces
+Foco(entrada enc : bool)
+encender()
+apagar()
-encendido
Foco
1 4..6
 
 
 
La implementación en C++ propuesta es la siguiente: 
 
****** foco.h *** 
 
#ifndef FOCO_H 
#define FOCO_H 
 
class Foco 
{ 
private: 
 bool encendido; 
public: 
 Foco(bool enc); 
 ~Foco(); 
 void encender(); 
 void apagar(); 
 bool estaEncendido(); 
}; 
 
#endif 
 
****** foco.cpp *** 
 
#include "foco.h" 
 
Foco::Foco(bool enc){ 
 encendido = enc; 
} 
void Foco::apagar(){ 
 encendido = false; 
} 
void Foco::encender(){ 
 encendido = true; 
} 
 
bool Foco::estaEncendido() 
{ 
 return encendido; 
} 
 
Foco::~Foco() 
{} 
3 
 
 
Juego de Luces tiene los siguientes atributos: 
 El conjunto de Focos o colección de Focos: Esto se va a modelar como un array
de objetos tipo 
Foco. 
 Y el número exacto de estos focos: Esto permite controlar cuantos Focos hay que encender por 
que el número es variable (unas veces 6 otras 4) y con un número máximo de 6. 
 
********************************************* juegodeluces.h *** 
 
#ifndef _JUEGOLUCES_H 
#define _JUEGOLUCES_H 
#include "Foco.h" 
 
#define MIN_LUCES 4 
#define MAX_LUCES 6 
 
class JuegoDeLuces{ 
private: 
unsigned int numLuces; 
Foco* Focos; 
public: 
JuegoDeLuces(int n=MIN_LUCES); 
~JuegoDeLuces(); 
void encender(int); 
void encenderTodos(); 
void apagar(int n); 
void apagarTodos(); 
void verEstado(); 
}; 
#endif 
 
********************************************* juegodeluces.cpp *** 
#include <iostream> 
#include "juegodeluces.h" 
using namespace std; 
JuegoDeLuces::JuegoDeLuces(int n){ 
if (n>0 && n<MAX_LUCES) 
 numLuces = n; 
else 
 numLuces = MIN_LUCES; 
Focos= new Foco[numLuces]; 
} 
JuegoDeLuces::~JuegoDeLuces(){ 
delete [] Focos; 
} 
void JuegoDeLuces::encender(int n){ 
Focos[n].encender(); 
} 
void JuegoDeLuces::encenderTodos(){ 
unsigned int i; 
 for (i=0; i<numLuces; i++) 
 Focos[i].encender(); 
} 
void JuegoDeLuces::apagar(int n){ 
 Focos[n].apagar(); 
} 
void JuegoDeLuces::apagarTodos(){ 
 unsigned int i; 
for (i=0; i<numLuces; i++) 
 Focos[i].apagar(); 
4 
 
} 
 
void JuegoDeLuces::verEstado(){ 
unsigned int i; 
 for (i=0; i<numLuces; i++) 
 if (Focos[i].estaEncendido()) 
 cout << "Foco ["<< i<<"]"<<" encendido"<<endl; 
 else 
 cout << "Foco ["<< i<<"]"<<" apagado"<<endl; 
} 
 
 
 
Vamos a probar estas clases creando un programa que realice lo siguiente: 
 
 Crear un juego de luces. 
 Encender todos los focos del juego de luces. 
 Apagar el primero foco del juego de luces. 
 
 
*** MAIN.CPP *** 
 
#include <iostream> 
#include <stdlib.h> 
#include "juegodeluces.h" 
 
using namespace std; 
 
int main(int argc, char *argv[]){ 
 
 JuegoDeLuces ju1(5); 
 
 ju1.encenderTodos(); 
 ju1.apagar(0); 
 ju1.verEstado(); 
 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
 
Nota: en esta solución se utilizan objetos estáticos en C++ 
 
En este programa a diferencia de los anteriores no hemos creado los objetos de forma dinámica. 
 
 La siguiente instrucción declara una variable (ju1) de tipo JuegoDeLuces e implícitamente se 
llama al constructor de JuegoDeLuces pasándole 5 como argumento 
 
JuegoDeLuces ju1(5); 
 
 En la siguiente instrucción se accede a una función miembro mediante el operador de acceso (.) 
 
ju1.encenderTodos(); 
 
El programa principal equivalente con memoria dinámica sería el siguiente. 
 
#include <iostream> 
#include <stdlib.h> 
#include "juegodeluces.h" 
using namespace std; 
 
5 
 
int main(int argc, char *argv[]){ 
 
 JuegoDeLuces *ju1=new JuegoDeLuces(5); 
 ju1->encenderTodos(); 
 ju1->apagar(0); 
 ju1->verEstado(); 
 delete jul;//Borramos 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
 
 
Actividad propuesta 
 
 Realice un programa principal que realice lo siguiente. 
 
 Cree un juego de luces con 4 focos. 
 
 Solicite una secuencia de longitud fija de 4 caracteres “0” o “1” (Ejemplo “0011”). Donde “1” 
significa foco encendido y “0” foco apagado. 
 
 Y en función de esta secuencia active o desactive los focos del juego de luces. 
 
Práctica guiada 2.C++ 
 
 
Modelar la información acerca de departamentos y empleados de una empresa. Para ello considere las 
siguientes características: 
 
La información que se desea representar sobre un departamento es la siguiente: nombre del 
departamento ( Marketing, I+D, etc) y los empleados que pertenecen al departamento. Por otro lado 
sobre los empleados sólo es necesario guardar su nombre. 
 
Se pide: 
 
Escribir un programa principal con las siguientes acciones 
 Crear un departamento de I+D 
 Crear dos empleados 
 Asignar los empleados al departamento creado. 
 
 
 
Estudio de la solución C++ 
 
 Las dos conceptos que manejamos son: Departamento y Empleado 
 La asociación es de tipo 1:N .Se puede plantear como una agregación débil 
 El modelo de clase en UML es el siguiente: 
 
Departamento
nombre: String
empleados: List
Departamento(String )
obtenerEmpelados():void
numeroDeEmpelados():int
Asignarempelado(Empleado)
Empleado
nombre:string
Empleado(String )
ObtenerNombre():String
0..*1
 
 
6 
 
 Modelamos la clase Departamento como un agregado compuesto por Empleados. 
 El tipo de agregación es débil ya que el ciclo de vida de los objetos de tipo Empleado no están 
controlados por el agregado y podrían formar parte de otras asociaciones 
 
Implementamos ahora cada una de las clases 
 
Archivo empleado.h 
 
#ifndef EMPLEADO_H 
#define EMPLEADO_H 
class Empleado 
{ 
 protected: 
 char *nombre; 
 
 public: 
 // constructores 
 Empleado(); 
 Empleado(char * nomb); 
 // destructor 
 ~Empleado(); 
 //funciones 
 char * getNombre(); 
}; 
#endif 
 
Archivo Empelado.cpp 
#include "empleado.h" 
// constructor 
Empleado::Empleado() 
{ 
 nombre="nombre no asignado"; 
} 
 
Empleado::Empleado( char *nomb){ 
nombre=nomb; 
} 
Empleado::~Empleado() 
{} 
char * Empleado::getNombre(){ 
 return nombre; 
} 
 
Archivo departamento.h 
 
#ifndef DEPARTAMENTO_H 
#define DEPARTAMENTO_H 
#include "Empleado.h" 
#include <list.h> 
class Departamento 
{ 
 
 protected: 
 char* nombre; 
 list<Empleado *> empleados; 
 
 public: 
 // contsructor 
 Departamento(); 
 Departamento(char *nomb); 
 // destructor 
 ~Departamento(); 
 //función miembro 
 void obtenerEmpleados(); 
 void numeroDeEmpleados(); 
 void asignarEmpleado(Empleado *e); 
7 
 
}; 
#endif 
 
 
Nota: Observe la siguiente declaración 
 
list<Empleado *> empleados; 
 
Esta línea declara una variable con nombre empleados de tipo list. list es 
tipo colección que se encuentra en la librería estándar de C++ 
 
 
 
Departamento.cpp 
 
#include "departamento.h" // class's header file 
 
Departamento::Departamento(char * nomb) 
{ 
 nombre=nomb; 
} 
 
Departamento::Departamento() 
{ 
 nombre="Departamento sin nombre"; 
} 
 
Departamento::~Departamento() 
{ 
 //se borra cada elemento de la lista empleados; 
 list<Empleado *>::iterator iterador_lista=empleados.begin(); 
 
 for (;iterador_lista!=empleados.end();iterador_lista++){ 
 delete *iterador_lista; 
 } 
 delete nombre; 
} 
void Departamento::obtenerEmpleados(){ 
 //itereador_lista se posiciona al principio de la lista 
 list<Empleado *>::iterator iterador_lista=empleados.begin(); 
 
 Empleado *e; 
 //Recorrer cada elemento de la lista 
 
 for (;iterador_lista!=empleados.end();iterador_lista++){ 
 e=*iterador_lista; //Asignar el contenido del iterador 
 cout<<"Empleado :"<<e->getNombre()<<endl; 
 } 
 
 } 
void Departamento::numeroDeEmpleados(){ 
 cout<<"Dpto "<<nombre<<" tiene "<< empleados.size()<<" empleados"; 
 } 
void Departamento::asignarEmpleado(Empleado *e){ 
 //inserta un empleado al principio de la lista 
 empleados.push_front(e); 
} 
 
 
El programa principal sería el siguiente: 
 
#include <cstdlib> 
#include <iostream> 
#include "Departamento.h" 
#include "Empleado.h" 
using namespace std; 
 
8 
 
int main(int argc, char *argv[]) 
{ 
 Departamento *dpto=new Departamento("DESARROLLO"); 
 Empleado *emp1=new Empleado("Juan"); 
 Empleado *emp2=new
Empleado("Pedro"); 
 dpto->asignarEmpleado(emp1); 
 dpto->asignarEmpleado(emp2); 
 dpto->obtenerEmpleados(); 
 dpto->numeroDeEmpleados(); 
 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
 
 
 
Práctica guiada 2. Java 
 
 
 
 
Estudio de la solución en Java 
 
El mismo estudio realizado anteriormente es válido. 
 
 
Clase Empleado.java 
 
public class Empleado { 
 String nombre; 
 
 public Empleado(String nombre) { 
 this.nombre = nombre; 
 } 
 public String getNombre() { 
 return nombre; 
 } 
} 
 
Clase Departamento.java 
 
import java.util.LinkedList; 
 
public class Departamento { 
 String nombre; 
 LinkedList empleados; 
 
 public Departamento(String nombre) { 
 this.nombre = nombre; 
 empleados=new LinkedList(); //instanciar colección 
 } 
 public void asignarEmpleado( Empleado e) 
 { 
 empleados.add(e); 
 } 
 public void obtenerEmpleados() 
 { 
 Empleado e; 
 
 int tam= empleados.size(); 
 int pos=0; 
 //Mientras haya elementos 
 while ( pos<=tam-1) 
 { 
9 
 
 //obtener elemento 
 e=(Empleado)empleados.get(pos); 
 System.out.println(e.getNombre()); 
 pos++; 
 } 
 } 
 public void numeroDeEmpleados() 
 { 
 System.out.println("Numero de empleados:" +empleados.size()); 
 } 
} 
 
Observa que para implementar la relación con multiplicidad * usamos un tipo colección ( la clase 
LinkedList). 
LinkedList empleados; 
 
LinkedList es una clase tipo colección que implementa una lista enlazada. Podríamos haber usados 
otras disponibles como por ejemplo Vector. LinkedList se encuentra el paquete java.util, luego antes de 
usarla debemos importarla: 
import java.util.LinkedList; 
 
Clase ProgramaPrincipal.java 
 
 
public class ProgramaPrincipal { 
 
 public static void main(String[] args) { 
 Departamento dpto = new Departamento("DESARROLLO"); 
 Empleado emp1 = new Empleado("Juan"); 
 Empleado emp2 = new Empleado("Pedro"); 
 dpto.asignarEmpleado(emp1); 
 dpto.asignarEmpleado(emp2); 
 dpto.obtenerEmpleados(); 
 dpto.numeroDeEmpleados(); 
 } 
} 
Programacion Orientada a Objetos 2015-2016 Campus/TEMA 2 Programación orientada objetos v3.pdf
1 
 
 Tema 2 
 
 
Conceptos y 
principios de la 
orientación a objetos 
 
 
 
 
 
 
 
 
 
 
 
2 
 
Índice de contenidos 
 
1. Introducción .......................................................................................................................... 3 
2. Factores de calidad del software ........................................................................................... 3 
3. El paradigma de orientación a objetos .................................................................................. 5 
3.1. El principio de modularidad .......................................................................................... 6 
3.1.1. El modulo y su estructura .......................................................................................... 7 
3.1.2. Características de la programación modular ......................................................... 8 
3.1.3. Tamaño del modulo ............................................................................................... 8 
3.1.4. Ocultación de la información ................................................................................ 8 
3.2. Diseño de módulos. Acoplamiento y cohesión ............................................................. 9 
3.2.1. Acoplamiento ........................................................................................................ 9 
3.2.2. Cohesión .............................................................................................................. 11 
4. Tipos de datos y Tipos abstractos de datos ......................................................................... 12 
5. De los módulos a los tipos abstractos de datos. .................................................................. 12 
6. Clases y Objetos .................................................................................................................. 14 
6.1. Atributos ...................................................................................................................... 17 
6.2. Estado de un objeto. .................................................................................................... 18 
6.3. Mensajes ,operaciones y métodos ............................................................................... 18 
6.3.1. Comportamiento de un objeto ................................................................................. 19 
6.3.2. Método .................................................................................................................... 19 
6.3.3. Definición de un Método ......................................................................................... 20 
6.3.4. Mensajes .................................................................................................................. 22 
6.4. Ocultación de información .Visibilidad de los atributos y métodos ........................... 22 
6.5. Propiedades de instancia y propiedades de clase ........................................................ 23 
6.6. Los métodos constructor y destructor de una clase ..................................................... 23 
 
 
3 
 
 
1. Introducción 
 
Vivimos en un mundo de objetos. Estos objetos existen en la naturaleza, en entidades 
hechas por el hombre, en los negocios y los diversos productos que usamos. Pueden ser 
clasificados, descritos, organizados, combinados, manipulados y creados. Por eso no es 
de extrañar que se proponga una visión orientada a objetos para la creación de software 
de computadora, una abstracción que modela el mundo de forma tal que nos ayuda a 
entenderlo mejor. 
 
A finales de los 60 se propuso un enfoque orientado a objetos para el desarrollo de 
software, pero esta tecnología ha necesitado casi veinte años para ser ampliamente 
usada. Durante los 90, la POO (programación orientada a objetos) se convirtió en el 
paradigma para muchos desarrolladores de software. 
 
Las tecnologías de objetos llevan a reutilizar, y la reutilización lleva a un desarrollo de 
software más rápido y a programas de mejor calidad. El software orientado a objetos 
es más fácil de mantener debido a que su estructura es inherentemente poco 
acoplada. Esto lleva a menores efectos colaterales cuando se deben hacer cambios y 
provoca menos frustración en el ingeniero del software y en el cliente. Además, los 
sistemas orientados a objetos son más fáciles de adaptar y escalan más fácilmente (por 
ejemplo: pueden crearse grandes sistemas ensamblando subsistemas reutilizables). 
 
En este capítulo presentamos los principios y conceptos básicos que forman el 
fundamento para la comprensión de la tecnología de objetos. 
 
2. Factores de calidad del software 
Algunos de los aspectos que han puesto de manifiesto las limitaciones de la 
metodología clásica de desarrollo del software son: la evolución constante de las 
aplicaciones informáticas para adaptarlas a nuevos requerimientos (tanto tecnológicos 
como funcionales), la necesidad de reutilización para acortar tiempos de desarrollo, y la 
capacidad de producir software más fiable y con un mayor grado mantenimiento. 
 
Para atender estas necesidades y hacer que las aplicaciones informáticas sean viables 
económicamente, es necesario facilitar la reutilización de programas minimizando el 
trabajo de hacer adaptaciones a las funcionalidades nuevas. Un prerrequisito para 
facilitar
la reutilización es que el software desarrollado responda a ciertos criterios o 
factores de calidad. 
 
El software básicamente es un mecanismo, que se usa para ejecutar rutinas de forma 
automática y de forma enormemente rápida a través de un ordenador. Pero aparte de 
estos aspectos, existen otros muy importantes como pudieran ser la fiabilidad con la que 
se ejecutan estas rutinas por ejemplo, lo fácil que es interactuar con el programa o la 
facilidad con la que se puede modificar o extender el programa para adecuarse a nuevos 
requisitos. 
4 
 
Aunque hay muchas medidas de la calidad de software, la corrección, facilidad de 
mantenimiento, integridad y facilidad de uso son algunos de los factores más 
importantes. 
Hace 25 años McCall y Cavano [MCC78] definieron un conjunto de factores de calidad 
como los primeros pasos hacia el desarrollo de métricas de calidad del software. Estos 
factores evalúan el software desde las siguientes dimensiones: 
 Operación del producto (utilizándolo). 
 Revisión del producto ( cambiándolo). 
 Transición del producto (modificándolo para que funcione en un entorno 
diferente ( portándolo). 
Para cada una de estas categorías, McCall definen una serie factores de calidad como 
se muestra la siguiente figura: 
 
Figura 1 Criterios de calidad de McCall 
La siguiente tabla muestra un resumen con todos los factores de calidad del modelo de 
McCall. 
Factores de calidad McCall 
Aspecto 
que trata 
 
Factor 
Calidad 
 
Relacionado con … 
 
Operación 
del Producto 
 
Corrección 
 
 
Fiabilidad 
 
 
Eficiencia 
 
 
Integridad 
 
 
Facilidad 
de uso 
 
Hasta donde satisface un programa su especificación y logra los objetivos propuestos por 
el cliente. 
 
Probabilidad de operación libre de fallos de un programa en un entorno determinado 
durante un tiempo específico. 
 
Cantidad de recursos de computadora y de código requeridos por un programa para 
llevar a cabo sus funciones. 
 
Grado en que puede controlarse el acceso al software o a los datos, por personal 
no autorizado. 
 
Esfuerzo requerido para aprender un programa, trabajar con él, preparar su entrada 
e interpretar su salida. 
 
Revisión 
del producto 
 
Facilidad de 
Mantenimiento 
 
Flexibilidad 
 
Facilidad 
de prueba 
 
Esfuerzo requerido para localizar y arreglar un error en un programa. 
 
 
Esfuerzo requerido para modificar un programa operativo. 
 
Esfuerzo requerido para probar un programa de forma que se asegure que realiza 
la función requerida. 
 
Transición Portabilidad Esfuerzo requerido para transferir el programa desde un hardware y/o entorno de 
5 
 
del producto 
 
 
 
Reusabilidad 
 
 
Facilidad de 
interoperación 
 
sistemas de software a otro. 
 
Grado en que un programa (o partes de un programa) puede volverse a usar en otras 
aplicaciones. 
 
Esfuerzo requerido para acoplar un sistema a otro 
 
 
La calidad no es tema desligado de los aspectos más técnicos, de hecho son algunos de 
estos factores y su cumplimiento lo que impulsan determinadas mejoras en la forma de 
abordar la construcción de un sistema software. 
 
3. El paradigma de orientación a objetos 
Un paradigma de programación hace referencia a como establecer, ver y resolver 
problemas (en nuestro caso de programación). El paradigma orientado a objetos 
establece el tipo de objeto como la unidad de representación y compresión de sistemas 
software complejos. 
El termino orientado a objetos sé uso para referirse al enfoque de desarrollo de software 
que usaban lenguajes como ADA 95,C++, SmallTalk, etc. Hoy en día el paradigma 
orientado a objetos encierra una visión completa de la ingeniería de software. 
Hay muchas formas de enfocar un problema utilizando una solución basada en software. 
El modelado y diseño orientado a objetos es una manera estructurar un sistema, en 
la que se utilizan objetos para representar conceptos del mundo real. El dominio del 
problema se representa mediante un conjunto de objetos con atributos y 
comportamientos específicos. Los objetos son manipulados mediante una colección de 
funciones llamados métodos y se comunican entre sí mediante un protocolo de 
intercambio de mensajes. 
 
Según Grady Booch: La POO es un método de implementación en el que los 
programas se organizan como colecciones de objetos cooperativos, donde cada uno de 
los cuales representa un instancia de alguna clase… . 
En esta definición se pueden distinguir tres cuestiones: 
1. La POO utiliza objetos cooperativos, no algoritmos (programación 
estructurada), como bloques de construcción principales. 
2. Visión cooperativa de objetos 
3. Cada objeto es una instancia de una clase. 
El concepto fundamental es el objeto, entendido como una combinación estructural 
de datos y comportamiento en una única entidad. De esta forma podemos definir un 
objeto como: 
 
6 
 
Un objeto es un módulo auto contenido de datos y operaciones que representa una 
entidad tangible o intangible con significado en el dominio del problema. 
 
De manera general la programación orientada a objetos divide el proceso en dos partes: 
1. Definición de clases (tipos de objetos) y sus relaciones. 
2. Puesta en funcionamiento de los objetos mediante la instanciación y el 
intercambio de mensajes entre ellos. 
El paradigma de orientación a objetos es atractivo debido a que favorece la creación y 
reutilización de componentes independientes lo que va unido a factores de calidad 
como son la reutilización y la extensibilidad, mantenimiento, etc. Además, los 
componentes de software derivados mediante el paradigma de objetos muestran 
características asociadas con el software de alta calidad, como la independencia 
funcional, la ocultación de información, etc. 
 
Para entender la orientación a objetos, es necesario abordar una serie de conceptos que 
influyen en el diseño. Muchos son conceptos anteriores a la POO, pero que representan 
una serie de importantes ideas que fundamentan las ventajas de la orientación de 
objetos. 
3.1. El principio de modularidad 
 
La modularidad es la posibilidad de subdividir una aplicación en piezas más 
pequeñas (denominadas módulos), donde cada una de las cuales debe ser tan 
independiente como sea posible. 
 
La programación modular se concibió como el desarrollo de programas a partir de 
acoplar pequeñas piezas de código, denominadas subrutinas, que al mismo tiempo se 
agrupaban en módulos cuando éstas trabajaban con los mismos datos. 
 
El cumplimiento de muchos de los criterios de calidad vistos en el apartado anterior 
depende en gran manera de la modularidad de las estructuras de representación, tanto de 
código como de datos. 
 
En este apartado profundizaremos en esta definición informal, y nos centraremos en 
aquellas propiedades y características que debe tener un método para garantizar la 
modularidad. Además discutiremos los criterios que se tienen que cumplir para que una 
estructura de representación se pueda calificar de modular y, por lo tanto, permita una 
metodología de desarrollo modular. 
 
7 
 
 
3.1.1. El modulo y su estructura 
Pensemos en un gran bloque monolítico de código. Desde el punto de vista del 
mantenimiento esta forma de codificar presenta numerosos problemas: la depuración de 
errores se vuelve extremadamente difícil y la comprensión del código se vuelve 
virtualmente imposible. La solución es dividir el bloque en otros bloques más 
pequeños, conocidos como módulos. 
La palabra modulo tiene diversas acepciones y significados. Un módulo es una unidad 
claramente definida y manejable, con una interfaz definida. En otras palabras, es la 
unidad mínima con sentido propio en un sistema software. No obstante vamos a ver una 
serie de
definiciones de la palabra modulo. 
 
Stevens, Myers y Constantine [1974] realizan un primer intento de definición. Ellos 
definen un módulo “como un conjunto de una o más instrucciones contiguas 
agrupadas con un nombre, por el cual otras partes del sistema pueden invocarlas y que 
preferiblemente poseen su propio conjunto de variables”. 
 En otras palabras un módulo es un bloque de código que puede ser invocado como un 
procedimiento, función o método. 
Diccionario Webster .”Una pieza independiente de software que es parte integrante de 
una u otras de mayor envergadura. Los módulos se compilan normalmente de forma 
separada e independiente y proporcionan un mecanismo de abstracción o de ocultación 
de información de forma que su implementación pueda ser cambiada sin afectar a otros 
módulos.” 
Un módulo se caracteriza fundamentalmente por su interfaz y por su 
implementación. Parnas define el módulo como “un conjunto de acciones 
denominadas, funciones o sub-módulos que corresponden a una misma abstracción, 
que comparten un conjunto de datos comunes llamados atributos. Las acciones o 
funciones de un módulo que son susceptibles de ser llamadas desde el exterior se 
denominan primitivas o puntos de entrada del módulo”. 
 
El concepto de modulo ha evolucionado a lo largo del tiempo junto con los lenguajes de 
programación. Desde los subprogramas (funciones y procedimientos), pasando por lo 
tipos abstractos de datos hasta el concepto de clase. El módulo ha ido cambiando de 
forma. Pero en todos los casos su fin es el de proporcionar unidades auto contenidas, 
independientes y que funcionen como si se permiten el símil como cajas negras. 
 
Cada módulo tendrá un significado específico y debe asegurarse que cualquier cambio 
en su implementación no afecte a su exterior (o al menos, que afecte lo mínimo 
posible). De igual modo, se debe intentar asegurar que los errores posibles, condiciones 
de límites o frontera, comportamientos erráticos, no se propaguen más allá del módulo 
(o como máximo a los módulos que estén directamente relacionados). 
 
8 
 
La Figura 2 muestra un esquema de las partes más importantes de un módulo 
idealizado desde el punto de vista del tipo abstracto de datos o clase. 
 
Primitivas de acceso y manipulación
Atributos
Algoritmos
Parámetros
Interfaz
Sección privada
 
Figura 2 Estructura de un módulo 
 
3.1.2. Características de la programación modular 
La modularización es una técnica de división como estamos viendo, que permite al 
programador resolver un problema, dividiéndolo en sub-problemas más pequeños, de 
forma que se puedan resolver independientemente unos de otros, para después 
combinarlos. 
 
 
3.1.3. Tamaño del modulo 
 
Al dividir u organizar de forma modular se deberá decidir la envergadura del módulo. 
Existen módulos grandes y módulos pequeños, pero una característica esencial es que el 
módulo sea auto contenido, completo, cohesivo y con el menor acoplamiento posible 
respecto a otros módulos, de forma que se pueda integrar con otras partes y que se 
pueda considerar para su corrección de forma aislada. 
 
3.1.4. Ocultación de la información 
 
En la etapa de diseño de un módulo, es imprescindible especificar el conjunto de las 
propiedades del módulo que constituirán la información a la cual tendrán acceso los 
otros módulos y a cuáles no. Estas propiedades las podemos dividir en dos tipos, 
propiedades públicas y propiedades privadas: 
 
 Las propiedades públicas son aquellas que son visibles desde fuera del módulo 
tanto para los usuarios como para otros módulos .Serán la interfaz del modulo 
 En cambio, las propiedades privadas son aquéllas que son internas al módulo, 
por lo que su visibilidad es exclusivamente dentro del módulo y estará oculta al 
exterior. 
 
9 
 
 
 
3.2. Diseño de módulos. Acoplamiento y cohesión 
 
Aunque el diseño modular persigue la división de un sistema grande en módulos más 
pequeños y manejables, no siempre esta división es garantía de un sistema bien 
organizado. Muchos aspectos de la modularización pueden ser comprendidos solo si se 
examinan módulos en relación con otros. Los módulos deben diseñarse con los 
criterios de acoplamiento y cohesión. El primer criterio busca la menor dependencia 
posible entre módulos y el segundo busca que todo lo agrupado bajo el modulo 
obedezca al mismo propósito. En este sentido Booch define la modularidad como :”la 
propiedad de un sistema que ha sido descompuesto en un conjunto de módulos 
cohesivos y débilmente acoplados”. 
 
Myers[1978] define cohesión y acoplamiento de la siguiente forma: 
 El acoplamiento entre módulos como el grado de interdependencia entre dos 
módulos. 
 La cohesión como el grado de interacción dentro de un módulo. 
Veremos a continuación estos dos conceptos tan importantes en el diseño de módulos 
 
3.2.1. Acoplamiento 
El acoplamiento es una medida de interdependencia entre módulos. Se dice que 
existe alto acoplamiento cuando el cambio de un modulo fuerza al cambio de los que 
dependen de él. El objetivo es hacer que está interdependencia sea mínima. 
 
En la siguiente Figura (Figura 3) el módulo C1 tienen un acoplamiento relevante con 
C10, si un cambio en C10 implica un cambio en C1. 
 
C1
C10
 
Figura 3 Interdependencia entre módulos 
Como el acoplamiento se puede producir sólo en las relaciones entre módulos, cuantas 
más relaciones tenga un módulo, mayor probabilidad habrá que se produzcan 
acoplamientos. Por lo tanto podemos extraer las siguientes conclusiones o reflexiones: 
 
 
10 
 
 Cuantas menos conexiones existan entre módulos, menos oportunidad habrá 
para que se produzca el «efecto onda», es decir, que un defecto en un módulo 
pueda afectar a otro. 
 Cuanto menos acoplados estén los módulos menos implicaciones o efectos 
colaterales podrán tener los cambios realizados. 
 
A la vista de los anterior podría deducirse que una buena medida de diseño que evita el 
acoplamiento sería la de limitar al máximo las conexiones entre módulos. Esto en 
parte es cierto, ya que a menor número de relaciones menor probabilidad de verse 
afectado por cambios en otros módulos, pero muchas veces es inevitable y es necesario 
que un módulo se comunique con otros muchos y no necesariamente aumenta su 
acoplamiento. Para entender esto hay que considerar además, el grado de exposición 
que tiene un módulo respecto a los detalles internos de otro. 
 
Pensemos en dos módulos M1 y M2. Supongamos que M2 es un módulo que representa 
un almacén de medidas de temperatura, y ofrece capacidades a otros módulos para que 
puedan ser extraídas en orden a como se obtuvieron. Y supongamos que M1es un 
módulo estadístico que opera sobre estas medidas a través de M2. Imaginemos que M1 
“conoce” el tamaño del array que tiene M2 para guardar las medidas. Si este tamaño 
cambiase en M2, el cambio afectaría de manera irreversible a M1. Esto es debido a que 
M1 tiene un fuerte acoplamiento con M2, no sólo por su relación con M2 sino también 
por su exposición a los detalles internos de M2 de cómo funciona. La conclusión es que 
el grado de acoplamiento entre módulos no necesariamente está solamente en función 
del número de conexiones entre módulos. 
 
El objetivo por tanto es obtener módulos débilmente acoplados. 
 
Un acoplamiento bajo indica un sistema bien particionado y puede obtenerse de dos 
maneras: 
 
 Reduciendo el número de relaciones: Cuanto menos conexiones existan entre 
módulos, menor será la posibilidad del efecto en cadena (un error en un módulo 
provoca otro colateral en otro modulo). 
 
 Debilitando el nivel de dependencia en las relaciones necesarias: Ningún 
módulo debería depender de los detalles internos de implementación de 
cualquier otro. Lo único
que tiene que conocer un módulo de otro es como 
invocarlo y como interpretar el resultado obtenido. 
 
La siguiente tabla muestra la clasificación de acoplamientos entre módulos y su grado 
de acoplamiento desde un punto de vista de la programación estructurada. 
 
Tipo de acoplamiento Grado de acoplamiento Facilidad de mantenimiento 
Por contenido Alto (fuerte) Bajo 
Común | | 
De control | | 
Por (Estampado) | | 
Datos ↓ ↓ 
 Bajo (débil) Alto 
 
11 
 
Las ventajas de un sistema débilmente acoplado son muchas. Tal como define Booch, 
un sistema modular débilmente acoplado facilita: 
 
 La sustitución de un módulo por otro, de modo que los módulos afectados por 
un cambio serán menos. 
 El seguimiento de un error y el aislamiento del módulo defectuoso que produce 
ese error. 
 
 
3.2.2. Cohesión 
 
Otro medio para evaluar la partición en módulos (además del acoplamiento) es observar 
como las actividades dentro de un módulo están relacionadas unas con otras; este es el 
criterio de cohesión. 
 
La cohesión tiene que ver con el grado de relación que tienen los elementos internos de 
un módulo, o más generalmente, la forma en la que agrupamos elementos en una 
unidad conceptual de mayor nivel. Por ejemplo, la forma en la que agrupamos 
funciones en una librería, o la forma en la que agrupamos métodos en una clase, o la 
forma en la que agrupamos clases en una librería, etc. Su objetivo es organizar los 
elementos de tal manera que los que tengan más relación a la hora de realizar una tarea 
pertenezcan al mismo módulo, y los elementos no relacionados figuren en módulos 
separados. 
 
La cohesión también se puede definir corno la medida de la relación funcional de los 
elementos de un módulo; entendiendo por elementos, tanto la sentencia o grupo de 
sentencias que lo componen, como las definiciones de datos o las llamadas a otros 
módulos. Idealmente, un módulo coherente sólo debe hacer una única cosa. 
 
El objetivo es diseñar módulos con una alta cohesión, cuyos elementos estén fuerte y 
genuinamente relacionados unos con otros. 
 
Myers define siete categorías o niveles de cohesión. La siguiente tabla muestra estos 
grados de cohesión: baja cohesión (no deseable) y alta cohesión (deseable). 
Tipo de cohesión Grado de cohesión Grado de mantenimiento 
Por coincidencia Bajo Bajo 
Lógica | | 
Temporal | | 
Procedimental | | 
Por comunicaciones | | 
Secuencial ↓ ↓ 
Funcional ↓ ↓ 
 Alto 
 
Alto 
 
Tabla. Clasificación de cohesión de módulos 
 
12 
 
 
 
4. Tipos de datos y Tipos abstractos de datos 
 
Todos los lenguajes de programación soportan el concepto de tipo de datos. Por 
ejemplo, el lenguaje de programación C soporta los tipos int, float, char, así como los 
tipos: colección y estructuras ( struct). 
Un tipo de datos define un conjunto de valores posibles y un conjunto de operaciones 
definidas para ese tipo de valores. 
 Por ejemplo un tipo entero define un conjunto de datos posibles (números enteros 
negativos y positivos) además de las operaciones permitidas sobre ellos (las operaciones 
aritméticas). 
Los tipos abstractos de datos (TAD), vistos en el siguiente apartado, extienden la 
función de los tipos de datos tradicionales .Y tienen la capacidad de ocultar detalles 
internos de su implementación o funcionamiento. Esta capacidad de ocultar la 
información permite el desarrollo de componentes de software reutilizables y 
extensibles. 
El tipo constituye así la forma en la cual un desarrollador modela las entidades de la 
realidad que intenta representar y manipular y para el compilador representa la manera 
de reservar memoria y saber cómo manipular determinados valores. Los lenguajes 
modernos suelen permitir definir al programador sus propios tipos de datos permitiendo 
así una mayor flexibilidad a la hora de representar entidades. 
5. De los módulos a los tipos abstractos de datos. 
 
Se ha ido avanzando en la independencia de conjuntos de instrucciones para hacerlas 
no solamente una unidad funcional independiente, sino que esta una unidad funcional 
sea cada vez de mayor envergadura. Los procedimientos y funciones pueden 
considerarse las primeras aproximaciones a la programación modular, los subprogramas 
(funciones o procedimientos) pueden ser vistos como módulos. Posteriormente el tipo 
abstracto de datos ha constituido un avance significativo en la construcción de unidades 
modulares más completas al unir estructuras de datos y funciones en una misma 
unidad, permitiendo reducir el acoplamiento y aumentando la cohesión. 
Una idea fundamental en la abstracción de los tipos abstractos de datos es la 
separación de la especificación del tipo y su implementación interna. Ya que si la 
representación interna se oculta, el modulo que llama o que usa esta unidad funcional 
tendrá un acoplamiento muy bajo. 
13 
 
Para ver más en detalle esto, vemos que en esencia un TAD es un tipo de dato que 
consta de datos y operaciones definidas para estos datos. 
T.A.D= Datos+operaciones (funciones y procedimientos) 
En el siguiente ejemplo se muestra un TAD que ofrece la característica de 
ocultamiento y encapsulación estando compuesto de dos partes: interfaz pública e 
implementación interna. 
Ejemplo de un TAD. Definición e interfaz 
TAD ListaCrtlProcesos: 
Datos internos 
 Lista: Proceso 
Operaciones accesibles 
AñadirProc ( Proceso) 
Proceso sacarProc ( ) 
Proceso obtenerPrimerProceso(); 
Implementación interna 
AñadirProc ( Proceso){ 
Lista.add(Proceso); 
} 
Proceso sacarProc ( 
(Proceso)Lista.obtenerPrimerProceso(); 
 ) 
 
Entre las ventajas que ofrecen los tipos abstractos de datos podemos destacar las 
siguientes: 
 Permiten una mejor conceptualización del mundo real. Las estructuras de datos 
complejas como son los TAD permiten mejorar la representación, mejorando 
con ello la comprensión. 
 Separa la implementación de la especificación con lo que disminuye el 
acoplamiento. 
 Favorece el diseño cohesivo de módulos puesto que obliga a plantear de 
manera conjunta datos y operaciones. 
 Aumenta la reusabilidad de unidades funcionales puesto que estas se han 
concebido como independientes, no acopladas y cohesivas. 
 
14 
 
 
6. Clases y Objetos 
 
La clase unifica los principios del diseño modular y la definición de tipo de datos. La 
clase en los lenguajes orientados a objetos es la unidad mínima de descomposición 
funcional del software (módulo). 
 
Desde el punto de vista de los tipos de datos. Una clase no es un tipo abstracto de datos 
compuesto por datos y operaciones. Desde el punto de vista del diseño y la 
programación modular, una clase es un módulo. La clase lleva al límite las ventajas de 
ofrecidas por los tipos abstractos de datos: bajo acoplamiento y alta cohesión. Desde un 
punto de vista cognitivo una clase representa una abstracción del mundo real, 
caracterizada por un conjunto de atributos y por el conjunto de operaciones que admite. 
 
En principio una clase puede representar casi cualquier cosa, una aproximación de lo 
que representa una clase sería la siguiente: 
 
Una clase representa una generalización de una serie de objetos que tienen el mismo 
conjunto de características. 
 
Como vemos, íntimamente relacionado con el concepto de clase, está el de objeto. El 
objeto representa una instancia particular, es decir un ejemplar especifico de una clase 
(Por ejemplo de la clase archivos, un SSOO puede tener los siguientes ejemplares: 
cab234.txt (3Mb), runtime.exe(2Kb)) 
 
A veces, es difícil diferenciar los conceptos de clase y objeto. Para ver mejor esto con 
un ejemplo, cuando hablamos de un objeto cuenta corriente, todo el mundo entiende 
que nos referimos a un concepto que sirve para reflejar el dinero que una
persona tiene 
en el banco, a través de un saldo, un código de identificación, y una serie de titulares. 
En este caso concreto, cuenta corriente se debe concebir como una clase de objetos, ya 
que representa un grupo o conjunto de entidades con ciertas características comunes 
(número, saldo, titulares).La clase describe como es una cuenta corriente para ser 
considerada como tal. 
 
Cuando hablamos de la cuenta corriente 6773-0100-89-2223873, con saldo 10.000 
euros, titulares Juan Antonio y Rosa , nos referimos a una cuenta en concreto. En este 
caso, esta cuenta corriente se debe entender como un objeto particular o más 
concretamente en programación, como una instancia de la clase Cuenta Corriente que 
identifica un miembro individual y concreto de la clase de objetos descritos. 
 
La clase describe un grupo de objetos que comparten un mismo conjunto de 
características, mientras que el objeto es un representante particular de este grupo. 
 
 Así podemos ver que una clase es una descripción generalizada, como por ejemplo una 
plantilla, que describe una colección de objetos similares. Por definición, todos los 
objetos que pertenecen a una clase deben tener sus atributos y las operaciones 
disponibles para la manipulación de los atributos. 
 
15 
 
 
La relación entre clases y objetos es una relación de pertenencia. Así un objeto se dice 
que pertenece a una clase. Enfocado desde un punto de vista más técnico el siguiente 
ejemplo muestra como un objeto instancia a una clase o dicho de otra forma una 
variable de tipo clase recibirá como valor un objeto concreto. 
 
 
 
Ejemplo de instancias de la clase cuenta 
 
 
Objetos 
 
El manual de referencia de UML (The UML Reference Manual [Rumbaugh] define un 
objeto como: “Una entidad discreta con limites perfectamente definidos, que 
encapsula un estado y un comportamiento instancia de una clase”. 
 
Según (Booch). “Un objeto es un conjunto cohesivo de datos y funciones. “ 
 
Cuando hablamos de objetos, hacemos referencia a un conjunto de datos concretos que 
pueden cambiar, mientras que cuando hablamos de clase hacemos referencia a como es 
la estructura que representa a esos objetos y que se usa como plantilla para crearlos. En 
este sentido la clase es un concepto estático, mientras que el objeto es un concepto 
más dinámico. 
 
En programación orientada a objetos una instancia se produce con la creación de un 
objeto perteneciente a una clase . A diferencia de una variable convencional, un objeto 
no se crea simplemente definiendo una variable hay que llamar a un método 
especial denominado constructor (visto más adelante) que indica la forma de crear 
el objeto. Para ver esto en la siguiente línea de código Java se crea un objeto cuenta 
corriente. 
CuentaCorriente cc01= new CuentaCorriente(Juan,Casas Fuente,5000); 
http://es.wikipedia.org/wiki/Objetos_%28programaci%C3%B3n_orientada_a_objetos%29
http://es.wikipedia.org/wiki/Clase_%28inform%C3%A1tica%29
16 
 
En nuestro caso cc01 es una instancia de la clase CuentaCorriente. O dicho de otra 
forma cco1 es un objeto de la clase CuentaCorriente En la documentación 
utilizaremos instancia, objeto o ejemplar indistintamente. 
 
Representación conceptual y representación a nivel de lenguaje de programación 
de una clase. 
 
Conceptualmente y de forma general una clase se caracteriza por un nombre de clase, 
un conjunto de atributos y un conjunto de métodos. La siguiente figura (Figura 4) 
muestra una representación simbólica de una clase en la notación UML ( Unified 
Modeling Language ver anexo I). 
 
+Op1()
+OpN()
-Atributo 1
-Atributo N
Nombre Clase
 
Figura 4 . Representación UML de una clase 
A nivel del lenguaje de programación la clase debe representarse con los elementos 
propios que proporcionan los lenguajes de programación. En concreto para Java y C++ 
seria de la siguiente forma. 
 
 
Representación y especificación de una clase en Java: 
 
public class Ordenador { 
 //atributos 
 
 String marca; 
 String modelo; 
 String procesador; 
 int memoria; 
 float frecuencia; 
 boolean encendido; 
 
 //metodos 
 public Ordenador(){} 
 public Ordenador (String mar,String mod,String micro,int men,float fre){ 
 marca=mar; 
 modelo=mod; 
 procesador=micro; 
 memoria=men; 
 frecuencia=fre; 
 } 
 public void encender(){ 
 if (!encendido) 
 encendido=true; 
 
 } 
} 
 
 
Representación y especificación de una clase en C++ 
 
Archivo Ordenador.h 
 
17 
 
class Ordenador 
{ 
 
 private: 
 char * marca; 
 char * modelo; 
 char * procesador; 
 int memoria; 
 float frecuencia; 
 bool encendido; 
 public: 
 
 Ordenador(); 
 Ordenador(char *mar,char *mod,char *micro,int men,float fre); 
 void encender(); 
 
 ~Ordenador(); 
}; 
 
Archivo Ordenador.cpp 
 
#include "ordenador.h" 
 
 
Ordenador::Ordenador() 
{ 
 
} 
Ordenador::Ordenador(char *mar,char *mod,char *micro,int men,float fre) 
{ 
 marca=mar; 
 modelo=mod; 
 procesador=micro; 
 memoria=men; 
 frecuencia=fre; 
} 
 void Ordenador::encender(){ 
 if (!encendido) 
 encendido=true; 
 
} 
 
Ordenador::~Ordenador() 
{ 
 
} 
 
Como se puede ver el mismo concepto es representado de forma diferente en los 
diferentes lenguajes. Pero ambas representaciones comprenden elementos comunes 
como son atributos y métodos. 
 
6.1. Atributos 
 
Cualquier objeto o entidad existente se puede describir a partir de una serie de atributos 
.Una clase puede tener un número arbitrario de atributos que definen las características 
más relevantes en el dominio del problema y que identifican unívocamente el tipo de 
objetos que está representado. 
 
Un atributo es una característica o propiedad de una entidad 
 
Un atributo toma valor en un dominio de información, que define el tipo de valores 
válidos en la interpretación de ese atributo .Por ejemplo, suponga que una clase 
18 
 
denominada Coche con el atributo color. El dominio de valores de color podría ser 
(blanco, negro, plata, gris, azul, rojo, amarillo, verde). 
 
 
6.2. Estado de un objeto. 
 
El estado de un objeto es el valor de sus atributos observados en un instante de tiempo 
concreto. 
 
En un instante de tiempo determinado un objeto tendrá una serie de valores en todos y 
cada uno de sus atributos, al conjunto de valores que toman los atributos de un objeto 
en un instante de tiempo dado se denomina estado del objeto. Como es evidente el 
estado de un objeto puede variar a lo largo del ciclo de vida del objeto en el programa. 
 
El concepto de estado define los valores transitorios y estables por los que puede pasar 
un objeto, de forma que se puede determinar la corrección de un sistema a partir del 
conjunto de estados alcanzado por los objetos del sistema. 
 
6.3. Mensajes ,operaciones y métodos 
 
Cuando se diseña una clase, la estructura interna y los detalles de implementación se 
ocultan, con la posibilidad de intercambiar mensajes como la única posibilidad de 
conexión. 
 
 
Datos
Métodos
Mensajes
Objeto
 
 
 
 
 En un modelo de clases y sus objetos, los métodos (operaciones o servicios) asociados 
a estas clases describen el comportamiento asociado a un objeto. 
Cada método tiene un nombre y un cuerpo que realiza la acción o comportamiento 
asociado con el nombre del método. Un método dentro de un objeto se activa por un 
mensaje que se envía por otro objeto al objeto que contiene el método. 
19 
 
O1
O2
O4
1.me
nsaj
e
2.Activación del 
servicio/operación
asociado a mensaje
 
 
6.3.1. Comportamiento de un objeto 
 
Cuando un objeto recibe algún mensaje, siempre inicia algún tipo de procesamiento. 
Cada una de las operaciones que tiene la clase proporciona una representación del 
comportamiento de los objetos a los que representa. Por eso el conjunto de 
operaciones que admite un objeto o clase de denomina comportamiento del objeto, y 
está íntimamente relaciona con la funcionalidad que puede realizar. 
 
Por ejemplo, dada la siguiente clase llamada Punto que representa puntos en un espacio 
bidimensional y que podemos representar de la siguiente forma: 
 
Punto
X:Real
Y:Real
Punto (X:Real,Y:Real)
mover(X1:Real,Y1:Real)
 
 
La consecuencia de la existencia de la operación mover(X1:Real,Y1:Real) es que la 
clase punto ha sido diseñada para recibir un estimulo (llamaremos a este estímulo 
mensaje).Cada vez que un objeto recibe un mensaje/estimulo inicia un comportamiento. 
El comportamiento especificado para un objeto instancia de esta clase es moverse a un 
desplazamiento determinado. El método para moverse vendrá implementado en el 
método. 
 
6.3.2. Método 
 
Un método es la especificación e implementación de un servicio u operación que puede 
ser requerido a cualquier objeto de la clase. El método muestra, o define, parte del 
comportamiento de los objetos representados en la clase. 
 
Un método se implementa en una clase de objetos, e indica cómo debe actuar el objeto 
cuando recibe el mensaje vinculado a ese método. Por ejemplo el método para mover un 
punto es incrementar en X1 e Y1 las coordenadas del punto. A su vez un método 
también puede enviar mensajes a otros objetos. 
 
20 
 
Los métodos describen el comportamiento asociado a un objeto. Debemos considerar 
que la ejecución de un método puede conducir a un cambio de estado del objeto. 
6.3.3. Definición de un Método 
 
Un método se define por un nombre, por los argumentos o parámetros de entrada que 
necesita y por el valor de retorno que se obtiene al ejecutar el comportamiento que 
implementa. Estos elementos (valor de retorno, nombre y parámetros) de un método se 
conocen como prototipo del método o de una función miembro. 
 
NombreMetodo (parametro1 Tipoparam1..., parametroNTipo paramN)Tipo de retorno 
 
 La implementación define el comportamiento del objeto ante la llamada del método 
 
NombreMetodo (parametro1Tipo param1..., parametroNTipo paramN)Tipo de retorno 
 
<Implementación del método> 
 
 
Podemos distinguir de manera informal varios tipos de métodos dentro de una clase: 
 
 Métodos constructores y destructores de objetos. 
 Métodos de acceso a propiedades: métodos set (establecer) y get (obtener) 
 Métodos de servicio (en general). 
 
La primera categoría representa los métodos dedicados a cómo crear y eliminar objetos 
en. Veremos más adelante estos dos métodos en profundidad. 
 
La segunda categoría corresponde un tipo de métodos muy comunes en cualquier 
clase. Los métodos “set y get“ están diseñados para obtener y establecer el valor de los 
atributos de un objeto. Un ejemplo típico de este tipo de métodos lo podemos encontrar 
en la clase Ordenador anteriormente comentada. Para esta clase se podría haber 
diseñado los siguientes métodos set y get. 
 
Ejemplo de métodos set y get para la clase Ordenador.java 
 
public class Ordenador { 
 private String marca; 
 private String modelo; 
 private String procesador; 
 private int memoria; 
 private float frecuencia_micro; 
 private boolean encendido; 
 
 public Ordenador(){} 
 public Ordenador (String mar,String mod,String micro,int men, 
 float fre){ 
 marca=mar; 
 modelo=mod; 
 procesador=micro; 
 memoria=men; 
 frecuencia_micro=fre; 
 } 
 
21 
 
 public void encender(){ 
 if (!isEncendido()) 
 setEncendido(true); 
 
 } 
 
 
 public String getMarca() { 
 return marca; 
 } 
 
 
 public void setMarca(String marca) { 
 this.marca = marca; 
 } 
 
 
 public String getModelo() { 
 return modelo; 
 } 
 
 public void setModelo(String modelo) { 
 this.modelo = modelo; 
 } 
 
 
 public String getProcesador() { 
 return procesador; 
 } 
 
 
 public void setProcesador(String procesador) { 
 this.procesador = procesador; 
 } 
 
 public int getMemoria() { 
 return memoria; 
 } 
 
 public void setMemoria(int memoria) { 
 this.memoria = memoria; 
 } 
 
 public float getFrecuencia() { 
 return frecuencia; 
 } 
 
 public void setFrecuencia(float frecuencia_micro) { 
 this.frecuencia = frecuencia_micro; 
 } 
 
 public boolean isEncendido() { 
 return encendido; 
 } 
 
 public void setEncendido(boolean encendido) { 
 this.encendido = encendido; 
 } 
} 
 
 
22 
 
Es muy importante asignar los valores de los atributos de un objeto siempre por medio 
de los métodos de acceso correspondientes, ya que permiten modificar la estructura 
interna de la clase sin modificar las interfaces de comunicación con las otras clases y, 
por lo tanto, no habrá que modificarlas cuando se realicen cambios que sólo afecten a la 
estructura interna. De esta manera, podemos mantener el principio de encapsulamiento 
y ocultación. 
 
Dentro de la última categoría denominada métodos de servicio agrupamos todos 
aquellos métodos que definen el comportamiento restante de la clase. 
 
 
6.3.4. Mensajes 
 
Los objetos generan un comportamiento enviándoles mensajes, esto es lo que se 
denomina colaboración. 
 
Cuando se crea un programa orientado a objetos, los objetos recibirán, interpretarán y 
responderán a mensajes de otros objetos. Usando la terminología presentada en la 
sección precedente, un mensaje estimula la ocurrencia de cierto comportamiento en el 
objeto receptor. 
 
 
6.4. Ocultación de información .Visibilidad de los atributos y métodos 
 
Al definir una clase se debe establecer el grado de visibilidad u ocultamiento que va 
a tener su estructura respecto al exterior. La visibilidad define por tanto el grado de 
acceso que tendrá una clase respecto a otra. 
Para poder establecer claramente la visibilidad de los atributos y métodos, los lenguajes 
de programación normalmente definen tres niveles de acceso distintos. 
 
 Público (public): cualquier clase puede acceder y utilizar cualquier atributo o 
método declarado como público de otra clase. 
 
 Privado (private): ninguna clase puede acceder a un atributo o método 
declarado como privado ni utilizarlo. 
 
Existe un tercer nivel denominado Protegido (protected): cualquier clase heredera 
puede acceder a cualquier atributo o método declarado como protegido en la clase padre 
y utilizarlo. 
 
 
Por ejemplo la clase java ordenador. 
 
public class Ordenador { 
 private String marca; 
 private String modelo; 
 private String procesador; 
 private int memoria; 
 private float frecuencia_micro; 
 private boolean encendido; 
23 
 
 
 //… 
 
 public String obtenerMarca() { 
 return marca; 
 } 
 
 
 public void asignarMarca(String marca) { 
 this.marca = marca; 
 } 
} 
 
En la clase ordenador del ejemplo anterior sólo se puede acceder al atributo marca a 
través de los métodos obtenerMarca() y asignarMarca() , ya que por defecto todos los 
atributos son prívate(privados) y no pueden ser accedidos desde fuera. 
 
6.5. Propiedades de instancia y propiedades de clase 
 
Como ya hemos mencionado el concepto de clase y objeto aunque están íntimamente 
relacionados, guardan
ciertas diferencias importantes. 
 
A la hora de definir los atributos de una clase se puede especificar que determinados 
atributos y sus valores sean de clase y no de cada objeto, significa que serán 
compartidos y comunes a todos los objetos de una clase y que su valor no depende del 
objeto en particular. Por ejemplo en la case ordenador podríamos definir periodo de 
garantía como nuevo atributo para la clase, pero si nos fijamos bien este atributo es 
compartido por todos los tipos y modelos de ordenadores, luego se podría pensar en el 
él como un atributo de clase, no de instancia. 
 
Ejemplo de una atributo de Clase 
 
public class Ordenador { 
 static int periodo_garantia; 
 private String marca; 
 private String modelo; 
 private String procesador; 
 private int memoria; 
 private float frecuencia_micro; 
 private boolean encendido; 
 
} 
 
Los lenguaje de programación (java y C++) permiten esta construcción através del 
modificador static. 
 
6.6. Los métodos constructor y destructor de una clase 
 
A diferencia de una variable convencional, cuando se define una variable y como tipo 
una clase, está variable no es un objeto hasta que no se construya (necesita tener 
asignados una serie de valores en sus atributos). El compilador necesita saber cómo 
construir y posteriormente destruir los objetos de una clase. 
 
24 
 
Los métodos constructores y destructores de una clase definen como construir un 
objeto y como destruirlo. 
 
Constructor de clase 
 
El objetivo del constructor es el de inicializar un objeto a través de sus atributos 
cuando éste es creado. El siguiente ejemplo muestra la creación de un objeto Circulo 
con un determinado radio. 
 
 
Java 
Circulo c1= new Circulo(10); 
 
C++ 
Circulo *c1= new Circulo(12); 
 
Cuando se crea un objeto (instanciación), se pasan los valores de los parámetros al 
constructor utilizando una sintaxis similar a una llamada a un método. 
 
Un constructor es el mecanismo que disponen las clases para inicializar los objetos. 
 
Cuando se ejecuta la instrucción anterior el compilador reservará memoria para albergar 
un objeto instancia de la clase Circulo y llamará al código asociado el constructor de 
esta clase. 
 
Un constructor se ejecuta cuando se crea una instancia de una clase. El constructor tiene 
como propósito la inicialización de los datos miembro o atributos del objeto de la 
clase. 
 
Podemos ver un constructor como un procedimiento con las siguientes características 
 Es un método obligatorio de la clase 
 Tiene el mismo nombre que la clase 
 Puede tener cualquier número de argumentos (incluso ninguno). 
 Puede haber más de un constructor, es decir distintas maneras de crear un objeto 
 
Formato general de un constructor de clase: 
 
 NombleClase(lista de argumentos) 
 { 
 // Instrucciones de inicialización 
 } 
 
 
Ejemplos de constructores en los distintos lenguajes: 
 
Java 
public class Contador { 
 int n; 
 Contador(){n=0;}; 
 Contador( int v_ini){ 
 n=v_ini; 
 } 
} 
http://es.wikipedia.org/wiki/Objeto_%28programaci%C3%B3n%29
25 
 
 
Ejemplo de constructors en C++ 
 
class Contador 
{ 
 private: 
 int n; 
 public: 
 
 Contador(){ n=0;}; 
 Contador (int v_ini){n=v_ini ;} 
 ~Contador(); 
}; 
 
La construcción de un objeto consta de tres etapas: 
 Se reserva espacio en memoria para la estructura de datos que define la clase. 
 Se inicializa los campos de la instancia con los valores por defecto. Garantiza 
que cada atributo de una clase tenga un valor inicial antes de la llamada al 
constructor 
 Se inicia el código definido por el constructor de clase. 
 
Tipos de constructores 
 
Generalmente existen dos tipos de constructores para una clase 
 Constructor por defecto (sin argumentos/vacio). 
 Constructor con argumentos 
 
Constructor por defecto es aquel que inicializa los atributos de un objeto a su valor 
por defecto. 
 
Constructor con argumentos es un constructor que recibe a través de argumentos los 
valores de inicialización de los atributos del objeto. 
 
En el ejemplo visto anteriormente de la clase contador C++ vemos estos tipos de 
constructores. 
 
Ejemplo en C++ de constructor por defecto y con argumentos 
 
class Contador 
{ 
 private: 
 int n; 
 public: 
 
 Contador(){ n=0;}; //constructor por defecto 
 Contador (int v_ini){n=v_ini ;} //constructor con argumentos 
 ~Contador(); 
}; 
 
Instanciación de la clase Contador 
 
#include <cstdlib> 
#include <iostream> 
#include "contador.h" 
using namespace std; 
 
int main(int argc, char *argv[]) 
26 
 
{ 
 
 Contador *cont1= new Contador(); 
 Contador *cont2=new Contador(2); 
 cont1->incrementar(); 
 cont2->incrementar(); 
 cout<<cont1->getValor(); 
 cout<<cont2->getValor(); 
 system("PAUSE"); 
} 
 
Destructor 
 
Si lo que queremos es eliminar un objeto previamente creado con un constructor habrá 
que llamar directa o indirectamente al método denominado destructor. Aquí también 
existen diferencias notables entre los lenguajes. Mientras C++ requiere una llamada 
explicita al destructor del objeto, otros lenguajes como Java realizan esta tarea de forma 
automática a través de un proceso especial llamado recolector de basura ( garbage 
collector) liberando al programador de esta responsabilidad. Aunque la destrucción se 
puede realizar explícitamente si se desea. 
 
La primera aproximación si se realiza bien garantiza la liberación de memoria y el 
momento de la liberación. La segunda sin embargo libera la memoria pero no ofrece 
garantías de cuando se hará. 
 
 
poligono p1 = new Poligono(10,20,40,15,50,20); 
poligono p2 = new Poligono(10,30,20,40,50,60); 
p1 = p2; //El objeto p1 deja de estar referenciado , puede ser liberado por 
el sistema. 
 
p1 = null;// null: palabra reservada; se utiliza para indicar referencia nula. 
 
El objeto Poligono(10,30,20,40,50,60); deja de estar referenciado 
(identificado), puede ser liberado por el sistema. 
 
 
Destrucción de objetos 
 
Java 
cont=null; 
 
C++ 
delete cont; 
 
 
Las ventajas que se obtienen de una liberación a través de recolector de basura son la 
de una liberación más segura de la memoria reservada, ya es frecuente que los 
programadores no borren explícitamente los objetos creados (por descuido), así se 
consigue un ahorro significativo en el espacio utilizado. 
 
Ejemplo de destrucción explicita en C++ del objeto Contador 
 
#include <cstdlib> 
#include <iostream> 
#include "contador.h" 
using namespace std; 
 
27 
 
int main(int argc, char *argv[]) 
{ 
 
 Contador *cont1= new Contador(); 
 Contador *cont2=new Contador(2); 
 cont1->incrementar(); 
 cont2->incrementar(); 
 cout<<cont1->getValor(); 
 cout<<cont2->getValor(); 
 
 delete cont1; 
 delete cont2; 
 system("PAUSE");} 
Programacion Orientada a Objetos 2015-2016 Campus/Tema 2-parcticas obligatorias 2013_2014 BLOQUE II.pdf
 
Página 1 
 
 
UPSAM 2012-2013 Laboratorio de prácticas 
 
Programación. Tema 2 Bloque II Prácticas obligatorias 
 
Normativa: 
 Las siguientes prácticas son obligatorias para aprobar la asignatura y tienen que ser entregadas 
y explicadas antes de la fecha fijada por el profesor. 
 El material a entregar será un diagrama UML y el código fuente de la práctica en el lenguaje 
requerido. 
 No se corregirán prácticas que no compilen. 
 Las prácticas se realizará de forma individual. 
 
 
 
 
 
Página 2 
 
 
Práctica 1. C++ 
Se desea diseñar un Sistema de colas con prioridades. Para ello considere el siguiente diagrama. 
M
Insertar(
t ) extraer() : t
FIFO
SISTEMA DE COLAS CON PRIORIDADES
COLA PRIORIDAD ALTA
Trabajo{
operacion,
prioridad
}
COLA PRIORIDAD NORMAL
 
Donde 
 M es la capacidad máxima de cada cola (Cola prioridad alta (CPA) y Cola prioridad normal 
(CPN)). Tome M=5 para las prácticas 
 FIFO (First in, First out ) es la disciplina que usa el sistema de gestión de la cola: primero en 
entrar, primero en salir 
 Trabajo representa un trabajo con el nombre de una operación (Ej” OP1” )y una prioridad 
asociada (ALTA o NORMAL) 
Las operaciones permitidas sobre el sistema de colas son: 
 insertar( Trabajo ): Inserta un trabajo en el sistema de colas, si el trabajo es de prioridad ALTA 
irá a la cola denominada CPA, sino a la cola denominada CPN 
 extraer (): Extrae el trabajo a ejecutar en función de la disciplina de cola empleada (en este 
caso FIFO) y considerando que se deben sacar primero los trabajos con prioridad alta. Un 
trabajo cuando es sacado desaparece del sistema 
 estado(): Devuelve el número de mensajes dentro del sistema de colas 
Se pide 
 Diseñe mediante una clase el sistema anterior y realice una representación en UML 
 Implemente en C++ la clase anterior junto con la clase Mensaje 
 Realice la siguiente prueba mediante un programa principal 
o Cree una instancia del sistema de colas 
o Cree tres mensajes: m1( “OP1”,prioridad =alta), m2 ( “OP2”,prioridad =alta), m3( 
“OP3”,prioridad =normal) 
o Envie al sistema multi-cola los tres trabajos en el orden anterior 
o Imprima por pantalla el estado actual del sistema de colas 
o Ejecute la operación extraer dos veces del sistema de colas 
o Imprima por pantalla el estado actual del sistema de colas 
 
 
Página 3 
 
 
 
Práctica 3. Java 
Dentro de la red la Internet, encontramos múltiples dispositivos encargados de gestionar el trasporte de 
la información entre los nodos de la red. Los enrutadores o encaminadores (Routers) se encargan de 
gestionar las múltiples rutas que puede seguir esta información. Un Router es un elemento de red que 
decide que enlace va a seguir el datagrama. Queremos modelar de manera muy simple el 
comportamiento de este componente. Para nosotros un Router tendrá las siguientes características: 
 Dos enlaces de salida. 
o Enlace 1=”193.146.145” 
o Enlace 2=”193.132.128” 
 Un buffer que permite almacenar temporalmente los datagramas entrantes. Con una 
capacidad limitada a N datagramas IP. 
Las operaciones para esta clase serán las siguientes: 
Función Descripción 
almacenar(DatagramaIP) Sólo se aceptarán datagramasIP si 
 
 DataGramaIP direcc_Red_Destino=(Enlace 1 o Enlace2) 
 
o existe capacidad de almacenamiento suficiente 
 
NOTA: devuelve false si no se ha podido almacenar 
 
encaminar Por cada datagramaIP en el buffer 
 
 Si DataGramaIP direcc_Red_Destino =Enlace 1 encaminar por el 
enlace uno 
 
Si DataGramaIP direcc_Red_Destino =Enlace2 encaminar por el 
enlace dos 
 
Sino error interno 
 
El datagramaIP cuando es encaminado cambia su valor de TTL a TTL-1 
 
Nota: IMPRIMA POR PANTALLA el resultado del enrutamiento para 
poderlo verlo después en las pruebas 
 
estado de Buffer Numero de datagramas en el buffer 
 
constructor Parámetros  Enlaces de salida y capacidad máxima 
 
IMPORTANTE 
Represente las direcciones de red de los enlaces como cadenas. 
La capacidad se puede indicar con una constante de la siguiente forma 
private final int CAPACIDAD=3; 
 
Página 4 
 
 
Para representar el buffer use el tipo abstracto Vector de Java SDK 
Se pide: 
1. Diagrama UML de la clase Router 
2. Realice la siguiente prueba sobre la clase Router 
o Crear un Router con la siguiente configuración(Enlace 1=”193.146.145”,Enlace 
2=”193.132.129”,CAPACIDAD=10) 
a. Crear tres datagramas con los siguientes datos 
 IPorigen IPdestino TTL Longitud total DF 
Datagrama1 193.123.134.34 193.130.129.34 3 35000 1 
Datagrama2 193.123.134.34 193.146.145.56 2 34000 0 
Datagrama3 193.123.134.34 193.130.129.58 3 32000 1 
 
 
b. Almacene cada datagrama en el Router 
c. Obtenga el valor de estado del Router 
d. Invocar a la operación encaminar() del Router 
e. Obtenga el valor de estado 
f. Cree ahora una prueba donde se sature la capacidad del router 
 
Programacion Orientada a Objetos 2015-2016 Campus/Tema 2 Prácticas guiadas c__ V2.pdf
Programación 2011-2012 UPSAM 
 
 
Laboratorio de Programación UPSAM 
Tema 2: Práctica guiadas C+++ 
 
 
Nota: Este documento constituye material de apoyo a las prácticas obligatorias que 
posteriormente se deberán entregar. 
 
Proceso de creación de un proyecto de programación en Dev C++ 
1. Crear nuevo proyecto Dev C++ 
 
2. Seleccionar el tipo de aplicación C++ : Aplicación de consola 
 
 
3. Indicamos ahora el directorio se va a guardar el archivo que describe el proyecto (*.dev) 
Programación 2011-2012 UPSAM 
 
 
 
4. Estructura organizativa creada por Dev C++ para nuestro proyecto 
PracticasTema2
 
5. Crear nuevas clases en el proyecto 
 
 
 
Programación 2011-2012 UPSAM 
 
 
Práctica guiada 1. 
Definimos un número racional como un decimal finito o infinito periódico (por ejemplo, el 
número decimal finito 0,75 es la representación decimal del número racional ¾ 
La representación en UML sería la siguiente: 
+visualizar()
+Racional(entrada int num, entrada int den)
-numerador : int
-denominador : int
Racional
 
Clase racional.h 
class Racional 
{ 
 private: 
 int numerador,denominador; 
 public: 
 Racional(int num,int den); 
 void VisualizarRacional(); 
 ~Racional(); 
}; 
 
Racional.cpp 
#include <cstdlib> 
#include <iostream> 
#include "racional.h" 
using namespace std; 
Racional::Racional(int num,int den) 
{ 
 numerador = num; 
 if (den == 0) den = 1; // el denominador no puede ser cero 
 denominador = den; 
} 
void Racional::VisualizarRacional(){ 
cout<<numerador<<"/"<<denominador<<endl; 
Programación 2011-2012 UPSAM 
 
} 
Racional::~Racional() 
{ 
} 
 
Nota: observe como se imprime en pantalla con cout . Para ello es necesario que importe en 
este archivo fuente lo siguiente 
#include <cstdlib> 
#include <iostream> 
using namespace std; 
Utilice la clase Racional en el programa principal (main.cpp) y cree el numero racional (3/4) 
para después visualizarlo en pantalla. 
Main.cpp 
#include <cstdlib> 
#include <iostream> 
#include "racional.h" 
using namespace std; 
int main(int argc, char *argv[]) 
{ 
 Racional *r1=new Racional(3,4); 
 r1->VisualizarRacional(); 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
 
Cuestiones: 
1.-En el método visualizarRacional() existe una dependencia respecto al objeto cout el cual 
representa la salida estándar ( la pantalla). Queremos desacoplar totalmente esta clase de 
su impresión por pantalla. Para ello añada un nuevo método a la clase que obtenga el valor 
decimal del número racional (tipo de dato float) representado e imprima este valor desde 
el programa principal por pantalla. 
 
Programación 2011-2012 UPSAM 
 
Práctica guiada 2 
Represente mediante una clase un Ordenador (marca, procesador, pantalla, indicador de 
encendido o apagado) y diseñe además un método denominado encenderOrdenador() que 
imprimirá por pantalla el mensaje “Ordenador ya está encendido” si ya está encendido y si no 
es así cambiará el valor de la variable de estado para ponerlo en valor “encendido”. 
Diseñe otro método llamado obtenerEstado() que imprima por pantalla el valor de cada uno de 
los atributos. 
La clase debe tener un constructor con tres argumentos: marca, procesador y pantalla. El estado 
inicial del ordenador será de “apagado”. 
La representación en UML de esta clase sería:
+encenderOrdenador()
+obtenerEstado()
-marca : String
-procesador : String
-pantalla : String
-ordenadorEncendido : bool
Ordenador
 
 
ordenador.h 
#include <string> 
using namespace std; 
class Ordenador 
{ 
 private: 
 string marca; 
 string procesador; 
 string pantalla; 
 bool OrdenadorEncendido; 
 public: 
 Ordenador(string mar,string pro,string pan); 
 void encenderOrdenador(); 
 void obtenerEstado(); 
 ~Ordenador(); 
}; 
 
 
Programación 2011-2012 UPSAM 
 
Nota importante: Observa como se ha declarado una cadena en C++ con la clase string 
 string marca; 
 string procesador; 
 string pantalla; 
Para ello hemos importado la librería <string.h> 
 
Programación 2011-2012 UPSAM 
 
 
Ordenador.cpp 
#include "ordenador.h" 
#include <cstdlib> 
#include <iostream> 
 
using namespace std; 
Ordenador::Ordenador(string mar,string pro,string pan) 
{ 
 marca=mar; 
 procesador=pro; 
 pantalla=pan; 
 OrdenadorEncendido=false; 
} 
void Ordenador::encenderOrdenador(){ 
 if (OrdenadorEncendido == true) // si está encendido... 
 cout <<"El ordenador ya está encendido."; 
 else // si no está encendido, encenderlo. 
 { 
 OrdenadorEncendido = true; 
 cout <<"El ordenador se ha encendido."; 
 } 
} 
void Ordenador::obtenerEstado(){ 
 cout<<"Estado del ordenador:" +marca + " Procesador :" +procesador + 
 " Pantalla :" +pantalla<< endl; 
 if (OrdenadorEncendido == true) // si el ordenador está encendido... 
 cout<<"El ordenador está encendido."; 
 else // si no está encendido... 
 cout<<"El ordenador está apagado."; 
 } 
Ordenador::~Ordenador() 
{ 
} 
Programación 2011-2012 UPSAM 
 
 
Nota importante: Observa cómo se concatena una cadena con el operador + en la siguiente 
instrucción 
cout<<"Estado del ordenador:" +marca + " Procesador :" +procesador + 
 " Pantalla :" +pantalla<< endl; 
 
Cuestiones: 
1.-Igual que el ejercicio anterior desacople totalmente la impresión por pantalla en la clase 
ordenador y realice esta tarea en la clase ProgramaPrincipal 
 
Práctica guiada 3. 
Representar un Cuenta Bancaría compuesta por un valor de saldo y un tipo de interés. La cuenta 
permitirá ingresar y retirar dinero, calcular los intereses de abono, cambiar el tipo de interés e 
imprimir el saldo actual de la cuenta. 
A la hora de implementar esta clase hay que tener en cuenta que: 
 Al establecer el tipo de interés se debe considerar que este no puede ser un valor 
negativo. 
 La retirada de dinero sólo se puede hacer si hay saldo suficiente. 
 Los intereses se calculan con la siguiente expresión 
saldo += saldo * tipoDeInteres / 100; 
La representación en UML sería la siguiente: 
+establecerTipo(entrada ti : double)
+ingresarDinero(entrada cantidad : Double)
+retirarDinero(entrada cantidad : double)
+abonarIntereses()
+imprimirSaldoActual() : String
-saldo : double
-tipo : double
CuentaBancaria
 
Diseñe un programa principal que cree una cuenta bancaria con los siguientes movimientos: 
 Ingreso de apertura:1.000.000 
 Abono de intereses 
 Ingreso :500.000 
 Retirada:200.000 
 Abono de intereses 
Y que imprima el saldo resultado resultante. 
cuentabancaria.h 
Programación 2011-2012 UPSAM 
 
#include <cstdlib> 
#include <iostream> 
using namespace std; 
class CuentaBancaria 
{ 
 private: 
 double tipoDeInteres; 
 double saldo; 
 public: 
 CuentaBancaria(); 
 void establecerTipoDeInteres(double ti); 
 void ingresarDinero(double ingreso); 
 void retirarDinero(double cantidad); 
 double saldoActual(); 
 void abonarIntereses(); 
 ~CuentaBancaria(); 
}; 
cuentabancaria.cpp 
#include "cuentabancaria.h" // class's header file 
 
// class constructor 
CuentaBancaria::CuentaBancaria(){ 
 saldo=0.0; 
 tipoDeInteres=0.0; 
} 
 
 void CuentaBancaria::establecerTipoDeInteres(double ti) 
 { 
 if ( ti < 0) 
 { 
 cout<<"El tipo de interés no puede ser negativo"; 
 return; // retornar 
 } 
 tipoDeInteres = ti; 
Programación 2011-2012 UPSAM 
 
 } 
 
 void CuentaBancaria::ingresarDinero(double ingreso) 
 { 
 saldo += ingreso; 
 } 
 
 void CuentaBancaria::retirarDinero(double cantidad) 
 { 
 if ( saldo - cantidad < 0) 
 { 
 cout<<"No tiene saldo suficiente"; 
 return; 
 } 
 // Hay saldo suficiente. Retirar la cantidad. 
 saldo -= cantidad; 
 } 
 
 double CuentaBancaria::saldoActual() 
 { 
 return saldo; 
 } 
 
 void CuentaBancaria::abonarIntereses() 
 { 
 saldo += saldo * tipoDeInteres / 100; 
 } 
// class destructor 
CuentaBancaria::~CuentaBancaria() 
{ 
 // insert your code here 
} 
 
Main.cpp 
Programación 2011-2012 UPSAM 
 
#include <cstdlib> 
#include <iostream> 
#include "cuentabancaria.h" 
using namespace std; 
 
int main(int argc, char *argv[]) 
{ 
 // Abrir una cuenta con 1.000.000 a un 2% 
 CuentaBancaria *Cuenta01 = new CuentaBancaria(); 
 Cuenta01->ingresarDinero(1000000); 
 Cuenta01->establecerTipoDeInteres(2); 
 
 cout.precision(7); 
 cout<<Cuenta01->saldoActual()<<endl; 
 Cuenta01->ingresarDinero(500000); 
 Cuenta01->retirarDinero(200000); 
 cout<<Cuenta01->saldoActual()<<endl; 
 Cuenta01->abonarIntereses(); 
 cout<<Cuenta01->saldoActual()<<endl; 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
 
Práctica guiada 3. 
Cree un fichero de texto usando el objeto ofstream e imprima la cadena “Prueba de escritura en 
archivo” en él. 
#include <cstdlib> 
#include <iostream> 
#include <fstream> 
 
using namespace std; 
int main(int argc, char *argv[]) 
{ 
 //llamada al constructor 
Programación 2011-2012 UPSAM 
 
 ofstream *archivo = new ofstream("c://fichero2.txt", ofstream::out); 
 if (!(*archivo)){ 
 cout << "fallo en apertura" << endl; 
 return -1; 
 } 
 (*archivo) << "prueba de escritura de fichero "; //escritura en el archivo de texto 
 archivo->close(); //cierre del archivo 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
 
Nota importante: Observe cómo se utiliza el objeto ofstream. 
 La escritura se realiza de igual manera que con cout 
(*archivo) << "prueba de escritura de fichero "; 
 
 
Programacion Orientada a Objetos 2015-2016 Campus/Anexo II Tema 4 Herencia clases abstractas y poliformismo C++.pdf
1 
 
Tema 4. 
Anexo I. Estudio de la implementación de la herencia, 
clases abstractas y polimorfismo en C++. 
Para ver como se implementa la herencia, clases abstractas y el polimorfismo vamos a utilizar 
el dominio de información de cuentas bancarias empleado en el Tema 2. En nuestra práctica del 
Tema 2, nuestro programa manipulaba una clase denominada CuentaBancaria. La clase 
CuentaBancaria se muestra en el siguiente diagrama UML. 
+ingresar(entrada cantidad : double)
+obtenerSaldo() : double
+obtenerCodigoCuenta() : String
+abonarIntereses()
-saldo : double
#tipoInteres : double
#titular : String
#codigo : String
CuentaBancaria
 
Supongamos que necesitamos manejar dos tipos de cuentas bancarias: CuentaAhorro y 
CuentaRemunerada. Las características de cada cuenta son las siguientes: 
 Cuenta Remunerada 
o En este tipo de cuenta se abonan intereses siempre independientemente del 
saldo 
 CuentaAhorro 
o En este tipo de cuenta se abonan intereses sólo sí el saldo es superior a 3000 
euros. 
Como se puede observar el método abonarIntereses() tiene un comportamiento que está en 
función del tipo de cuenta.
Es decir es diferente para cada subclase. Este método debe ser 
sobrescrito en cada subclase a fin de dar la implementación adecuada. 
Analizando estas clases obtenemos el siguiente diagrama de clases UML 
+ingresar(entrada cantidad : double)
+obtenerSaldo() : double
+obtenerCodigoCuenta() : String
+abonarIntereses()
-saldo : double
#tipoInteres : double
#titular : String
#codigo : String
CuentaBancaria
+abonarIntereses()
CuentaAhorro
+abonarIntereses()
CuentaRemunerada
 
 
2 
 
1. Implementación de la Generalización/especialización en C++. 
La definición de la clase base CuentaBancaria.h es la siguiente: 
#include <stdlib.h> 
#include <string> 
using namespace std; 
class CuentaBancaria 
{ 
 private: 
 double saldo; 
 protected: 
 string titular; 
 double tipoInteres; 
 string codigo; 
 //Constructor 
 CuentaBancaria(double saldo, string titular, string codigo, double tipoInteres) { 
 this->saldo = saldo; 
 this->titular = titular; 
 this->codigo = codigo; 
 this->tipoInteres = tipoInteres; 
 } 
 public: 
 //Método para ingresar dinero en la cuenta 
 void ingresar(double cantidad){saldo+=cantidad;} 
 //Método para obtener el saldo 
 double obtenerSaldo(){return saldo;} 
 //Método para obtener el código de identificación 
 string obtenerCodigoCuenta(){return codigo;} 
 //Método abstracto que necesita ser sobrescrito por cada subclase 
 virtual void abonarIntereses()=0; 
 ~CuentaBancaria(); 
}; 
Observe con atención el siguiente método de la clase CuentaBancaria 
virtual void abonarIntereses()=0; 
3 
 
El método se define como abstracto (palabra reservada virtual ) y no se le proporciona cuerpo, 
esto indica que no tiene ninguna implementación (su cuerpo esta vacio ) y se indica con ”=0”, 
la responsabilidad de la implementación de este método corresponde a cada subclase. 
Importante: Debido a que CuentaBancaria define un método abstracto la clase en sí se 
convierte en una clase abstracta: Una clase abstracta en C++ es aquella que al menos define una 
función virtual pura. 
 
1.1. Intento de instancia de una clase abstracta 
 
Si intentamos realizar una instancia de la clase CuentaBancaria el compilador nos avisara que 
no se puede instanciar una clase abstracta. 
int main(int argc, char *argv[]) 
{ 
 CuentaBancaria *c001=new CuentaBancaria(100,"Gustavo","C00023",3.2); 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
1.2. Definición de una subclase en C++ 
 
Sintaxis general: 
 class <NombreSubclase> : <NombreClaseBase>{ 
//Cuerpo de la clase 
} 
El carácter “:” indica que la clase <NombreSubclase> hereda de la clase denominada 
<NombreClaseBase>. 
Subclase CuentaAhorro.h 
#include <string> 
#include "cuentabancaria.h" 
using namespace std; 
class CuentaAhorro : CuentaBancaria { 
 public: 
 CuentaAhorro(double saldo, string titular, string codigo, double tipoInteres); 
 void abonarIntereses(); 
 ~CuentaAhorro(); 
}; 
4 
 
CuentaAhorro.cpp 
#include "cuentaahorro.h" 
CuentaAhorro::CuentaAhorro(double saldo, string titular, string codigo, double 
tipoInteres):CuentaBancaria(saldo,titular,codigo,tipoInteres) 
{ 
} 
void CuentaAhorro::abonarIntereses(){ 
 //Abonar intereses sólo si el saldo es superior a 3000 euros 
 if (this->obtenerSaldo()>=3000.0){ 
 double cant=this->obtenerSaldo()*(tipoInteres/100); 
 this->ingresar(cant); 
 } 
} 
CuentaAhorro::~CuentaAhorro() 
{ 
} 
 
Definición Subclase CuentaRemunerada.h 
#include "cuentabancaria.h" 
class CuentaRemunerada: CuentaBancaria 
{ 
public: 
CuentaRemunerada(double saldo, string titular, string codigo, double tipoInteres); 
void abonarIntereses(); 
 ~CuentaRemunerada(); 
}; 
CuentaRemunerada.cpp 
#include "cuentaremunerada.h" 
CuentaRemunerada::CuentaRemunerada(double saldo, string titular, string codigo, double 
tipoInteres):CuentaBancaria(saldo,titular,codigo,tipoInteres) 
{ 
} 
void CuentaRemunerada::abonarIntereses() 
{ 
 //Abonar intereses siempre 
5 
 
 double cant=this->obtenerSaldo()*(tipoInteres/100); 
 this->ingresar(cant); 
} 
CuentaRemunerada::~CuentaRemunerada() 
{ 
} 
Prueba de la subclase CuentaAhorro y CuentaRemunerada 
#include <cstdlib> 
#include <iostream> 
#include "cuentabancaria.h" 
#include "cuentaahorro.h" 
#include "cuentaremunerada.h" 
using namespace std; 
int main(int argc, char *argv[]) 
{ 
 CuentaAhorro *c001=new CuentaAhorro(1000,"Luis Eduardo Abad Diaz","C00023",3.2); 
 CuentaRemunerada *c002=new CuentaRemunerada(3000,"Alfonso Garcia Ruiz","C0043",5.0); 
 //ingresar dinero en cuentas 
 c001->ingresar(1000); 
 c002->ingresar(3000); 
 //abonar intereses en cuentas 
 c001->abonarIntereses(); 
 c002->abonarIntereses(); 
 cout<<"saldo cuenta:"<<c001->obtenerCodigoCuenta()<<": "<<c001-
>obtenerSaldo()<<endl; 
 cout<<"saldo cuenta:"<<c002->obtenerCodigoCuenta()<<": "<<c002-
>obtenerSaldo()<<endl; 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
Salida: 
saldo cuenta:C00023: 2000.0 
saldio cuenta:C0043: 6300.0 
 
Como se puede comprobar la subclase ha heredado los atributos y métodos de la clase base 
CuentaBancaria 
6 
 
1.3. Análisis de miembros heredados por una subclase 
 
Las subclases CuentaAhorro y CuentaRemunerada han heredado atributos y comportamiento 
de la clase base CuentaBancaria. Además cada subclase deber proporcionar una 
implementación al método abonarIntereses().Pero ¿Qué hereda exactamente la subclase 
CuentaAhorro y CuentaRemunerada? La siguiente tabla muestra un resumen de todo lo 
heredado por cada subclase. 
Atributos Métodos 
private double saldo; 
protected String titular; 
protected String codigo; 
protected double tipoInteres; 
void ingresar(double cantidad) 
double obtenerSaldo() 
String obtenerCodigoCuenta() 
 
Obligación de sobrescribir el método abonarIntereses() 
 Tabla herencia CuentaBancaria y subclases 
IMPORTANTE: Los constructores de una clase base no son heredados por las subclases. Como 
se ve en el constructor de la clase base la inicialización correcta de una subclase implica 
inicializar la clase base primero. Esto se realiza con la siguiente llamada a super(lista 
parametros). 
CuentaRemunerada::CuentaRemunerada(double saldo, string titular, string codigo, double 
tipoInteres):CuentaBancaria(saldo,titular,codigo,tipoInteres) 
{ 
} 
Para instanciar una clase base desde una subclase en general utiliza la siguiente sintaxis: 
public <NombreSubclase>(lista parámetros):ContructorClaseBase 
{ 
//Cuerpo del constructor de la subclase 
} 
1.4. Acceso a los miembros de una clase base 
 
Los métodos de la subclase no tienen acceso a los miembros privados de la clase base, pero sí 
a los miembros protegidos y públicos. Por ejemplo en el método abonarIntereses() de la 
subclase CuentaRemunerada no tiene acceso directo al atributo saldo ya que este es declarado 
privado (prívate) en la clase base CuentaBancaria. Para acceder al atributo saldo la subclase 
debe usar el método obtenerSaldo() como se observa en el siguiente fragmento. 
void CuentaRemunerada::abonarIntereses() 
{ 
 //Abonar intereses siempre 
 double cant=this->obtenerSaldo()*(tipoInteres/100); 
 this->ingresar(cant); 
} 
7 
 
Sin embargo la subclase si tiene acceso al atributo tipoInteres de la clase base ya que este es 
declarado como protegido (protected). 
Importante: La restricción prívate en los atributos de una clase base puede sorprender pero 
es necesaria para mantener la propiedad
de encapsulación de la información. Si se pudiese tener 
acceso a los miembros privados de una clase, simplemente se conseguiría heredando. De esta 
forma se cumple que el interfaz de una clase es el único medio de manipulación de la clase. 
Luego como regla general es un mejor establecer el modificador private para los atributos de 
una clase base y que las subclases accedan a ellos a través de una interfaz 
Tabla general de visibilidad de miembros entre clases base y subclases: 
 Un miembro declarado en una clase como 
Puede ser accedido desde privado predeterminado protegido público 
Su misma clase Sí Sí Sí Sí 
Cualquier clase o subclase de su mismo 
paquete 
No Sí Sí Sí 
Cualquier clase de otro paquete No No No Sí 
Cualquier subclase de otro paquete No No Sí Sí 
 
Tabla. Javier Ceballos (Java 2) 
1.5. Estudio del uso del polimorfismo en C++ 
 
En ejemplo anterior declarábamos una clase base denominada CuentaBancaria. Esta clase era 
abstracta por que definía un método virtual puro (=0) que no tenía implementación virtual void 
abonarIntereses(). La implementación adecuada de abonarIntereses() se proporciona por cada 
una de las subclases que heredan de esta clase base. 
Vamos a suponer que tenemos una clase denominada ControlIntereses que expone un único 
método denominado abonarInteresesCuentas() con una lógica de negocio como lo siguiente: 
Todos los días uno de cada mes se procede a abonar los interés correspondientes a cada una de 
las cuentas existente en el banco. 
El diseño de clases en UML podría ser el siguiente: 
+ingresar(entrada cantidad : double)
+obtenerSaldo() : double
+obtenerCodigoCuenta() : String
+abonarIntereses()
-saldo : double
#tipoInteres : double
#titular : String
#codigo : String
CuentaBancaria
+abonarIntereses()
CuentaAhorro
+abonarIntereses()
CuentaRemunerada
+abonarInteresesCuentas()
ControlIntereses *1
 
El método abonarInteresesCuentas() tiene algunas consideraciones de diseño importantes: 
1. La lógica de control de este método considera todas las cuentas existentes de manera 
homogénea y a para todas aplica el mismo tratamiento abonarIntereses(). 
8 
 
2. La lógica de abonoIntereses() es propia de cada subclase e independiente de este 
método. 
3. Si en el futuro se añade una nueva cuenta este modulo no debería sufrir cambios. 
Definición de la clase ControladorIntereses.h 
#include <list.h> 
#include "cuentaBancaria.h" 
class ControladorIntereses 
{ 
 private: 
 list<CuentaBancaria *> cuentas; 
 public: 
 
 ControladorIntereses(); 
 void registrarCuenta(CuentaBancaria *cb); 
 void abonarInteresesCuentas(); 
 ~ControladorIntereses(); 
}; 
ControladorIntereses.cpp 
 
#include "controladorintereses.h" 
#include "cuentabancaria.h" 
ControladorIntereses::ControladorIntereses() 
{ 
} 
void ControladorIntereses::registrarCuenta(CuentaBancaria *cb){ 
 
 cuentas.push_front(cb); 
} 
void ControladorIntereses::abonarInteresesCuentas() 
{ 
 //itereador_lista se posiciona al principio de la lista 
 list<CuentaBancaria *>::iterator iterador_lista=cuentas.begin(); 
 CuentaBancaria *cb; 
 //Recorrer cada elemento de la lista 
 for (;iterador_lista!=cuentas.end();iterador_lista++){ 
9 
 
 cb=*iterador_lista; //Asignar el contenido del iterador 
 cb->abonarIntereses(); 
 } 
} 
ControladorIntereses::~ControladorIntereses() 
{ 
} 
Ahora probamos el polimorfismo 
#include <cstdlib> 
#include <iostream> 
#include "cuentabancaria.h" 
#include "cuentaahorro.h" 
#include "cuentaremunerada.h" 
#include "controladorintereses.h" 
using namespace std; 
 
int main(int argc, char *argv[]) 
{ 
 //crear cuentas bancarias 
 CuentaAhorro *ca001=new CuentaAhorro(2000,"Luis Eduardo Abad Diaz","CA001",3.2); 
 CuentaAhorro *ca002=new CuentaAhorro(4000,"Manuel García Solis","CA002",3.2); 
 CuentaRemunerada *cr001=new CuentaRemunerada(3000,"Alfonso Garcia 
Ruiz","CR003",5.0); 
 
 //crear el controlador 
 ControladorIntereses *ctrl= new ControladorIntereses(); 
 //Registrar cada una de las cuentas en el controlador 
 ctrl->registrarCuenta(ca001); 
 ctrl->registrarCuenta(ca002); 
 ctrl->registrarCuenta(cr001); 
 //abonar intereses en todas las cuentas 
 ctrl->abonarInteresesCuentas(); 
 //Comprobar el pago de intereses 
 cout<<"saldo cuenta:"<<ca001->obtenerCodigoCuenta()<<": "<<ca001-
>obtenerSaldo()<<endl; 
10 
 
 cout<<"saldo cuenta:"<<ca002->obtenerCodigoCuenta()<<": "<<ca002-
>obtenerSaldo()<<endl; 
 cout<<"saldo cuenta:"<<cr001->obtenerCodigoCuenta()<<": "<<cr001-
>obtenerSaldo()<<endl; 
 system("PAUSE"); 
 return EXIT_SUCCESS; 
} 
Programacion Orientada a Objetos 2015-2016 Campus/Anexo I Tema 4 Herencia_clases abstractas y polimorfismo java.pdf
1 
 
Tema 4. 
Anexo I. Estudio de la implementación de la herencia, 
clases abstractas y polimorfismo en Java. 
Para ver como se implementa la herencia, clases abstractas y el polimorfismo vamos a utilizar 
el dominio de información de cuentas bancarias empleado en el Tema 2. En nuestra práctica del 
Tema 2, nuestro programa manipulaba una clase denominada CuentaBancaria. La clase 
CuentaBancaria se muestra en el siguiente diagrama UML. 
+ingresar(entrada cantidad : double)
+obtenerSaldo() : double
+obtenerCodigoCuenta() : String
+abonarIntereses()
-saldo : double
#tipoInteres : double
#titular : String
#codigo : String
CuentaBancaria
 
Supongamos que necesitamos manejar dos tipos de cuentas bancarias: CuentaAhorro y 
CuentaRemunerada. Las características de cada cuenta son las siguientes: 
 Cuenta Remunerada 
o En este tipo de cuenta se abonan intereses siempre independientemente del 
saldo 
 CuentaAhorro 
o En este tipo de cuenta se abonan intereses sólo sí el saldo es superior a 3000 
euros. 
Como se puede observar el método abonarIntereses() tiene un comportamiento que está en 
función del tipo de cuenta. Es decir es diferente para cada subclase. Este método debe ser 
sobrescrito en cada subclase a fin de dar la implementación adecuada. 
Analizando estas clases obtenemos el siguiente diagrama de clases UML 
+ingresar(entrada cantidad : double)
+obtenerSaldo() : double
+obtenerCodigoCuenta() : String
+abonarIntereses()
-saldo : double
#tipoInteres : double
#titular : String
#codigo : String
CuentaBancaria
+abonarIntereses()
CuentaAhorro
+abonarIntereses()
CuentaRemunerada
 
 
2 
 
1. Implementación de la Generalización/especialización en Java 
La definición de la clase base CuentaBancaria,java es la siguiente: 
public abstract class CuentaBancaria { 
private double saldo; 
protected String titular; 
protected double tipoInteres; 
protected String codigo; 
 //Constructor 
 public CuentaBancaria(double saldo, String titular, String codigo, double 
tipoInteres) { 
 this.saldo = saldo; 
 this.titular = titular; 
 this.codigo = codigo; 
 this.tipoInteres = tipoInteres; 
 } 
 //Método para ingresar dinero en la cuenta 
 public void ingresar(double cantidad){ 
 saldo+=cantidad; 
 } 
 //Método para obtener el saldo 
 public double obtenerSaldo(){return saldo;} 
 //Método para obtener el código de identificación 
 public String obtenerCodigoCuenta(){return codigo;} 
 //Método abstracto que necesita ser sobrescrito por cada subclase 
 abstract void abonarIntereses(); 
} 
Observe con atención el siguiente método de la clase CuentaBancaria 
abstract void abonarIntereses(); 
El método se define como abstracto (abstract), esto indica que no tiene ninguna 
implementación
(su cuerpo esta vacio), luego la responsabilidad de la implementación 
corresponde a cada subclase. 
Importante: Debido a que CuentaBancaria define un método abstracto debe declararse como 
abstracta (abstract class) 
 
3 
 
1.1. Intento de instancia de una clase abstracta 
 
Si intentamos realizar una instancia de la clase CuentaBancaria el compilador nos avisara que 
no se puede instanciar una clase abstracta. 
public class ProgramaPrincipal { 
 public static void main(String[] args) { 
 //El intento de una instancia de una clase abstracta dará error 
 CuentaBancaria c001=new CuentaBancaria(100,"Gustavo","C00023",3.2); 
 } 
} 
1.2. Definición de una subclase en Java. 
 
Sintaxis general: 
public class <NombreSubclase> extends <NombreClaseBase>{ 
//Cuerpo de la clase 
} 
La palabra reservada extends indica que la clase <NombreSubclase> hereda de la clase 
denominada <NombreClaseBase>. 
Por defecto toda clase en Java es una subclase de la clase base denominada Object. 
Subclase CuentaAhorro.java 
public class CuentaAhorro extends CuentaBancaria { 
 //Constructor 
 public CuentaAhorro(double saldo, String titular, String codigo, double tipoInteres) 
{ 
 super(saldo, titular, codigo, tipoInteres); 
 } 
 void abonarIntereses() { 
 //Abonar intereses sólo si el saldo es superior a 3000 euros 
 if (this.obtenerSaldo()>=3000.0){ 
 double cant=this.obtenerSaldo()*(tipoInteres/100); 
 this.ingresar(cant); 
 } 
 } 
} 
 
4 
 
Definición Subclase CuentaRemunerada.java 
public class CuentaRemunerada extends CuentaBancaria{ 
 //Constructor 
 public CuentaRemunerada(double saldo, String titular, String codigo, double 
tipoInteres) { 
 super(saldo, titular, codigo, tipoInteres); 
 } 
 public void abonarIntereses(){ 
 //Abonar intereses siempre 
 double cant=this.obtenerSaldo()*(tipoInteres/100); 
 this.ingresar(cant); 
 } 
} 
Prueba de la subclase CuentaAhorro 
public class ProgramaPrincipal { 
 public static void main(String[] args) { 
 CuentaAhorro c001=new CuentaAhorro(1000,"Luis Eduardo Abad Diaz","C00023",3.2); 
 CuentaRemunerada c002=new CuentaRemunerada(3000,"Alfonso Garcia 
Ruiz","C0043",5.0); 
 //ingresar dinero en cuentas 
 c001.ingresar(1000); 
 c002.ingresar(3000); 
 //abonar intereses en cuentas 
 c001.abonarIntereses(); 
 c002.abonarIntereses(); 
 //ver los saldos 
 System.out.println("saldo cuenta:"+c001.obtenerCodigoCuenta()+": 
"+c001.obtenerSaldo()); 
 System.out.println("saldo cuenta:"+c002.obtenerCodigoCuenta()+": 
"+c002.obtenerSaldo()); 
 } 
} 
Salida: 
saldo cuenta:C00023: 2000.0 
saldio cuenta:C0043: 6300.0 
 
5 
 
Como se puede comprobar la subclase ha heredado los atributos y métodos de la clase base 
CuentaBancaria 
1.3. Análisis de miembros heredados por una subclase 
 
Las subclases CuentaAhorro y CuentaRemunerada han heredado atributos y comportamiento 
de la clase base CuentaBancaria. Además cada subclase deber proporcionar una 
implementación al método abonarIntereses().Pero ¿Qué hereda exactamente la subclase 
CuentaAhorro y CuentaRemunerada? La siguiente tabla muestra un resumen de todo lo 
heredado por cada subclase. 
Atributos Métodos 
private double saldo; 
protected String titular; 
protected String codigo; 
protected double tipoInteres; 
void ingresar(double cantidad) 
double obtenerSaldo() 
String obtenerCodigoCuenta() 
 
Obligación de sobrescribir el método abonarIntereses() 
 Tabla herencia CuentaBancaria y subclases 
IMPORTANTE: Los constructores de una clase base no son heredados por las subclases.Como 
se ve en el constructor de la clase base la incialización correcta de una subclase implica 
inicializar la clase base primero.Esto se realiza con la siguiente llamada a super(lista 
parametros). 
public CuentaAhorro(double saldo, String titular, String codigo, double tipoInteres) { 
 super(saldo, titular, codigo, tipoInteres); 
 } 
Para instanciar una clase base desde una subclase en general utiliza la siguiente sintaxis: 
public <NombreSubclase>(lista parámetros){ 
 super(lista parámetros); 
//Cuerpo del constructor de la subclase 
} 
1.4. Acceso a los miembros de una clase base 
 
Los métodos de la subclase no tienen acceso a los miembros privados de la clase base, pero sí 
a los miembros protegidos y públicos. Por ejemplo en el método abonarIntereses() de la 
subclase CuentaRemunerada no tiene acceso directo al atributo saldo ya que este es declarado 
privado (prívate) en la clase base CuentaBancaria. Para acceder al atributo saldo la subclase 
debe usar el método obtenerSaldo() como se observa en el siguiente fragmento. 
public void abonarIntereses(){ 
 //Abonar intereses siempre 
 double cant=this.obtenerSaldo()*(tipoInteres/100); 
 this.ingresar(cant); 
6 
 
 } 
Sin embargo la subclase si tiene acceso al atributo tipoInteres de la clase base ya que este es 
declarado como protegido (protected). 
Importante: La restricción prívate en los atributos de una clase base puede sorprender pero 
es necesaria para mantener la propiedad de encapsulación de la información. Si se pudiese tener 
acceso a los miembros privados de una clase, simplemente se conseguiría heredando. De esta 
forma se cumple que el interfaz de una clase es el único medio de manipulación de la clase. 
Luego como regla general es un mejor establecer el modificador private para los atributos de 
una clase base y que las subclases accedan a ellos a través de una interfaz 
Tabla general de visibilidad de miembros entre clases base y subclases: 
 Un miembro declarado en una clase como 
Puede ser accedido desde privado predeterminado protegido público 
Su misma clase Sí Sí Sí Sí 
Cualquier clase o subclase de su mismo 
paquete 
No Sí Sí Sí 
Cualquier clase de otro paquete No No No Sí 
Cualquier subclase de otro paquete No No Sí Sí 
 
Tabla. Javier Ceballos (Java 2) 
1.5. Estudio del uso del polimorfismo en Java 
 
En ejemplo anterior declarábamos una clase base denominada CuentaBancaria. Esta clase era 
abstracta por que definía un método que no tenía implementación abstract void 
abonarIntereses(). La implementación adecuada de abonarIntereses() se proporciona por cada 
una de las subclases que heredan de esta clase base. 
Vamos a suponer que tenemos una clase denominada ControlIntereses que expone un único 
método denominado abonarInteresesCuentas() con una lógica de negocio como lo siguiente: 
Todos los días uno de cada mes se procede a abonar los interés correspondientes a cada una de 
las cuentas existente en el banco. 
El diseño de clases en UML podría ser el siguiente: 
+ingresar(entrada cantidad : double)
+obtenerSaldo() : double
+obtenerCodigoCuenta() : String
+abonarIntereses()
-saldo : double
#tipoInteres : double
#titular : String
#codigo : String
CuentaBancaria
+abonarIntereses()
CuentaAhorro
+abonarIntereses()
CuentaRemunerada
+abonarInteresesCuentas()
ControlIntereses *1
 
El método abonarInteresesCuentas() tiene algunas consideraciones de diseño importantes: 
7 
 
1. La lógica de control de este método considera todas las cuentas existentes de manera 
homogénea y a para todas aplica el mismo tratamiento abonarIntereses(). 
2. La lógica de abonoIntereses() es propia de cada subclase e independiente de este 
método. 
3. Si en el futuro se añade una nueva cuenta este modulo no debería sufrir cambios. 
Definición de la clase ControladorIntereses.java 
import java.util.LinkedList; 
 
public class ControladorIntereses { 
 LinkedList cuentas; 
 public ControladorIntereses()
{ 
 this.cuentas =new LinkedList(); 
 } 
 public void registrarCuenta(CuentaBancaria cb){ 
 cuentas.add(cb); 
 } 
 public void abonarInteresesCuentas(){ 
 CuentaBancaria cb=null; 
 //Obtener nuemro de cuentas 
 int tam=cuentas.size(); 
 
 for (int i=0;i<tam;i++){ 
 cb=(CuentaBancaria)cuentas.get(i); 
 //Por cada cuenta abonar intereses 
 cb.abonarIntereses(); 
 } 
 } 
} 
 
Ahora probamos el polimorfismo 
public class ProgramaPrincipal { 
 
 public static void main(String[] args) { 
 //crear cuentas bancarias 
 CuentaAhorro ca001=new CuentaAhorro(2000,"Luis Eduardo Abad Diaz","CA001",3.2); 
8 
 
 CuentaAhorro ca002=new CuentaAhorro(4000,"Manuel García Solis","CA002",3.2); 
 CuentaRemunerada cr001=new CuentaRemunerada(3000,"Alfonso Garcia 
Ruiz","CR003",5.0); 
 
 //crear el controlador 
 ControladorIntereses ctrl= new ControladorIntereses(); 
 //Registrar cada una de las cuentas en el controlador 
 ctrl.registrarCuenta(ca001); 
 ctrl.registrarCuenta(ca002); 
 ctrl.registrarCuenta(cr001); 
 //abonar intereses en todas las cuentas 
 ctrl.abonarInteresesCuentas(); 
 //Comprobar el pago de intereses 
 System.out.println("saldo cuenta:"+ca001.obtenerCodigoCuenta()+": 
"+ca001.obtenerSaldo()); 
 System.out.println("saldo cuenta:"+ca002.obtenerCodigoCuenta()+": 
"+ca002.obtenerSaldo()); 
 System.out.println("saldo cuenta:"+cr001.obtenerCodigoCuenta()+": 
"+cr001.obtenerSaldo()); 
 
} 
} 
Programacion Orientada a Objetos 2015-2016 Campus/Tema_5._Tratamiento_de_errores_excepciones_v3.pdf
Tema 5 
 
 
Tratamiento de 
errores con 
excepciones 
 
 
 
 
 
 
 
 
 
 
UPSAM 2010-2011 
 
 
 
 
Contenido 
1. Concepto de Excepción ......................................................................................................... 3 
2. Manejo de excepciones. ........................................................................................................ 3 
3. Tratamiento de Excepciones ................................................................................................. 4 
4. Lanzamiento de Excepciones ................................................................................................ 5 
5. Manejadores de excepciones ................................................................................................ 6 
6. Declaración de Excepciones .................................................................................................. 8 
 
 
 
 
 
 
 
1. Concepto de Excepción 
Las Excepciones son anomalías (condiciones de error no esperadas) producidas durante la 
ejecución de un programa. Por ejemplo división por cero, punteros a zonas de memoria 
ilegales, punteros no inicializados correctamente, acceso a una zona de memoria no permitida, 
índices de arrays fuera de rango, errores de E/S,etc. 
Normalmente las excepciones suspenden la ejecución del programa, emitiendo en el sistema 
el mensaje de error correspondiente. Los lenguajes orientado a objetos como C++ y Java 
tienen construcciones específicas para tratar con excepciones. Cuando un programa detecta 
una excepción, se notifica al resto del sistema elevando (raising) o lanzando (throwing) un 
excepción. En alguna parte existe un código especifico que maneja y captura (catch) la 
excepción. 
De la misma forma que el concepto de Clase ha cambiado la forma de organizar y programar, 
las excepciones han cambiado la forma de manejar las condiciones de error en el flujo de 
control de los programas. 
2. Manejo de excepciones. 
Muchos lenguajes de programación como C, incorporan un tratamiento de errores basado en 
el valor de retorno de las funciones. Así el valor devuelto por las funciones se utiliza para 
determinar si una función se ha ejecutado satisfactoriamente (Por ejemplo si devuelve -1 
indica error y en otro caso es interpretado como ejecución correcta). 
Ejemplo. Fragmento de código y tratamiento de errores tradicional. 
int codError=0; 
codError=leerArchivo(archivo); 
if (codigoError!=0){ 
switch(codigoError) 
} 
case -1: //No se encontró el archivo 
 //… 
 break; 
case -2: //El archivo está corrupto 
 break; 
case -3://El dispositivo no está listo 
break; 
default://… 
} 
} 
else{ 
//procesar los datos leídos 
} 
El problema de esta forma de tratamiento es que el código de tratamiento está mezclado junto 
con el flujo normal. El tratamiento de errores con excepciones es un método que clasifica los 
tipos de errores y estructura el código de una manera más clara de cara a realizar este 
tratamiento. 
3. Tratamiento de Excepciones 
El modelo de excepciones en la mayoría de los lenguajes de programación orientados a 
objetos se basa en los siguientes mecanismos: lanzar (throw) y capturar (catch): 
1. Cuando se detecta una excepción, el programa lanza (throw) una excepción. El 
lanzar una excepción hace que el programa realice un salto antes de continuar la 
ejecución. 
2. La excepción se captura (catch) mediante un manejador de excepciones (handler). 
Los manejadores de excepciones comienzan con la palabra catch y se declaran al final 
de un bloque try ( en C++ y Java). 
En resumen un código bajo este mecanismo puede lanzar una excepción directamente o 
indirectamente, en un bloque try utilizando la expresión throw. La excepción se maneja 
invocando un manejador adecuado seleccionado de una lista de manejadores encontrados 
inmediatamente después del bloque try . De forma general un bloque de código con 
tratamiento de excepciones tiene la siguiente forma general. 
try{ 
//El código de este bloque puede producir alguna excepción 
} 
catch( TipoExcepción1 e){ 
//Código de tratamiento de la excepción 
}… 
catch (TipoExcepcionN e){ 
} 
La estructura en C++ y Java exige que cualquier excepción que ocurra, debe ser lanzada 
dentro de un bloque try. El bloque try es una sentencia que especifica el conjunto de 
sentencias que al ejecutarse podrían lanzar una excepción y si es así que manejadores 
tratarían las excepciones. 
El manejador de excepciones es el bloque de código diseñado para manejar una condición 
anómala y excepcional. Debería haber al menos un manejador por cada tipo de excepción que 
se pueda lanzar. 
El manejo de excepciones como hemos dicho antes ofrece una forma de separar 
explícitamente el código que trata las condiciones de error y el código de control de flujo 
normal de la aplicación haciéndola más legible. El siguiente fragmento de código muestra el 
tratamiento de los errores del ejemplo anterior con el mecanismo de excepciones. 
try{ 
 //Código de acceso y tratamiento de archivo 
 leerArchivo(archivo); 
} catch(ArchivoDesconocido e){ 
 //Tratamiento si el archivo es desconocido 
}catch(DispositivoError e){ 
 //Tratamiento si el dispositivo da algún tipo de error 
}catch(ArchivoCorrupto e){ 
 //Tratamiento si el archivo esta dañado 
} 
4. Lanzar una Excepción (throw) 
Las excepciones se lanzan utilizando la sentencia throw 
throw tipoexcepcion; 
Donde tipoexcepción es una expresión que determina el tipo de la excepción.Este tipo es 
cualquier tipo válido en el lenguaje .Este objeto se utiliza para inicializar la variable del 
manejador de expresiones (clausula catch). 
Ejemplo: 
// exceptions_trycatchandthrowstatements.cpp 
// compile with: /EHsc 
#include <iostream> 
using namespace std; 
int main() { 
 char *buf; 
 try { 
 buf = new char[512]; 
 if( buf == 0 ) 
 throw "Memory allocation failure!"; 
 } 
 catch( char
* str ) { 
 cout << "Exception raised: " << str << '\n'; 
 } 
} 
El operando throw es sintácticamente similar a una sentencia return 
 En esencia los pasos que se siguen son los siguientes: 
1. Se lanza la excepción 
2. Se busca un manejador adecuado en función de su orden de aparición y se comprueba 
su tipo (cada manejador tiene un tipo diferente ) 
3. Si el manejador se encuentra, el control del programa se transfiere al manejador 
4. Si no se encuentra ningún manejador, se elevará esta al bloque try superior si lo 
hubiere si no el programa terminará la ejecución. 
 
5. Manejadores de excepciones 
Las excepciones lanzadas dentro de un bloque son capturadas usando clausulas catch 
asociadas a un bloque try. 
5.1. Bloques try-catch 
Un bloque try -catch tiene la siguiente sintaxis: 
try{ 
//Sentencias 
}catch(Exceppcion1 e){} 
catch(Exceppcion2 e){} 
catch(ExceppcionN e){} 
Un bloque try es un contexto para indicar cual son la instrucciones a considerar y los 
manejadores que se invocarían si se produjesen determinadas anomalías en esas 
instrucciones. 
El orden en el que se especifican los manejadores determina el orden en que se prueban los 
mismos cuando se lanza una excepción .Siempre existe un bloque try junto a un bloque de 
manejadores catch. 
La clausula catch es muy similar a una declaración de función de un argumento sin tipo de 
retorno. 
Una excepción se captura cuando su tipo coincide con el tipo de la sentencia catch. Una vez 
que existe correspondencia de tipos, el control del programa se transfiere al manejador. El 
manejador especifica que acciones deben realizarse para tratar la anomalía del programa. 
Ejemplo en C++ 
class desbordamiento{ 
public: 
desbordamiento(); 
}; 
float división(float a ,float b) 
{ 
 if (b==0) throw “division por 0”; 
 if (a>LIMITE1 && b<LIMITE2) throw new desbordamiento(); 
 return a/b; 
} 
void main() 
{ 
Try{ 
division(5,3); 
}catch( char *e){ 
Cout<<e<<endl; 
}catch(desbordamiento *e){ 
} 
} 
 
Ejemplo 2 
//Recibe cierto valor numérico 
 
try{ 
 
if (n % 2 == 1){ 
// n es impar 
 throw new ArithmeticException(); 
}else 
 // Código normal para valores pares 
}catch (ArithmeticException e){ 
 
// Tratar el caso especial de valores impares 
} 
 
 
6. Declaración de Excepciones 
Sintácticamente una especificación de excepciones es parte de la declaración de funciones y 
tiene el siguiente formato. 
tipo_ retorno nombre_funcion( parámetros) throw lista tipos de excepciones 
 
Consideraciones: 
 La lista de excepciones es la lista de tipos que una expresión throw puede tener en la 
función. 
 Cualquier función o bloque de código que invoque esta función deberá 
obligatoriamente capturar estos tipos de excepciones. 
Ejemplo1 .C++ 
void escrituraDisco throw (DeviceError,WritError); 
… 
void enviarMail throw (direcc_incorrecta,fallo_sistema); 
 
Ejemplo 2.Java 
public escribirArchivo( String nombre_fichero) throws IOException 
 
public class Conexion{ 
 
public Conexion(Stringurl) throws RedNoDisponible, ServidorNoEncontrado, 
RecursoNoDisponible 
{ … 
} 
 
publicStringleerLinea() throws RedNoDisponible 
{ 
 …} 
 
public void cerrar() 
{ …} 
} 
 
 
Programacion Orientada a Objetos 2015-2016 Campus/Tema 2-parcticas obligatorias 2013_2014 BLOQUE I.pdf
 
Página 1 
 
 
UPSAM 2012-2013 
 
Programación. Tema 2 Prácticas obligatorias 
 
Normativa: 
 Las siguientes prácticas son obligatorias para aprobar la asignatura y tienen que ser entregadas 
y explicadas antes de la fecha fijada por el profesor. 
 El material a entregar será un diagrama UML y el código fuente de la práctica en el lenguaje 
requerido. 
 No se corregirán prácticas que no compilen. 
 Las prácticas se realizará de forma individual. 
 
 
 
 
 
Página 2 
 
 
Práctica 1. Java 
Represente un Semáforo de Tráfico mediante una clase. El estado del semáforo podrá ser ROJO, 
AMBAR y VERDE. La clase deberá permitir cambiar el estado interno del semáforo mediante una única 
operación denomina cambiar estado. Esta operación modificará en cada llamada el estado interno del 
semáforo de la siguiente forma (VERDE->AMBAR->ROJO VERDE…). Además la clase semáforo debe 
definir una operación que permita obtener el valor del estado actual. 
Se pide 
 Representación UML de la clase Semáforo 
 Diseñe una clase que represente un semáforo (use un tipo enum para representar el estado) 
 El estado inicial del semáforo debe ser VERDE 
 Mediante un programa principal pruebe la siguiente secuencia 
o Crear un semáforo, cambiar su estado interno a ROJO, mostrar por pantalla el estado 
interno del semáforo 
NOTA: Para esta práctica investigue como declarar y usar un tipo enum en java 
Práctica 2. Java 
Represente un Servidor mediante una clase. Para ello considere la siguiente información 
 Un servidor escucha solicitudes de conexión en un puerto determinado (Ej 8080) y en una 
dirección IP (Ej 10.16.1.20 use una cadena para representarla) 
 El nombre del servidor representa el servicio que ofrece (Ej servidor de correo, servidor de 
archivos, servidor de base de datos, etc). 
 Un servidor mantiene internamente un recuento del número de hilos activos en cada momento 
 La capacidad máxima del servidor ( en número de hilos) viene definida por la constante 
MAX_CON ( que se configura al crear el servidor) 
 Cada hilo se corresponde con una solicitud de servicio activa. El servidor no atenderá 
solicitudes si ha llegado al límite de su capacidad MAX_CON 
Se pide: 
 Representación en UML de la clase Servidor 
 Implemente en Java la clase Servidor 
 Defina en la clase Servidor la operación solicitar servicio, cuyo efecto es el de incrementar en 
uno el número de hilos activos que están procesando solicitudes dentro del servidior o 
devuelve -1 si es rechazada 
 Defina en la clase Servidor la operación estadoCapacidad que devuelve la capacidad actual en 
cada momento 
 Realice un programa principal que basándose en la clase Servidor realice lo siguiente 
 
o Crear dos servidores : servidor base de datos (SBD), servidor de archivos(SA) con 
capacidades máximas de 3 y 5 hilos respectivamente 
o Realizar 4 solicitudes de servicio al servidor de base de datos y 2 al de archivos 
o Mostrar por pantalla la capacidad actual de cada servidor 
 
 
Página 3 
 
 
 
Práctica 3. Java 
Se requiere que la Cuenta Bancaria (ver prácticas guiadas) tenga asociada además información de la 
nómina del cliente (sólo será necesario la cantidad mensual cobrada) de manera que se pueda calcular 
el riesgo de determinadas operaciones. Para ello se incorpore a la cuenta el método calcularRiesgo 
(float cuota-prestamo) que devolverá el grado de riesgo que tiene el préstamo a un cliente en función 
de su nómina. La lógica de este método es la siguiente: 
 Si <cuota_prestamo> representa el 50% o más de la nómina ,el riesgo es alto 
 Si <cuota_prestamo> representa menos del 50% y más del 31% ,el riesgo es medio 
 Si <cuota_prestamo> representa 30% o menos ,el riesgo bajo 
 
Se pide: 
 Representación en UML de la clase CuentaBanacaria 
o Diseñe un programa principal que dada una CuentaBancaria (saldo=100 
,interés=0.0.3,nomina=1000) 
o Que solicite por teclado una cuota y compruebe el riesgo que representa esa 
cantidad frente a la nómina de la cuenta indicada. 
Practica 4.C++ 
Diseñe una clase C++ que represente una Calculadora. La calculadora es un poco especial y debe 
permitir realizar lo siguiente: 
 Realizar operaciones aritméticas :suma, resta, división ,multiplicación 
 Tienen una operación denominada deshacerOperacion que deshará sólo la última operación 
realizada.
 La operación reset. Que establece a cero el estado interno de la calculadora. 
 Para ver el resultado de la operación se utilizará un método denominado verestado que 
devuelve el valor de la calculadora tras la última operación realizada. 
IMPORTANTE 
El estado interno de la calculadora debe poder representar la última operación realizada y el 
resultado antes de hacer esta última operación. 
La calculadora opera con números reales (tipo double) 
Todas las operaciones aritméticas son procedimientos. 
Para ver el resultado hay que invocar al método verResultado() 
Considerando que se trabaja con una notación infija <operando1> operaciónX <operando2>. La clase 
calculadora debe definir dos prototipos diferentes para las funciones aritméticas: 
 operacionX(operando1,operando2) : 
o Si el estado interno es diferente de 0 se pone a 0 antes de hacer la operación 
o La función resuelve internamente la expresión <operando1> operaciónX 
<operando2> 
 
Página 4 
 
 
 operaciónX(operando2): Este método considera el estado interno de la última operación 
como operando 1 . 
 X representa las operaciones posibles que 
serán:suma(SUM),resta(RES),multiplicación(MUL),división(DIV) 
Veamos a continuación todo esto con un ejemplo .Si queremos calcular ((1+5)-3) la secuencia de 
invocación de operaciones sobre la calculadora será: 
 Operación sumar 1 y 5 
 Operación restar 3 //Toma el resultado almacenado internamente como operando1 y 
le suma 3. 
 Ver el resultado //Mostraría 3 
 Deshacer operación //Devolvería 6 
 Deshacer operación // Sigue devolviendo 6 no deshace 1+5 
Nota importante: Deshacer operación sólo puede mostrar el resultado anterior a la última llamada de 
operacionX realizada que en el ejemplo anterior sería operacionRES(3). 
Se pide: 
 Diagrama UML de la clase Calculadora 
 Un programa principal que pruebe la siguiente secuencia de operaciones 
o (((((((5+1)-3)deshacerOP)+6)/4)deshacerOP)+12)  resultado 24 
 
Práctica 4. Java 
Un datagrama IP es una unidad de transferencia y organización en las redes de comunicación IP .El 
datagrama IP se divide en campos de control (cabecera) y datos .La siguiente figura muestra el detalle 
de su estructura. 
Versión Longitud de cabecera Tipo de Servicio Longitud total 
Identificador Flags Offset 
TTL Protocolo Checksum de cabecera 
Dirección IP origen del datagrama 
Dirección IP destino del datagrama 
Opciones Relleno 
 
 
Datos 
Figura. Estructura de un datagrama IP 
Considerando sólo la parte de control, nos centraremos sólo en los siguientes campos: 
Campo de 
control 
Descripción y observaciones 
Longitud total: Tamaño en bytes del datagrama(cabecera y datos). Máximo valor 65535 octetos 
Flag DF Simplificamos este campos a los siguiente: 
o Si vale DF=0 significa que se puede fragmentar 
o Si vale DF=1 significa que no se puede fragmentar 
 
Dirección IP 
origen y destino 
Una dirección IP es una etiqueta numérica que identifica, de manera lógica y 
jerárquica, a una interfaz (elemento de comunicación/conexión) de un dispositivo 
http://es.wikipedia.org/wiki/Interfaz_de_red
 
Página 5 
 
 
(DIR.IP ORIGEN Y 
DIR.IP.DESTINO) 
 
(habitualmente una computadora) dentro de una red que utilice el protocolo IP 
(Internet Protocol), A través de Internet, los ordenadores se conectan entre sí 
mediante sus respectivas direcciones IP. 
Ejemplo de dirección IP destino 192.146.123.45 
o IMPORTANTE :Suponiendo siempre una red de clase C tenemos 
que 192.146.123 es la dirección de red 
TTL 
 
Time to live .Indica el máximo número de enrutadores que un paquete puede 
atravesar. Cada vez que algún nodo procesa este paquete disminuye su valor en 1 
como mínimo, una unidad. 
 
NOTA IMPORTANTE 
Las direcciones IP serán representadas con tipo cadena s (Ej “192.156.124.45”) 
Para obtener la red de la dirección IP investigue las funciones lastIndexOf(…) y substring(…) de la clase 
String en Java.Para ello considere que la dirección de red de la IP “192.156.124.45” sería “192.156.124” 
La clase Datagrama deberá contemplar la siguiente funcionalidad: 
 Construir un Datagrama con todos y cada uno de los campos 
 Que se pueda modificar el estado de la propiedad TTL del datagrama. 
 Obtener el valor de longitud del datagrama. 
 Y comprobar si el paquete se puede fragmentar. El método devolverá true si se puede y false 
en otro caso 
 
Se pide: 
 clase UML de la clase DatagramaIP 
 Realice una clase denominada ProgramaPrincipal que realice lo siguiente: 
o Construya un datagrama (46000,1,”10.16.0.2”,”10,16.0.15”,5) 
o Cambie el estado del su TTL a 4. 
o Imprima por pantalla si el datagrama de puede fragmentar. 
o Imprimir por pantalla la dirección de red de la dirección origen 
http://es.wikipedia.org/wiki/Computadora
http://es.wikipedia.org/wiki/Red_de_computadoras
http://es.wikipedia.org/wiki/Protocolo_de_Internet
Programacion Orientada a Objetos 2015-2016 Campus/T4 paracticas obligatorias parte 1v4 corregida.pdf
1 
 
UPSAM 2011-2012 Laboratorio de prácticas 
 
Programación. Prácticas obligatorias 
 
Normativa: 
 Las siguientes prácticas son obligatorias para aprobar la asignatura y tienen que ser entregadas y 
explicadas antes de la fecha fijada por el profesor. 
 El material a entregar será un diagrama UML por cada clase que se diseñe y el código fuente de 
la práctica en el lenguaje requerido. 
 No se corregirán prácticas que no compilen. 
 Las prácticas son individuales. 
 
 
 
2 
 
Práctica 1.Java 
Un programa de dibujo simple trabaja con una serie de tipos de figuras geométricas como son el Circulo y el 
Cuadrado .Se debe poner calcular sobre cuqluier tipo de Figura el valor del área 
Se pide: 
a) Diagrama de clases UML 
b) Una Clase ProgramaPrincipal que defina el siguiente método prototipo : 
private void calcularArea(…..){ 
//obtener el área de la figura e imprimirla por pantalla 
} 
c) En el método main() realice lo siguiente: 
 Crear un cuadrado lado=4 
 Crear un circulo con radio=6 
 obtener el área del circulo y cualdrado através del método calcularArea(..) 
mencionado anteriormente. 
Práctica 2. Java 
Un módulo de control de tareas ejecuta diversos tipos de tareas. Una Tarea se define a partir de un script o 
guion (cadena) que indica las instrucciones que hay que ejecutar, un valor de prioridad (valor 1..3) y un 
GUID (numérico). El módulo trabaja fundamentalmente con dos tipos de Tareas: Tarea Planificada y 
Tarea Normal. Las características de cada tipo de tarea son las siguientes. 
 Tarea Normal: Cuando el modulo de control la ejecute (llamada a ejecutar ()) , se ejecutará siempre 
que se cumpla la condición GUID=0 y prioridad > 2 dentro de la tarea. 
 Tarea Planificada: Es una tarea que tiene una fecha de ejecución (día (numérico) y mes 
(numérico)), y cuando el modulo de control la ejecute sólo se ejecutará si su fecha interna 
(previamente establecida) coincide con la fecha del instante de ejecución. Si se intenta ejecutar la 
tarea fuera de esa fecha sencillamente no pasará nada. 
 
Un ejemplo de la tarea de planificación podría ser día=1 mes=12 lo que indicaría que la tarea sólo se 
ejecutaría dentro del módulo si ocurre el 1 de diciembre. 
 
 Todas las tareas tienen además un indicador de estado(0/1) que indica si la tarea ya ha sido ejecutada 
estado=1 o no luego estado=0. 
La clase modulo de control de tareas dispone de dos métodos: registrar(Tarea) y ejecutarTareas(). El 
primer método registra una tarea dentro del módulo para ser ejecutada posteriormente y el segundo método 
ejecuta todas y cada una de las tereas registradas hasta ese momento, además cambia el estado interno de cada 
tarea ( 1 si se ejecuto o 0 sino). 
Se pide: 
 Modele la relación de generalización /especialización
de las tareas del enunciado así como la 
asociación de estas con la clase módulo de control. 
 Cree una clase ProgramaPrincipal que 
o Cree un array de tamaño 3 
o Cree 1 Tarea Ordinaria (T1) y 2 Tareas Planificadas (T2,T3) 
o Establezca la fecha en la que tiene que ejecutar la T2 (Ejemplo fecha de hoy) y T3 (día 
tres del mes que viene). 
o Registre las tareas en el módulo de control de tareas 
o Ejecute todas las tareas y compruebe el estado de cada tarea. Muestre por pantalla este 
estado 
3 
 
Consideraciones sobre la práctica: 
 Para obtener el día y el mes se debe usar la clase GregorianCalendar (import java.util.Calendar y 
import java.util.GregorianCalendar) . El siguiente fragmento de código permite obtener el día y mes 
actual. Utilice este código para la Tarea Planificada 
GregorianCalendar fechaActual=new GregorianCalendar();//crea un objeto representando la fecha actual 
int diaMes=fechaActual.get(Calendar.DAY_OF_MONTH);//Obtiene el día del mes actual 
int Mes=fechaActual.get(Calendar.MONTH);//Obtiene el mes del año actual 
 El atributo script es de tipo cadena y tiene el siguiente formato: 
“I1:comando1,I2: comando2,…”. 
 Se considera que el comportamiento del método ejecutar de una tarea es el siguiente 
 Prototipo String ejecutar() 
o devuelve la cadena “EJECUCIÓN : script ={ I1:comando1,I2:comando2}” si la 
ejecución puede realizarse o la cadena “ NO ES POSIBLE LA EJECUCIÓN” en 
otro caso. 
Práctica 3.C++ 
Un sistema Domótico usa los siguientes tipos de sensores entre otros: Humedad y Temperatura. En general un 
Sensor se caracteriza por las siguientes propiedades: 
 Un identificador de dispositivo( cadena “ID00N”) 
 Lectura (numérico ) 
 Unidades de interpretación de la lectura (HumendadRelativa % ,Temperatura(Celsius) (cadena) 
 Estado actual (cadena) 
De manera general un Sensor se puede activar, leer. Los estados posibles para cada tipo de sensor se 
muestran en la tabla siguiente: 
Tipo de sensor Estado (inicial) Estados posibles 
Sensor Humedad “Operativo” “Error”,”Operativo” 
Sensor Temperatura “Operativo” “Error”,”Operativo”,”Perdida 
Contacto” 
 
Cada sensor implementa la operación activar() cambia el estado del sensor en de la siguiente forma: 
 Sensor Humedad: Estado =”Operativo” 
 Sensor Temperatura: Estado =”Operativo” 
La operación double leer() de cada sensor tiene el siguiente comportamiento 
 Sensor de Humedad y Sensor Temperatura: Si el sensor está en estado “Operativo” devuelve la 
última lectura obtenida en otro caso -1. 
Además se dispone de una clase denominada Detector que permite obtener el vector de estado a través de 
un método denominado obtenerEstado(), de todos los sensores registrados(método registrar(Sensor)) .la 
clase Detector además implementa un método denominado activarSensores() que activa todos y cada uno 
de los sensores registrado. 
 
4 
 
Se pide : 
 Cree un modelo UML de las clases implicadas en el ejemplo 
 Cree una clase ProgramaPrincipal que en su método main realice lo siguiente: 
o instancie 2 objetos de tipo Sensor, uno de cada tipo. 
o Instanciar una objeto Detector 
o Registrar los dos sensores en el ámbito del módulo Detector a través del método 
registrar (Sensor )). 
o Y realizar la siguiente operación con el objeto Detector : 
 Activar todos los sensores. 
 Obtener el vector de estado 
Como ejemplo considere la siguiente salida en el programa principal 
<ID001:Estado:”Operativo”:Lectura:13.4, 
ID002:Estado:”Error”:Lectura:_, 
ID003:Estado:”Operativo”:Lectura:30:” ,….> 
 
Programacion Orientada a Objetos 2015-2016 Campus/TEMA 1 Introduccion Programacion clásica y POOv2.pdf
 1 
 
Tema 1 
 
 
Programación clásica 
y Programación 
Orientada a Objetos 
 
 
 
 
 
 
 
 
 
 
 
 2 
 
 
Índice de contenidos 
 
 
1. Programación clásica y crisis de software ............................................................................. 3 
2. La orientación a objetos ........................................................................................................ 4 
3. Reutilización del código ........................................................................................................ 6 
4. Programación y abstracción .................................................................................................. 8 
5. Lenguajes de programación orientados a objetos ................................................................ 10 
5.1. Clasificación de los lenguajes de programación ......................................................... 12 
5.1.1. Lenguajes de primera generación ........................................................................ 12 
5.1.2. Lenguajes de segunda generación ....................................................................... 12 
5.1.3. Lenguajes de tercera generación ......................................................................... 13 
5.1.4. Lenguajes de cuarta generación o actuales .......................................................... 13 
6. Lenguajes Orientados a Objetos ..................................................................................... 14 
6.1. Características básicas de los lenguajes de programación orientados a objetos .......... 14 
7. Bibliografía ..................................................................................................................... 16 
 
 
 
 
 
 3 
 
 
1. Programación clásica y crisis de software 
 
 A finales de los años 60, en una conferencia organizada por la Comisión Científica de 
la OTAN se reconoció abiertamente que había un problema en el desarrollo de los 
sistemas software. Las aplicaciones que se construían, o bien no llegaban nunca a 
completarse con éxito, o bien, además de ser muy costosas, y de estar mal estructuradas 
eran prácticamente imposibles de mantener, como consecuencia la mayoría de las 
aplicaciones eran poco fiables. Esta situación fue denominada crisis del software. 
 
Las causas de esta crisis tienen el origen en la complejidad intrínseca de las 
aplicaciones software, que deriva fundamentalmente de cuatro elementos que se 
pueden identificar en aplicaciones de mediana y gran envergadura: 
 
 La complejidad de gestionar el proceso de desarrollo: Normalmente el equipo 
de desarrollo no suele tener claro desde el primer momento qué se espera de la 
aplicación, ya que los usuarios no siempre consiguen transmitir sus necesidades 
y expectativas. Esto obliga a realizar multitud de cambios en los requisitos 
durante el desarrollo, lo cual dificulta considerablemente este proceso. Si la 
captura de requisitos fuera eficiente y el usuario los pudiera verificar mediante 
prototipos en fases tempranas del desarrollo, esta dificultad y el coste respectivo 
se reducirían. 
 
 La complejidad del dominio y de la solución hace que haya que descomponer 
la aplicación en gran cantidad de módulos y, por lo tanto, que se requieran 
grandes equipos de desarrollo. Hay que tener en cuenta que hablamos de 
aplicaciones que modelan el mundo real. Esto comporta la dificultad de 
gestionar un equipo de desarrollo con muchos miembros, manteniendo una 
unidad e integridad del diseño de la aplicación. 
 
 Ausencia de estándares: casi siempre que se construye una aplicación, se 
identifican partes que se podrían resolver de manera parecida a como se hizo en 
aplicaciones anteriores, pero la ausencia de estándares en la industria del 
software hace que aquello que ya se ha implementado sea difícilmente 
reutilizable. 
 
 Caracterizar el comportamiento de sistemas discretos es difícil. Estos 
sistemas pueden tener un número muy grande de estados. Por este motivo, las 
pruebas no se pueden considerar nunca completas, ya que es prácticamente 
imposible simular
todas las situaciones por las que puede pasar el sistema. 
 
Esta complejidad, unida a la falta de una metodología a la hora de desarrollar las 
aplicaciones, hacía que los desarrollos basados en las estructuras de datos clásicas 
presentaran los siguientes inconvenientes: 
 
 Limitaciones en el modelado de problemas no estructurados, ya que el 
método utilizado para construir las aplicaciones está basado en el hecho de 
descomponer la aplicación en una jerarquía de módulos funcionales ideados para 
transformar unas entradas determinadas en salidas bien definidas. Este tipo de 
 4 
 
diseños no facilitan resolver problemas complejos desestructurados. El enfoque 
tradicional, se trata de un enfoque más apropiado para resolver problemas 
estructurados, en los que se puede describir el código según algoritmos de 
transformación de datos. 
 
 Dificultad a la hora de reutilizar el código, ya que los módulos se 
caracterizaban por la transformación de unos datos en concreto, cosa que los 
hacía muy dependiente de la aplicación original. 
 
 Mantenimiento difícil y costoso, ya que los módulos estaban muy orientados a 
tratar los datos y normalmente, durante su desarrollo, no se tenían en cuenta 
posibles cambios futuros. 
 
 Reducción de la calidad de las aplicaciones, medida en flexibilidad, eficiencia, 
fiabilidad y robustez, criterios que trataremos en el módulo siguiente. La calidad 
se reduce a medida que se hacen modificaciones, ya que éstas complican cada 
vez más el diseño original, con lo cual se resta flexibilidad al desarrollo y cada 
vez resulta más complejo introducir nuevos cambios. 
 
2. La orientación a objetos 
 
La orientación a objetos se podría definir como el conjunto de disciplinas que 
desarrollan y modelan sistemas software a partir de componentes (objetos), lo que 
facilita la construcción de sistemas complejos. 
 
Podemos decir que la orientación a objetos es una nueva forma de resolver problemas 
de estructuración y representación más ajustada al mundo real y, por lo tanto, más 
próxima al proceso mental de las personas. 
 
La orientación a objetos está basada en tres aspectos organizativos próximos al 
razonamiento humano, ya que establecen conexiones entre los siguientes elementos: 
 
 Un objeto y sus características; por ejemplo, un coche tiene un color, un 
número de puertas, un tipo de motor, etc. 
 
 Un objeto y otros objetos con los que se asocia o interactúa (que al mismo 
tiempo se relacionan con otros objetos); por ejemplo, el coche tiene ruedas (con 
unas características propias), un motor, etc. 
 
 Un objeto general y otros objetos que comparten sus mismas propiedades 
pero más específicos; por ejemplo, un coche puede ser deportivo o familiar. 
 
 
La programación orientada a objetos es una técnica para construir aplicaciones más 
antigua de lo que puede parecer. Si bien pasó desapercibida hasta principios de la 
década de los noventa, su origen se remonta a 1967, cuando dos noruegos, Ole-Johan 
Dahl y Kristen Nygaard, idearon los conceptos básicos de la programación orientada a 
objetos tal y como se conoce hoy. 
 
 5 
 
El atractivo de la orientación a objetos es que proporciona conceptos y herramientas 
con las cuales se modela, de forma a aproximada a como entendemos la realidad, 
es decir basada en objetos con propiedades. 
Como apuntaban Ledbetter y Cox (1985). 
La programación orientada a objetos permite una representación más directa del 
modelo de mundo real en el código. El resultado es que la transformación llevada 
a cabo de los requisitos del sistema (definidos en términos del usuario) a la 
especificación del sistema (definido en términos del la computadora) se reduce 
considerablemente. 
Teniendo en cuenta todo lo que hemos explicado hasta ahora, podemos decir que la 
orientación a objetos va más allá de la implementación de aplicaciones. Podemos decir 
que define toda una filosofía para analizar y diseñar sistemas dinámicos. 
 
Antes de empezar a implementar cualquier problema, es necesario examinar qué 
requisitos existen y desde la perspectiva de la programación orientada a objetos usar 
clases y objetos que aproximen el dominio del problema. Una vez obtenido el modelo 
del problema, será necesario refinar y adaptar el modelo al lenguaje de programación 
que se utilizará, dado que no todos los lenguajes implementan los conceptos de la 
programación orientada a objetos de la misma manera. 
 
A continuación, mostraremos un ejemplo que nos permitirá comprobar las diferencias 
entre el desarrollo orientado a objetos y el desarrollo procedimental. 
 
Problema 
Tenemos que hacer una aplicación para gestionar las nóminas de los profesores de una facultad y la 
dotación económica que recibe cada departamento. 
En la facultad, cada profesor imparte docencia en una asignatura, cobra de acuerdo con las horas 
impartidas y pertenece a un departamento. 
Según la carga docente, los departamentos tienen una dotación económica u otra. Para nuestra aplicación, 
necesitaremos saber los datos que modelan los departamentos (nombre, director, secretario, dirección 
postal, los profesores que hay asignados, etc.) y una operación que nos diga cuál es la dotación económica 
de los departamentos (para evaluarla, necesitaremos saber el número de horas de docencia de todos sus 
profesores). 
De los profesores, aparte de los datos identificadores (el nombre y los apellidos, el despacho, el correo 
electrónico, etc.), necesitaremos poder consultar el nombre de las asignaturas que imparten y las horas de 
docencia que realizan en estas asignaturas. 
De las asignaturas, habrá que saber el nombre y las horas de docencia. 
 
Solución con el desarrollo clásico 
 
Por una parte, tendríamos que definir las estructuras de datos que necesitaríamos en nuestra aplicación 
(departamento, profesor y asignatura) y, para cada una de éstas, deberíamos definir una estructura de 
datos para almacenarlas todas. 
Después necesitaríamos una estructura de datos que nos permitiera representar las relaciones entre los 
departamentos y los profesores, y otra para representar la relación entre los profesores y las asignaturas. 
Finalmente, tendríamos que implementar las operaciones que nos permitieran realizar las funcionalidades 
pedidas, y también otras operaciones que nos sirviesen para “navegar” entre las relaciones almacenadas. 
 
 
Solución con el desarrollo orientado a objetos 
 6 
 
 
En este caso, lo que tendríamos que hacer es identificar cada entidad (departamento, profesor y 
asignatura) y utilizar tres módulos diferentes en los que se definirían las propiedades de cada entidad y se 
implementarían las funcionalidades por separado. 
Por ejemplo, en la operación del departamento de cálculo de las horas lectivas (para calcular la 
asignación), en vez de tener que navegar por toda la estructura de datos para saber si un profesor es o no 
del departamento, puesto que cada departamento ya está 
relacionado con sus profesores, sólo haría falta pedirles las horas lectivas y sumarlas. 
 
Una vez definidas estas entidades en nuestra aplicación de gestión, tendríamos tantos objetos de tipo 
Profesor como profesores haya, cada uno de los cuales con los datos que lo definen, y tantos objetos 
de tipo Departamento como departamentos haya en el sistema. 
En este caso, no sería necesaria una estructura que representara las relaciones, ya que los objetos ya están 
relacionados entre sí. 
 
A la vista del ejemplo anterior, podríamos decir que la orientación a objetos permite 
agrupar las funcionalidades referentes a un mismo tipo de objeto en una misma 
entidad y, por lo tanto, localizar posibles problemas es mucho más rápido y hacer 
modificaciones es más sencillo. 
 
Algunos argumentos que podríamos esgrimir de forma rápida para usar
la programación 
orientada a objetos son la mejora de la calidad de las aplicaciones, la escalabilidad y 
adaptabilidad de éstas, y también la disminución del tiempo y el coste de desarrollo. 
Gran parte de estas ventajas provienen de otra fundamental: el concepto de reutilización 
del código, que veremos en el apartado siguiente. 
 
3. Reutilización del código 
 
 
Cuando se construye un automóvil, o un dispositivo electrónico, se ensamblan una serie 
de piezas independientes, Existen piezas que se diseñan una vez y se reutilizan para 
distintos modelos, en vez de fabricarlos cada vez que se necesita construir un nuevo 
circuito u automóvil. En la construcción de software surge la cuestión. ¿Por qué no se 
utilizan componentes ya construidos para formar parte de otros sistemas? 
 
Las técnicas orientadas a objetos proporcionan un mecanismo para construir 
componentes de software reutilizables que posteriormente podrán ser interconectados 
entre sí para formar grandes proyectos de software. Esta propiedad de poder utilizar 
elementos de software programados y probados previamente durante la construcción de 
aplicaciones nuevas se denomina reutilización o reusabilidad. 
 
Podemos resumir las ventajas principales que se obtienen de reutilizar un código de la 
siguiente forma: 
 
 Disminución del esfuerzo de mantenimiento. Ya que se utilizan componentes 
previamente probados en otros sistemas. Además puesto que todo el código que 
afecta a una funcionalidad concreta está en un mismo módulo, las tareas de 
mantenimiento se basan en modificaciones específicas y lo que es má 
importante están acotadas. 
 
 Mayor velocidad en el desarrollo de aplicaciones, favorecida principalmente 
por el hecho de poder reutilizar componentes totalmente funcionales . 
 7 
 
 Aumento de la fiabilidad de los programas. El módulo reutilizable habrá 
superado pruebas de funcionamiento previas, con lo que su fiabilidad habrá sido 
comprobada. 
 
 Abaratamiento de costes favorecido por el hecho de no tener querealizar 
nuevos desarrollos, ya que se pueden aprovechar de proyectos anteriores. 
 
Para ejemplarizar mejor el concepto de reutilización de código, podemos hablar de las 
interfaces gráficas de usuario. Esta colección de elementos que utilizamos cuando 
desarrollamos aplicaciones gráficas (menús, botones, cuadros de texto, etc.) no los 
programamos cada vez que realizamos un proyecto, sino que los tomamos de un 
repositorio que los creadores del sistema operativo o el lenguaje de programación nos 
ofrecen. Si los tuviéramos que implementar nosotros, aparte de que deberíamos tener un 
gran conocimiento sobre este tema, necesitaríamos mucho más tiempo, ya que estos 
elementos los utilizamos muchas veces en nuestro programa. 
 
El desarrollo de los elementos de las interfaces gráficas ha sido realizado por un grupo 
de programadores una única vez, y todos nosotros sacamos provecho del mismo. Por lo 
tanto, podemos decir que, en este caso, aprovechamos los siguientes aspectos de la 
programación orientada a objetos: 
 
 Eficiencia de nuestra aplicación para tratar la interfaz gráfica, ya que suponemos 
que estos elementos son lo más eficientes posible. 
 
 Fiabilidad de la interfaz gráfica, ya que estos elementos están muy probados y, 
por lo tanto, no presentarán un comportamiento inesperado. 
 
 Consistencia de la interfaz de usuario, dado que todos los elementos tienen unas 
características parecidas. 
 
 Más velocidad de desarrollo y menos costes. Como hemos comentado, estos 
elementos ya nos vienen dados y, por lo tanto, ahorramos tiempo y dinero. 
 
 Eliminación de los costes de mantenimiento, ya que estos elementos, puesto 
que ya están hechos, no necesitan –ni pueden– ser modificados. 
 
 8 
 
 
 
4. Programación y abstracción 
 
El proceso de abstracción es común a todas las ciencias, y como vemos forma parte del 
proceso de razonamiento humano.Las personas normalmente comprenden el mundo 
construyendo modelos de partes del mismo: un modelo mental es una vista simplificada 
de cómo funcionan las cosas. Los modelos abstraen las características básicas o 
esenciales para un propósito particular y desechan las irrelevantes. A este proceso se 
denomina abstracción. 
 
Veamos la definición de abstracción del Diccionario de la Real Academia Española 
(D.R.A.E). 
abstracción. 
(Del lat. abstractĭo, -ōnis). 
1. f. Acción y efecto de abstraer o abstraerse. 
abstraer. 
(Del lat. abstrahĕre). 
1. tr. Separar por medio de una operación intelectual las cualidades de un objeto para 
considerarlas aisladamente o para considerar el mismo objeto en su pura esencia o 
noción. 
2. intr. Prescindir, hacer caso omiso. Abstraer DE examinar la naturaleza de las cosas. 
U. t. c. prnl. 
 
La abstracción es esencial para el funcionamiento de la mente humana normal y es una 
herramienta muy potente para tratar la complejidad. 
En general un programa de ordenador no es más que una descripción abstracta de 
un procedimiento o fenómeno que sucede en el mundo real. La relación entre 
abstracción y lenguaje de programación es doble: por un lado se utiliza el lenguaje de 
programación para escribir un programa que es una abstracción del mundo real; por otro 
lado se utiliza el leguaje de programación para describir de un modo abstracto el 
comportamiento físico de la computadora que se está utilizando (Ej usando números 
decimales en lugar de números binarios, variables en lugar de celdas de memoria 
direccionadas directamente). 
Como describe Wulf: << Los humanos hemos desarrollado una técnica 
excepcionalmente potente para tratar la complejidad: abstraernos de ella. Incapaces de 
dominar en su totalidad los objetos complejos, se ignora los detalles no esenciales, 
 9 
 
tratando en su lugar con el modelo ideal del objeto y centrándonos en el estudio de sus 
aspectos esenciales>>. 
Algunas reglas interesantes para realizar abstracciones en programación orienta a 
objetos son las siguientes: 
 En primer lugar se debe identificar los objetos principales. 
 Tener en cuenta que la abstracción es un proceso iterativo de ensayo-y-error, con 
aproximaciones y refinamientos sucesivos. 
 Posteriormente se debe definir propiedades y métodos esenciales de cada 
objeto. En definitiva características relevantes o esenciales en el dominio del 
problema. 
 Si existen objetos semejantes ,clasificar los objetos según sus 
semejanzas/diferencias 
 Por último se debe establecer cómo se relacionan entre sí, cómo se comunican 
(eventos). 
En la década de los cincuenta, uno de los pocos mecanismos de abstracción para 
ordenadores era el lenguaje ensamblador y lenguaje máquina. Posteriormente los 
lenguajes de programación de alto nivel ofrecieron un nuevo nivel de abstracción. 
Los diferentes paradigmas de programación han aumentado su nivel de abstracción, 
comenzando desde los lenguajes máquina, los más próximos al ordenador y más 
lejanos a la comprensión humana; pasando por los lenguajes de comandos, los 
imperativos, la orientación a objetos (OO). 
La abstracción ofrecida por los lenguajes de programación se puede dividir en dos 
categorías: abstracción de datos (pertenecientes a los datos) y abstracción de control 
(perteneciente a las estructuras de control) 
El siguiente diagrama se puede observar la evolución de los lenguajes de programación. 
 
 10 
 
 
Existe como vemos una evolución constante en el nivel de abstracción apreciable a lo 
largo de la historia y evolución de los lenguajes de programación. 
 
5. Lenguajes de programación orientados a objetos 
 
El primer lenguaje orientado a objetos fue SIMULA 67. Fue creado por científicos 
noruegos (Nygaard y Gahl) que, después de probar otros lenguajes de tercera 
generación, decidieron crear un lenguaje
que les permitiera realizar todo lo que los otros 
no les permitían a causa de las limitaciones que tenían. SIMULA 67 fue el primer 
lenguaje de programación en el que se introdujeron los conceptos de clase y objeto. 
 
Más tarde, a principios de los años setenta, en los laboratorios de Xerox, un equipo de 
desarrollo intentó implementar un prototipo de ordenador para niños, conocido como 
DynaBook, que utilizaba por primera vez el concepto de interfaz gráfica de usuario. 
Enseguida los desarrolladores relacionaron las ideas de la programación orientada a 
objetos con las piezas del ordenador nuevo, ya que se podía definir perfectamente el 
funcionamiento de cada elemento gráfico como un objeto, porque tenía un 
comportamiento muy definido y respondía a interacciones muy concretas. 
 
Para implementar el proyecto DynaBook, elaboraron un lenguaje de programación 
nuevo denominado Smalltalk, que estaba fuertemente influido por SIMULA 67. Este 
lenguaje introducía el concepto de herencia de manera explícita. La herencia es el 
mecanismo que permite que un objeto comparta las características y el comportamiento 
de otro objeto, del cual se dice que hereda. 
 
El concepto de herencia es muy valioso para reutilizar código, ya que determinados 
objetos, aunque no son idénticos, sí que tienen características comunes. Por ejemplo, 
toda persona tiene un nombre y una fecha de nacimiento. Estas propiedades se utilizarán 
tanto si dentro de nuestra aplicación la persona es una estudiante como si es un profesor. 
En próximos temas, lo estudiaremos más detalladamente. 
 
A partir de este momento, los lenguajes de programación orientados a objetos van 
incorporando características nuevas que los hacen evolucionar hasta los lenguajes de 
programación que conocemos hoy en día. 
 
En la década de los ochenta, Bjarne Stroustrup, empleado de AT&T Labs., amplió el 
lenguaje de programación C para adaptarlo a la programación orientada a objetos. 
Inicialmente se denominó C with classes, pero finalmente se llamó C++. Este lenguaje 
está basado en el SIMULA 67, con respecto a la orientación a objetos, y en el C, en 
cuanto a la sintaxis. 
 
Desde que aparecieron los lenguajes orientados a objetos, éstos han ido aportando 
funcionalidades nuevas a la programación orientada a objetos en versiones sucesivas, 
como las clases abstractas, los métodos static o la genericidad. Veremos todos estos 
conceptos en los próximos módulos. 
 
 11 
 
Actualmente hay muchos lenguajes de programación orientada a objetos, unos 
descendientes de lenguajes procedimentales que se han adaptado al paradigma de la 
programación orientada a objetos (POO) y otros que se han creado desde el principio 
pensando en este paradigma. Por poner algunos ejemplos, aparte de C++, SIMULA 67 y 
Smalltalk, que ya hemos mencionado antes, podemos mencionar Perl 5, Python, Java, 
C#, Delphi, Eiffel, etc. 
 
El siguiente gráfico muestra la evolución y la relación existente entre distintos lenguajes 
de programación. 
 
 
 
 
 12 
 
 
5.1. Clasificación de los lenguajes de programación 
 
Los lenguajes de programación han tenido grandes transformaciones durante su historia. 
Hay muchas clasificaciones que, según diferentes criterios, así lo muestran. Una de las 
más conocidas es la clasificación de Wegner, que divide los lenguajes de 
programación de alto nivel de acuerdo con el orden de aparición y las 
características comunes. Concretamente, la clasificación consta de lenguajes de 
primera generación, de segunda generación, de tercera generación y la generación de los 
lenguajes actuales. 
 
 
5.1.1. Lenguajes de primera generación 
 
Lenguajes que se desarrollaron entre 1954 y 1958. Con éstos se produjo un salto en la 
abstracción a la hora de programar, ya que anteriormente se utilizaba el lenguaje 
ensamblador, que requería un buen conocimiento de la máquina en la que se ejecutaría 
el código. Algunos ejemplos de estos lenguajes son Fortran I, Algol 58 o IPL V: todos 
estaban basados en codificar de la mejor manera expresiones matemáticas y, por lo 
tanto, su dominio de aplicación estaba centrado en el cálculo y las aplicaciones 
científicas y de ingeniería. 
 
Los programas que se hacían con estos lenguajes se basaban en subprogramas que 
compartían los datos al ser ejecutados. 
 
 
 
 
Esta situación presentaba problemas serios porque cualquier error en el funcionamiento 
de un subprograma se propagaba al resto de la aplicación. Aparte, de que cuando el 
programa era demasiado grande, cualquier cambio se traducía en una tarea muy costosa 
que no siempre se conseguía llevar a cabo sin complicar todavía más el diseño original. 
 
5.1.2. Lenguajes de segunda generación 
 
Aparecieron a finales de la década de los cincuenta y a principios de los sesenta. En 
estos lenguajes, se empezó a poner énfasis en la abstracción algorítmica y la 
programación se extendió poco a poco a otros dominios del mundo real. Algunos de los 
lenguajes pertenecientes a esta generación son Cobol, Algol 60 o Fortran II. En el 
 13 
 
terreno de la estructuración de los programas, la segunda generación empieza a 
introducir el concepto de procedimientos dentro de los subprogramas, con lo cual 
aparecen los conceptos de paso de parámetros y de visibilidad de las variables. 
 
 
 
5.1.3. Lenguajes de tercera generación 
 
 En esta época, los costes del hardware continúan abaratándose, lo cual hace que las 
aplicaciones informáticas lleguen cada vez más a ámbitos más distintos. Esta 
diversificación en los campos de trabajo comporta que se tenga que trabajar con tipos 
de datos diferentes de los puramente matemáticos, y cambiantes de una aplicación a 
otra. Por este motivo, en esta época aparece el concepto de tipo abstracto de datos que 
permite al programador especificar un tipo adecuado para cada problema y dotarlo de 
significado mediante un conjunto de operaciones sobre este tipo de dato. 
 
 
Este nuevo concepto resuelve algunos problemas de la programación de aplicaciones 
grandes, ya que de esta manera distintos desarrolladores pueden implementar módulos 
diferentes que se pueden compilar por separado y, finalmente, combinarlos. A pesar de 
todo, durante esta época el concepto de abstracción de los datos no fue demasiado 
utilizado como tal, sino que simplemente permitía agrupar distintas funcionalidades en 
módulos diferentes. Otro concepto que no existía era el de la visibilidad de los datos. 
Por lo tanto, nada impedía que diferentes módulos modificasen directamente los datos 
de otros módulos. 
 
5.1.4. Lenguajes de cuarta generación 
 
Estos son lenguajes de programación que no son de propósito general, como los 
descritos anteriormente. Han sido diseñados para algún propósito específico. Entre 
 14 
 
otros, están Sculptor ( lenguaje de dirigido por dominio) o lenguajes de especificación 
como SQL. 
Aunque no existe consenso sobre lo que es un lenguaje de cuarta generación (4GL). 
Una característica relevante podría ser el hecho de necesitar menos líneas de código 
debido a su poder de abstracción, y son lenguajes en los que no se indican detalles de 
implementación, simplemente se especifica lo que se desea obtener , dejando al 
interprete el implementar como hacerlo 
 Los 4GL se apoyan en unas herramientas de más alto nivel de abstracción denominadas 
herramientas de cuarta generación. A groso modo los 4GL podrían abarcar: 
 Lenguajes de presentación, como lenguajes de consultas y generadores de 
informes. 
 Lenguajes especializados, como hojas de cálculo y lenguajes de bases de datos. 
 Generadores de aplicaciones que definen, insertan, actualizan y obtienen datos 
de la base de datos. 
 Lenguajes de muy alto nivel que se utilizan para generar el código de la 
aplicación. 
 
6.
Lenguajes Orientados a Objetos 
 
Se denominan lenguajes orientados a objetos todos aquellos lenguajes de programación 
que exhiben en mayor o menor medida una serie de características que veremos a 
continuación, pero su rasgo esencial es que utilizan el concepto de clase de objetos 
como mecanismo de representación de abstracciones y de organización del código. 
 
6.1. Características básicas de los lenguajes de programación orientados 
a objetos 
 
 
 Tipificación estricta de datos 
 
Tipificar es el proceso de declarar el tipo de información que puede contener una 
variable. Este hecho implica que si dos expresiones están relacionadas, tanto si se trata 
de una asignación como si se trata de cualquiera de las operaciones que se pueden hacer 
sobre expresiones, tienen que coincidir en tipo. Si no lo hacen, el compilador genera un 
error en tiempo de compilación. 
 
 
 Encapsulamiento 
 
Es un mecanismo que permite agrupar datos y operaciones relacionadas en una 
misma entidad (clase ). Esta propiedad facilita que aparezcan otras características de la 
programación orientada a objetos como la reutilización. 
 
 15 
 
El encapsulamiento está relacionado con la ocultación de la información y con la 
separación de una implementación y su especificación. Proporcionando un 
acoplamiento más débil. 
 
De esta manera, y debido a la ocultación de la información, los usuarios de una clase 
disponen de unos métodos que permiten consultar y modificar el comportamiento de la 
clase, pero no tienen acceso directo a los datos internos. 
 
Para aclarar los conceptos de encapsulamiento y ocultación, podemos pensar en la clase 
Date( existente en Java). Una fecha se puede descomponer en día, mes y año, y puede 
tener unos métodos para acceder al día, al mes y al año y otros para modificar la fecha 
que almacena. 
 
 
Cualquier objeto que intente consultar o establecer la fecha de este objeto, lo tendrá que 
hacer mediante las operaciones destinadas a esta finalidad y no podrá acceder 
directamente a los atributos que representan el día, el mes y el año. 
 
La ventaja de esta situación es que si en cualquier momento tenemos que cambiar la 
representación interna de los datos almacenados, no será necesario modificar ninguna de 
las aplicaciones que utilizan objetos de tipo Date, ya que, a pesar de cambiarse su 
estructura interna, no se modifica su interfaz. 
 
Otra de las ventajas del encapsulamiento es la posibilidad de ocultar operaciones 
internas de la clase que no deben ser visibles por objetos externos a ésta. Por ejemplo, 
podríamos tener un método que, dada una fecha, comprobase si ésta es correcta y que 
sólo sea accesible internamente. 
 
 
 
 16 
 
Esta operación oculta puede servir para asegurarnos de que no insertamos fechas 
erróneas o para realizar cualquier otra operación que se utiliza internamente pero que no 
se quiere que se ejecute desde otras partes del programa. 
 
 Genericidad 
 
Esta propiedad permite la definición de módulos de programa (clases) con un alto grado 
de reusabilidad. La genericidad es básica para reutilizar código. Estos módulos se 
diseñan con parámetros formales genéricos, qué se instanciarán posteriormente con 
parámetros reales específicos. Esta permite definir métodos que tienen como parámetros 
elementos de cualquier tipo. 
 
Un ejemplo de genericidad lo tendríamos si quisiéramos definir un objeto que fuera un 
vector o una lista que pudiera recibir elementos de cualquier tipo. Esto tiene sentido 
porque el comportamiento de este objeto siempre es el mismo (obtener, borrar, insertar, 
etc.), independientemente del tipo u objeto contenido. De esta manera, lo podríamos 
usar una vez como vector de enteros, otra como vector de caracteres, etc. 
 
 
 Herencia 
 
Propiedad que nos permite definir una clase según otra u otras de manera que la clase 
que hereda tenga el mismo comportamiento y las mismas características que la clase de 
la cual hereda, más las características y el comportamiento de la propia clase. 
 
Por ejemplo, podríamos definir una clase Persona con los atributos y métodos 
correspondientes y dos clases más, Estudiante y Profesor, que hereden de la clase 
Persona. Las tres clases tendrían una parte común, y las clases Estudiante y Profesor 
añadirían otros métodos y atributos específicos para cada una de éstas. 
 
 Polimorfismo 
 
El polimorfismo está estrechamente vinculado con la herencia. Se puede definir como la 
propiedad por la cual se pueden realizar tareas diferentes invocando la misma 
operación, y esta se realizará según el tipo de objeto sobre el cual se invoca. 
 
Continuamos con el ejemplo anterior de los Estudiantes y los Profesores. Si tuviéramos 
un método denominado obtenerCreditos, en el caso de un estudiante, debería devolver 
el número de créditos de los que éste está matriculado, y en el caso de un profesor, nos 
tendría que devolver el número de créditos de las asignaturas que imparte. Las tareas 
necesarias para devolver el resultado según el tipo de objeto serían diferentes. 
 
Éstas son las características principales de los lenguajes de programación orientada a 
objetos, aunque hay lenguajes que incorporan otras características propias adicionales, 
como la gestión de objetos en memoria (de C# y Java) o la gestión de excepciones. 
 
7. Bibliografía 
 
 Object -Oriented Construction 2nd .Bertrand Meyers .ISE California 
 Java 2.Curso de programación. Fco Javier Ceballos.Ra-Ma 
 17 
 
 Programación orientada a objetos. Joan Arnedo Moreno. Daniel Riera I 
Terrén.www,uoc.edu 
 Programming Languages.1976 Wegner 
 Programación Orientada a Objetos .Luis Joyanes Aguilar.Mc Graw Hill 
 
Programacion Orientada a Objetos 2015-2016 Campus/TEMA 3 Relaciones entre clases v2.pdf
1 
 
Tema 3 
 
 
Relaciones entre 
clases 
 
 
 
 
 
 
 
2 
 
Índice de contenidos 
 
1. Introducción .......................................................................................................................... 3 
2. Relaciones (asociaciones) entre clases .................................................................................. 3 
2.1. Asociaciones entre clases .............................................................................................. 3 
2.2. Cardinalidad/Multiplicidad ........................................................................................... 5 
2.3. Navegabilidad de las asociaciones ................................................................................ 7 
2.4. Tipos de asociaciones entre clases ................................................................................ 8 
2.4.1. Asociaciones reflexivas ............................................................................................. 8 
2.4.2. Asociaciones de agregación compartida ................................................................... 9 
2.4.3. Agregación de composición ...................................................................................... 9 
 2.4.4. La clase asociación .............................................................................................. 10 
 
 
 
 
 
 
3 
 
 
1. Introducción 
Hasta ahora hemos abordado los conceptos de la POO centrándonos en el concepto de 
clase de forma aislada. Cuando nos enfrentamos al diseño de un sistema complejo lo 
normal es que aparezcan numerosos conceptos o abstracciones relacionadas entre sí. 
Una relación entre clases representa una conexión semántica entre objetos que son 
instancia de esa clase y esta relación podrá tener distintas propiedades. 
 
En este tema veremos cómo abstraer y modelar las relaciones semánticas entre clases, 
viendo los distintos tipos de relaciones existentes y estudiando cada una de ellas.
Como 
notación emplearemos UML ( anexo I) que es un lenguaje de representación 
independiente del lenguaje de programación que usemos (C++ o Java en nuestro caso). 
 
2. Relaciones /Asociaciones entre clases 
 
Un aspecto muy importante del proceso de diseño es definir las relaciones que se tienen 
que establecer entre las clases que representan el modelo del dominio del problema 
que pretendemos resolver de la forma más completa posible. La idea es que las clases( 
sus objetos) colaboran entre sí para realizar algún tipo de proceso o funcionalidad que es 
preciso llevar a acbo. 
 
 
Para representar las clases y sus relaciones utilizaremos como hemos mencionado antes 
un diagrama de clases en UML (Unified Modeling Language). Un diagrama de 
clases representa las clases y las asociaciones existentes entre ellas y las relaciones que 
existirán entre los objetos . Las relaciones entre clases también se denominan 
asociaciones. 
 
 
2.1. Asociaciones entre clases 
Dadas dos clases A y B una asociación representa algún tipo de relación existente 
entre dichas clases en el dominio del problema. La asociación se representa en UML 
con un segmento que une las dos clases (ver Figura 1). 
Clase A Clase B
Asociación
 
Figura 1 Asociación entre clases 
La asociación representa una relación estructural entre objetos normalmente de clases 
diferentes y es la más común de las relaciones. Aunque es frecuente que la mayoría de 
las asociaciones sean binarias, pueden existir relaciones ternarias o n-arias. 
 
4 
 
Las conexiones que describe una asociación dan lugar a interacciones entre los objetos 
que comúnmente se denomina colaboración. En esencia, una asociación es una relación 
entre clases que indica cómo se pueden enlazar juntas las instancias de las clases, 
además de que estas van a colaborar para desarrollar alguna funcionalidad. 
 
Por ejemplo, dado el siguiente diagrama de clases 
 
Estudiante Curso
**
 
 
Una posible instancia de estos objetos sería la siguiente: 
 
Pedro:Estudiante
Laura:Estudiante
Seguridad:Curso
Ingenieria sw:Curso
 
 
Otros ejemplos de asociaciones entre clases: 
 
ColaMensajes Mensaje
Circuito Componente
 
 
Al observar estos ejemplos surgen las siguientes cuestiones. ¿Un objeto de tipo Mensaje 
con cuantos objetos de tipo ColaMensajes está relacionado y viceversa? O de la misma 
forma ¿Con cuántos objetos de tipo Circuitos puede estar relacionado un objeto de tipo 
Componente?. 
 
Al plantearnos las relaciones entre clases surgen cuestiones como la 
cardinalidad/multiplicidad y la direccionalidad de la relación (sentido de la relación) 
,conceptos estos que proporcionan información adicional acerca de la asociación. Más 
adelante veremos estos importantes conceptos en detalle. 
 
 
5 
 
Implementación de la asociación entre clases 
 
Dado el siguiente diagrama de clases (Figura 2 
-nombre
-apellidos
Profesor
-nombre_dept
Departamento
* 1
 
 
Figura 2 Diagrama de clases 
Vamos a ver como se implementaría en un lenguaje de programación Java : 
 
public class Profesor { 
 String nombre; 
 String apellidos; 
 Departamento dept; 
 Profesor (){} 
} 
 
 
 
public class Departamento { 
 String nombre_dept; 
 List empleados; 
 
 Departamento(){} 
 public void setProfesor(Profesor p){ 
 empleados.add(p); 
 } 
}
 
 
2.2. Cardinalidad/Multiplicidad 
 
Dado que una asociación representa una interconexión entre instancias de objetos de 
dos clases, se podría indicar, cuántos objetos de cada clase pueden estar involucrados en 
la asociación. Para ello se debe definir la cardinalidad (o multiplicidad) de la 
asociación. 
 
Cardinalidad/Multiplicidad: es el número de objetos de un extremo de la asociación 
que están enlazados con un objeto del otro extremo. Por ejemplo, un objeto de tipo 
Departamento puede emplear (relación) a muchos objetos de tipo Empleado. La 
multiplicidad de la relación de Departamento con Empleado es "una a muchos": 1 .. *. 
 
Podemos indicar el valor máximo y mínimo de la cardialidad , la cardinalidad máxima y 
mínima se ponen encima o debajo de la línea que representa la asociación y, 
dependiendo de los valores, tienen un significado u otro. 
 
Clase A Clase B
1..* *
cardinalidades
 
Figura 3 Representación de cardinalidades 
La cardinalidad es una de restricción introducida en el modelo para representar más 
fielmente la realidad (Ejemplo un estudiante sólo puede estar matriculado en un único 
6 
 
curso). Esto tendrá su implicación en la implementación de la asociación en el 
lenguaje de programación. 
La cardinalidad se puede expresar mediante diferentes notaciones: 
 
 Número exacto (1, 3, etc.). Expresa que un objeto de la clase Clase A tiene que 
estar relacionado obligatoriamente con el número de objetos que indica la 
cardinalidad de la clase Clase B . 
Clase A Clase B
1
 
 
 Rango de valores (0..1, 3..5, etc.). Con esta notación, indicamos el número 
mínimo y máximo de objetos con los que puede estar relacionado un objeto en el 
otro extremo de la relación. En el ejemplo un objeto instancia de la ClaseA sólo 
podrá estar relacionado como mucho con 3 instancias de la clase B y como 
mínimo lo estará con una. 
 
Clase A Clase B
1..3
 
 
 Muchos (*).Indica que la instancia de la clase Clase A puede estar relacionada 
con cualquier número de instancias de la clase Clase B (o incluso con ninguna) 
equivale a escribir 0..*. 
 
Clase A Clase B
*
 
 
 Combinaciones de las anteriores. 
 
A continuación (Figura 4) se muestran algunos ejemplos de cardinalidades. 
 
 
ColaMensajes Mensaje
*1
 
 
 
Cuenta Titular
21
 
 
Almacen Motor
*1
Pieza
1 *
 
 
7 
 
Figura 4 Ejemplo de multiplicidades/cardinalidades 
 
2.3. Navegabilidad de las asociaciones 
Otra característica ( o restricción) que podemos expresar en una relación de clases 
dentro de un diagrama UML es la navegabilidad de la relación. Esta restricción indica 
cómo se debe interpretar la asociación. La navegación indica el sentido en el cuál es 
posible recorrer la asociación. 
 
Las dos opciones o tipos de navegabilidad posibles son las siguientes: 
 
Asociación unidireccional: sólo una de las dos clases tiene constancia de la existencia 
de la otra. Y consecuentemente sólo un objeto tendrá “constancia del otro”. 
 
Persona Pais
 
El sentido semántico de esta restricción es el de que sólo un tipo de objeto participante en la 
relación va a interpretar la relación , en el otro no es necesario. 
Implementación en Java 
 
public class Persona { 
 String nombre; 
 String apellidos; 
 Departamento dept; 
 Pais pais; 
 Persona(){}; 
} 
public class Pais { 
 String nombre; 
 int población; 
 String religion_mayoritaria; 
} 
 
 
 
Como se puede observar en la implementación anterior, desde un objeto de tipo 
Persona se tendrá acceso a uno de tipo País, pero no al revés. 
 
Asociación Bidireccional: los dos tipos de objetos pueden interpretar la relación 
existente. 
 
-nombre
-apellidos
-dni
Ciudadano
-Nserie
-clase
-claves
CertificadoDigital
1 1
 
Ejercicio. Plantee como seria la implementación en Java de esta asociación 
bidireccional 
8 
 
 
2.4. Tipos de asociaciones entre clases 
 
2.4.1. Asociaciones reflexivas 
 
Las asociaciones reflexivas son un tipo de asociación binaria, en la que las dos clases 
origen y destino son del mismo tipo. Como muestra este tipo de relación, no hay 
ninguna restricción que impida que una instancia de una clase se relacione con otras 
instancias de la misma clase. 
 
Se representan con una relación que entra y sale de la misma clase .Para poder 
identificar correctamente los roles de los dos
extremos de la asociación, hay que 
definirlos con un texto aclaratorio. 
 
Clase
 
Figura 5 . Asociación reflexiva 
 
Un ejemplo de esta asociación seria una clase que representa un directorio en un sistema 
de archivos ( Figura 6). 
 
Directorio
*
1
subdirectorio
 
Figura 6.Asociación Reflexiva 
Implementación en C++ 
 
La implementación de una clase reflexiva del ejemplo anterior sería de la siguiente 
forma 
 
class Directorio; 
 
class Directorio 
{ 
 char *nombre; 
 Directorio raiz; 
 List<*Directorio> subdirectorios; 
 
 public: 
 
 Directorio(); 
 ~Directorio(); 
}; 
 
9 
 
 
2.4.2. Asociación de agregación compartida 
Las asociación de agregación modela la relación semántica “todo/parte” o bien “ x 
tiene un y” o “ y es parte de x”. A continuación podemos el esquema general de este 
tipo de asociación. 
 
Todo Partees parte de
 
El rol “Parte” puede pertenecer a más de un agregado (de ahí el nombre de agregación 
compartida. La existencia del “Todo” está supeditada a la de las Partes. y la destrucción 
del “Todo” no implica la destrucción de las Partes. 
Ejemplo : un Equipo de trabajo se compone de diferentes Personas. Una misma 
Persona puede ser miembro de más de un equipo de trabajo. 
 
 Otro ejemplo podría ser la relación de una Universidad con sus distintas Facultades 
como se muestra en el siguiente diagrama: 
Universidad Facultades parte de
*
 
La relación anterior lleva implícito el significado que si una de las partes desaparece la 
parte Todo continua existiendo, de ahí que a este tipo de agregación se le denomina 
débil. En el ejemplo una Facultad podría desaparecer y la Universidad seguiría 
existiendo. 
2.4.3. Agregación de composición 
Una agregación de composición es aquella que posee (<<contiene>>) a sus partes y 
entraña una fuerte dependencia de propiedad sobre ellas. Se dice entonces que la 
agregación es fuerte lo que constituye la principal diferencia con la agregación 
anterior. 
 
La característica de agregación fuerte confiere a la asociación la característica de que 
el Todo no puede existir sin las partes ni viceversa. El ciclo de vida de las Partes está 
estrechamente ligado al del Todo, de forma que si este se destruye se destruyen también 
las Partes. La cardinalidad en el lado Todo tiene que ser uno, mientras que en el lado 
Parte puede ser un intervalo. 
 
10 
 
Todo Parte N
Parte 1 Parte 2
1
1
1
 
 
Figura 7 Relación de composición 
A diferencia de la agregación simple la agregación de composición requiere que las 
partes no puedan tener una relación de agregación con a otra clase ( no pueden estar 
compartidas). 
Ejemplos de agregación de composición pueden ser los siguientes diagramas de clases: 
Avión Motor
Asientos Fuselaje
1
*
1 1
1 1
 
Figura 8 Ejemplos de asociación por composición 
2.4.4. La clase asociación 
En muchas ocasiones en el diseño, surge la necesidad de representar explícitamente una 
relación entre clases como otra clase más debido a su relevancia y por que la propia 
relación tiene características que deben ser reflejadas. 
Una clase asociación es una clase que representa una relación entre clases y tiene la 
suficiente relevancia en el problema para tener su propia representación. Es decir la 
clase asociación nos permite caracterizar la propia relación existente entre clases. 
Para ver esto considere el siguiente ejemplo (Figura 9): 
Cliente Producto
-fecha
-cantidad
-importe
Pedido
* *
 
Figura 9 . Representación de una clase asociación 
11 
 
En este ejemplo la asociación entre la clase Cliente y Producto tiene tanta relevancia en 
el dominio que se opta por modelarla como una clase más. Esto tiene la ventaja de que 
se pueden especificar atributos y métodos que describen la propia relación. 
Programacion Orientada a Objetos 2015-2016 Campus/T3 paracticas obligatoriasv3 2013_2014.pdf
1 
 
UPSAM 2013-2014 Laboratorio de prácticas 
 
Programación. Prácticas obligatorias 
 
Normativa: 
 Las siguientes prácticas son obligatorias para aprobar la asignatura y tienen que ser entregadas 
y explicadas antes de la fecha fijada por el profesor. 
 El material a entregar será un diagrama UML por cada clase que se diseñe y el código fuente 
de la práctica en el lenguaje requerido. 
 No se corregirán prácticas que no compilen. 
 Las prácticas son individuales. 
 
 
 
2 
 
Práctica 1. Java 
El sistema solar está formado fundamentalmente por una estrella (el Sol) y ocho planetas (o nueve para 
los nostálgicos de Plutón) que giran en orbitas elípticas alrededor del Sol, además de estos astros en el 
sistema solar existen: satélites, asteroides, cometas y meteoritos. 
 
En esta práctica se pretende modelar el sistema solar de manera muy simplificada, con el sol, los 
planetas y sus satélites más importantes con las siguientes características para cada astro: 
Estrella 
Atributos Funciones 
Nombre 
Radio ecuatorial 
Temperatura del núcleo 
Distancia respecto a la tierra 
Obtener la temperatura en grados centígrados 
Obtener el valor de cada uno de los atributos 
 
 
Planeta 
Atributos Funciones 
Nombre 
Radio ecuatorial 
Temperatura media diurna 
Distancia respecto al sol 
Distancia respecto a la tierra 
Conjunto de Satélites (si los tiene). 
Valor temperatura en grados centígrados 
Número de satélites 
Valor del tamaño del planeta 
Obtener distancia al sol 
Atrapar astro en el campo gravitatorio 
 
Satélites de planetas: 
Atributos Funciones 
Nombre 
Radio 
Obtener el valor nombre y el radio 
 
Nota: esta información está incompleta a nivel de clase, el alumno deberá completar las funciones que 
posteriormente sean necesarias para cada clase. 
3 
 
Nota: Aunque existen planetas con muchos satélites (Saturno tiene 23), sólo se desean reflejar los más 
importantes (ver anexo práctica). 
Consideraciones: 
El sistema solar se debe formar de la siguiente manera: 
1. Inicialmente está vacío 
2. En el momento de la llamada al método denominado bigBang() dentro de la clase 
SistemaSolar , se creará el Sistema Solar a partir de los planetas y satélites recogidos en el 
anexo de la práctica con la siguiente secuencia: 
a. Primero se forman las masas planetarias: planetas interiores y planetas exteriores 
b. Segundo se forman las masas de los astros (Satélites) que posteriormente serán 
capturados uno a uno por la fuerza gravitatoria de los planetas. Se debe considerar lo 
siguiente en la captura: 
i. La captura de un astro por el campo gravitatorio de un planeta se hará con la 
operación : atraparAstroEnCampoGravitatorio(Satelite ) de la clase Planeta. 
Ejemplo Tierra.atraparAstroEnCampoGravitatorio(luna) donde luna es un 
objeto de tipo Satelite 
A partir de ese momento el astro se convierte en un satélite del planeta. 
Se pide: 
 
A. Modelo de clases UML completo del Sistema solar. El sistema solar debe ser modelado como 
una composición de Planetas y Satélites 
B. Implementar métodos en la clase Sistema Solar para obtener los siguientes datos: 
o Menor distancia de un planeta respecto al sol 
o Nombre del planeta que menos satélites tiene 
o Temperatura del planeta más caliente del sistema solar 
o El Nombre del planeta más pequeño del sistema solar 
o Número de satélites de un planeta del sistema solar determinado( es un parámetro) 
C. En el programa principal realice el siguiente test de funcionalidad 
o Crear el sistema solar 
 El estado inicial del Sistema Solar debe ser vacío 
o Activar la operación bigBang ().es estado del sistema solar será el de estar formado 
por todos los planetas y satélites 
o Obtenga la siguiente información del sistema solar 
- Número de satélites
de Jupiter 
- Nombre del planeta que menos satélites tiene 
- El nombre del Planeta que más temperatura tiene y el más pequeño 
- Los satélites que tiene cada planeta ( los nueve) con la siguiente salida por 
pantalla 
 ******************* 
 “Tierra” : NSatelites =1 
 “Marte” :NSatelites=2 … 
4 
 
 
ANEXO DE LA PRÁCTICA 
Datos del sistema solar 
Sol 
Parámetro Valor 
Radio ecuatorial ( 510 km) 7 
Temperatura media (K) 
 -del núcleo 
15 x 610 
Distancia de la tierra ( 810 km) 1,496 
 
Planetas interiores 
Parámetros Mercurio Venus Tierra Marte 
Radio ecuatorial ( 310 Km) 2,439 6,052 6,378 3,396 
Numero de satélites conocidos 0 0 1 2 
Distancia media del sol ( 610 Km) 57,9 108,2 149,6 227,940 
Distancia media de la tierra( 610 Km) 91,7 41,4 ----- 78,4 
Temperatura media (K) diurna 700,15 Nubes (243,15 K) 
Suelo (753,15 K) 
289 293,15 
 
Planetas exteriores o Jovianos. 
Parámetros Júpiter Saturno Urano Neptuno 
Radio ecuatorial ( 310 Km) 70,85 60 25,4 24,3 
Numero de satélites conocidos 16 23 15 8 
Distancia media del sol ( 610 Km) 778,3 1.429,4 2.871 4.504,3 
Distancia media de la tierra( 610 Km) 628,8 1.277,4 2.719,7 4.347,4 
Temperatura media (K) diurna 303,15 Nubes 
(148,15 ) 
 
Nubes 
(80,15) 
Nubes 
(120,15) 
 
Principales Satélites( o lunas) de los planetas del sistema solar ( nota :no están todos) 
Nombre Radio (Km) Planeta sobre el que 
orbita 
5 
 
Luna 1738 Tierra 
Phobos 17,5 Marte 
Deimos 12,5 Marte 
Io 1.801 Júpiter 
Europa 1.569 Júpiter 
Ganimedes 2.631 Júpiter 
Calisto 2.400 Júpiter 
Titán 2.575 Saturno 
Mimas 390 Saturno 
Tetis 525 Saturno 
Dione 560 Saturno 
Rea 764 Saturno 
Hiperión 180 Saturno 
lapetus 720 Saturno 
Phoebe 110 Saturno 
Oberón 582,60 Urano 
Titania 788 Urano 
Umbriel 584 Urano 
Triton 1.350 Neptuno 
Proteo 210 Neptuno 
Nereida 170 Neptuno 
 
 
 
Programacion Orientada a Objetos 2015-2016 Campus/Tema 3 Práctica 2 2013_2014.pdf
UPSAM 2013-2014 Laboratorio de prácticas 
 
Programación. Prácticas obligatorias 
Práctica 2 Java /C++ 
1
1
1
1
1
111
1
1
1
2 2
2
2
2
22
22
2
33
3 3
33
Matriz Raster
Imagen terreno 
satélite idealizada
N
M
Escala=1:50000
Zona=A23
Provincia=Madrid
Término municipal=Escorial
Datos cartográficos 
de la cobertura
3
 
Figura 1 imagen satélite con una representación codificada en formato Raster 
Una imagen satélite de una cobertura geográfica contiene características del terreno (Ej .zonas de bosques, 
zonas casas, zonas de cultivos, zonas fluviales, etc). La imagen se codifica en un formato denominado Raster 
como un conjunto celdas donde cada valor de celda ser corresponde con el valor codificado de cada 
característica. Para esta práctica los valores son los siguientes 
 celda (zona rio)=1 
 celda (zona pinos)=2 
 celda (zona cultivo)=3 
 celda (zona habitada)=5 
El resultado es una matriz que representa la imagen satélite codificada. La codificación servirá para 
realizar determinados tipos de algoritmos. 
Consideraciones para la práctica 
Se deberán abstraer las clases: Cobertura Geográfica y Matriz Raster. Sabiendo que la relación entre ambas 
clases es la de que una Cobertura geográfica tiene asociada una única Matriz Raster. Se debe modelar como 
una composición 
A continuación se muestran datos relevantes de estas entidades. 
Clase Características 
Cobertura 
Geográfica 
Información cartográfica de interés (escala ,zona, provincia, término municipal) 
 forma codificada de la imagen de su cubiertaMatriz con datos Raster 
Matriz Raster Matriz bidimensional de tamaño N x M de números enteros (supondremos siempre una 
matriz cuadrada N=M) 
Sobre el tipo de operaciones que podremos realizar en cada entidad se resumen en la siguiente tabla 
Clase Operaciones 
Cobertura 
geográfica 
Constructores necesarios 
AlgoritmoAPermite obtener si existen ríos en la cobertura geográfica ( devuelve true o 
false) 
AlgoritmoBPermite obtener si el área de influencia de un rio en la cubierta pudiera afectar a 
una zona de cultivo si este se desbordara 
AlgoritmocPermite obtener si el área de influencia de un rio en la cubierta pudiera afectar a 
una zona habitada si este se desbordara 
Matriz Raster Constructores necesarios 
 Obtener (i,j) . Permite obtener el valor codificado que tiene la posición (i,j) de la matriz 
 
Para implementar las operaciones considere la siguiente información: 
- Clase Cobertura Geográfica AlgoritmoA: true si existe un rio en la cobertura 
- Clase Cobertura Geográfica AlgoritmoB: obtener si el área de influencia de un rio pudiera afectar 
a una zona de cultivo si este se desbordara 
- Clase Cobertura Geográfica AlgoritmoC: obtener si el área de influencia de un rio pudiera afectar 
a una zona habitada si este se desbordara 
Se define el área de influencia de una característica (Ej rio desbordado)(Figura 2) como el conjunto de 
celdas adyacentes a las celdas que forman la característica en cuestión. Por ejemplo el área de influencia del 
rio para el ejemplo seria la siguiente: 
1
1
1
1
1
111
1
1
1
Área de influencia del rio
 
Figura 2 Área de influencia de la característica rio 
Para la determinación del área de influencia se usa el siguiente algoritmo: 
Por cada celda (i ,j) perteneciente a la característica Ck marcar los pixeles : celda(i,j-1) 
,celda(i,j+1), celda(i-1,j) , celda(i+1,j) siempre y cuando no exista un celda de la propia 
característica en estas posiciones. 
De forma general una característica Cv estará afectada por otra característica Ck si Ck en su área de influencia 
afecta a algúna celda perteneciente a Cv 
El algoritmo anterior para la imagen de ejemplo daría como resultado que el área de influencia del rio afecta a 
la característica zona urbana=3. Esto se muestra en la siguiente figura: 
1
1
1
1
1
111
1
1
1
3 3 3
3
33
3
 
 
 
Se pide: 
 Realice un modelo de clases y represéntelo usando UML 
 Implemente las clases Cobertura Geográfica y Matriz Raster en Java o C++ 
 Realice la siguiente prueba en la clase Programa Principal 
o Cree el mapa del ejemplo con las clases Cobertura y Matriz Raster (suponga una imagen de 
10x10) , para ello use los códigos que se han mencionado 
o Imprima por pantalla si una supuesta crecida del rio afectaría a alguna zona urbana o de 
cultivo 
 NOTA: Este algoritmo sólo es aplicable si existe un rio en la cobertura