Iimplementacion, Lenguajes OO

Anuncio
Programación
Orientada a Objetos
IMPLEMENTACION
LENGUAJES OO
Objetos como Tipo de Datos
Abstractos
• Los Objetos combinan estados (campos de datos /
variables de instancia / miembros de datos / slots)
y comportamiento (operaciones / métodos /
funciones miembro / funciones)
• Desde fuera, los clientes solo pueden ver el
comportamiento del objeto, desde adentro, los
métodos proveen el comportamiento a través de
modificaciones de estado y de interacción
con
top
otros objetos.
top pop push
pop
push
2
Interfase e Implementación
• Principio de Parnas para módulos:
– Se debe proveer al usuario con toda la información
necesaria para utilizar el módulo correctamente y
nada más.
– Se debe de proveer al implementador con toda la
información para completar el módulo y nada más.
• Principio de Parnas para objetos:
– La definición de una clase debe proveer al usuario
de toda la información necesaria para manipular las
instancias de la clase correctamente y nada más.
– Un método debe tener acceso a toda la información
necesaria para llevar a cabo sus responsabilidades
y nada más.
3
Aprendiendo un Lenguaje OO (1)
• Definición de Clase
– es como una definición de tipo
– especifica los datos y el comportamiento
(métodos) a ser asociados con los datos
– No crea un valor (objeto)
– Pueden definirse valores por defecto para los
datos.
– La visibilidad de los datos y comportamientos
puede ser especificado
4
Aprendiendo un Lenguaje OO (2)
• Instanciación de Objetos
– Es la creación de una nueva instancia de una clase. Ej.: un
valor (objeto)
– Se pueden definir valores iniciales para los campos de datos.
[Existe confusión en algunos lenguajes con verificación fuerte
de tipos donde la instanciación de objetos toma
sintácticamente la forma de una declaración]
• Mensajes
– Tienen un receptor (el objeto al cual se envía el mensaje)
– Tiene un selector de mensaje (algún nombre que indica que
mensaje se esta enviando
– Tiene argumentos.
5
Aprendiendo un Lenguaje OO (3)
• Jerarquía de Clases
– Son definidas cuando en la definición de una clase,
una o màs clases padres son especificadas
(herencia simple o multiple)
• Herencia
– Es la propiedad por la cual las instancias de una
clase hija o subclase pueden acceder tanto a los
datos como al comportamiento (métodos) asociados
con una clase padre o superclase.
• Unión de métodos o búsqueda de método
– Es un mecanismo para determinar que método será
usado en respuesta a un mensaje enviado
6
Ejemplo de la Carta
1
•Una carta pertenece a un palo, tiene un valor y un color
•Una carta pude estar boca arriba o boca abajo y puede ser volteada
de una posición a otra.
•Una carta pude ser mostrada o retirada.
•Una carta mágica es un tipo especial de carta que puede cambiar su
valor
7
Definición de la Clase
Carta
Palo: {brillo, trebol, corazon, espada}
Valor: integer
BocaArriba: boolean
Color: {rojo, negro}
Voltear() <algún código>
Dibujar(W:window;X,Y:position) <algún
código>
Borrar() <algún código>
“define class”
<nombre>
<definición datos>
<declaración métodos
& definiciones>
“define class”
Carta
...
...
8
Definición de Clase: visibilidad,
alcance, protección
Carta
Priv Fix Palo: {brillo, trebol, corazon, espada}
Priv Fix Valor: integer
Priv BocaArriba: boolean
Pub Class NrdeCartasenJuego: integer
Pub
Pub
Pub
Pub
define class”
<nombre>
<definición datos>
<declaración métodos
& definiciones>
Voltear()
Dibujar(W:window;X,Y:position) ...
Borrar()
Class Barajar()
9
Definición de Clase
• Modificadores de Visibilidad para controlar la
visibilidad y así la manipulación de los campos
de datos y los métodos
• Métodos para Setear y Obtener para controlar
como los datos son accedidos y modificados
• Campos de datos constantes o inmutables para
garantizar que no ocurran cambios en ellos.
• Campos de datos de clase (variables de clase)
que son compartidos entre todas las instancias.
• Interfaces e Implementación
10
Clases y Métodos en C++ (1)
• C++ distingue archivos de interfase (extensión .h) y
archivos de implementación (extensión .cpp)
• El #include puede ser usado para "importar"
descripciones de interfaz en los archivos de
implementación
• Las definiciones de clase se parecen a las definiciones
de estructuras pero ambos, datos y métodos son
permitidos.
• Debido a que los métodos son tratados implemente
como tipos especiales de campos, un método y un
campo de datos no pueden tener el mismo nombre.
• Los métodos con el mismo nombre que la clase son
especiales: son los constructores y son usados para
crear instancias de la clase.
11
Clases y Métodos en C++ (2)
• La palabra reservada private precede aquellas
porciones del código que pueden ser accedidas solo
por los métodos de la clase en sí.
• La palabra reservada public precede a aquellas
porciones de código que pueden ser accedidas por los
clientes de la clase.
• Debido a que los usuarios de la clase están
mayormente interesados en las partes públicas, estas
deberían estar primero.
• Las descripciones de los miembros privados son dadas
para beneficio del compilador solamente. Los clientes
no deberían verlas  viola el principio de Parnas.
12
Clases y Métodos en C++ (3)
• Un archivo de implementación de una clase de proveer las
definiciones de los métodos descritos en el archivo de
interfase (a menos que sea un método virtual puro)
• Las funciones Inline se proveen para promover el uso de
los principios de abstracción y encapsulación al mismo
tiempo que se evita la sobrecarga de una llamada a
procedimiento.
• Las definiciones Inline ocurren también cuando el cuerpo
de un método es definido directamente en la definición de
clase; esto hace las definiciones de clase más difíciles de
leer y algunos compiladores requieren que se listen los
miembros privados de datos antes que la interfaz pública.
13
C++ Carta: Archivo de Interfaz
class carta {
public: enum palos {brillo, trebol, corazon, espada};
enum colores {rojo, negro};
carta(palos,int);
//constructor
colores color();
//acceso a atributos
bool
EstaBocaArriba();
int
valor();
palos palo();
void
Voltear();
//acciones
void
Dibujar(window &, int, int);
void
Borrar(window &, int, int);
private: bool
BocaArriba;
int
valordelValor;
palos valordelPalo;
};
14
C++ Carta: Archivo de
Implementación
#include "carta.h"
carta ::carta(palos sv ,int rv);
{ valordelPalo = sv;
valordelValor = rv;
BocaArriba = true; }
inline int carta::valor()
{ return valordelValor; }
colores carta::color()
{ if (palo()==corazon || palo()==brillo)
return rojo;
else return negro; }
void
carta::Voltear()
{ BocaArriba = ! BocaArriba
}
15
C++ Carta: Archivo Interfaz (bis)
class carta {
private: bool
BocaArriba;
const int
valordelValor;
const palos valordelPalo;
public: enum palos {brillo, trebol, corazon, espada};
enum colores {rojo, negro};
carta(palos sv , int rv): valordelPalo(sv), valordelValor (rv) { };
//constructor
colores color(); //acceso atributos
bool
EstaBocaArriba() { return BocaArriba; };
int
valor() { return valordelValor; };
palos palo() { return valordelPalo; };
...
16
Java
• Java no es un dialecto de C++, hay
similaridades superficiales, pero las diferencias
internas son substanciales.
• Java no tiene punteros, referencia, estructuras,
uniones, sentencias goto, definición de
funciones o sobrecarga de operadores.
• Java usa recolección de basura para manejo de
memoria, introduce el concepto de interfaces,
introduce los paquetes y la visibilidad de
paquetes, soporta multihilo y manejo de
excepciones.
• Java es un lenguaje muy portable debido a que
usa una máquina virtual para ejecutarse.
17
Clases y Métodos en Java
• No hay preprocesador, variables globales o tipos de
datos enumerados. Los valores simbólicos son
creados por declaración e inicialización de un campo
de datos con las palabras final y static.
• La palabra reservada static usada en los campos de
datos indica que solo una copia del campo de datos
existe y es compartida por todas las instancias. En un
método indica que este existe aún cuando no existan
instancias de la clase. Un programa de Java necesita
una clase con un método main static.
• La implementación de los métodos debe ser
proporcionada directamente en la definición de la clase.
• Las palabras reservadas public y private son aplicadas
individualmente a cada definición de variable o método.
18
Clase Carta: Java (1)
class Carta {
final static public int rojo = 0;
final static public int negro = 1;
final static public int espada = 0;
final static public int corazón = 1;
final static public int brillo = 2;
final static public int trebol = 3;
private boolean bocaArriba;
private int valordePalo;
private int valordeValor;
// valores estáticos
// campos de datos
19
Clase Carta: Java (2)
Carta (int sv, int rv)
// constructor
{ valordePalo = sv; valordeValor = rv; bocaArriba = true; }
public boolean estaBocaArriba() { return bocaArriba; } // acceso
atributos
public int valor() { return valordeValor; };
public int palo() { return valordePalo; };
public int color() { if (palo() == corazon || palo() == brillo) //acciones
return rojo;
return negro; }
public void voltear() { bocaArriba = ! bocaArriba; }
public void dibujar( Graphics g, intx, int y)
…
};
//clase Carta
20
CLOS
• Common Lisp Object System es un extensión
orientada a objetos del lenguaje LISP
• CLOS usa un acercamiento funcional al
contrario del paso de mensajes para invocar
operaciones específicas de las clases
• CLOS soporta actualización automática de las
instancias cuando cambia la definición de las
clase y permite que las instancias se conviertan
en instancias de una clase diferente.
21
CLOS Funciones Genéricas
• Asuma que existen las clases racional y
complejo y siguen las siguientes definiciones:
(defgeneric suma (x y))
(defmethod suma (x y) (+ x y))
(defmethod suma ((x racional) (y racional))
(<cuerpo>))
(defmethod suma ((x complejo) (y complejo))
(<cuerpo>))
• El método ejecutado cuando se llama (suma A
B) dependerá de los tipos de A y B, el método
es escogido por un proceso que selecciona,
ordena y combina los métodos aplicables.
22
Clases y Métodos en CLOS
• El macro defclass define una clase con nombre
y retorna un nuevo objeto clase
• Una clase CLOS define slots que contienen las
instancias de esa clase
• Un slot tiene un nombre y puede contener un
valor simple; un slot sin un valor es un slot nounido.
• Ambos especificadores de slots y opciones de
clase son usados para determinar las
características de los slots y crear maneras de
manipular el slot
• Se puede añadir documentación a los slots y a
las clases
23
Clases y Métodos en CLOS
• El especificador :allocation permite distinguir
entre slots locales y compartidos. Un slot es
local cuando su valor es único para cada
instancia individual. Un slot es compartido si es
común para todas las instancias de una clase.
• El especificador :type permite especificar un
tipo para un slot
• El especificador :documentation permite añadir
documentación al slot
24
Clases y Métodos en CLOS
• Los specificadores :reader, :writer y :accessor
crean métodos especiales que permiten leer,
escribir o ambos los valores de un slot.
• El especificador :initarg introduce una palabra
clave que puede ser usada mientras se
instancia una clase para inicializar un slot.
• Los especificadores :initform permite especificar
un valor por defecto para un slot.
• La opción de clase :documentation permite
añadir documentación a una clase
• La opción de clase :default-args permite
especificar valores por defecto para los slots de
una clase usando los initarg.
25
Clases y Métodos en CLOS
• La macro defmethod define un método y
devuelve un objeto método.
• La clase en la que un método es definido se
menciona explicitamente
• Los métodos pueden ser calificados como
:before, :after o :around
• Cuando no existe una función genérica con el
mismo nombre esta se genera
automáticamente.
– Los argumentos deben ser compatibles sino se
produce un error
– Cuando no contienen un objeto método con el
mismo especializador y calificador un nuevo objeto
método es agregado.
– Cuando este contiene un objeto método con el
mismo especializador y calificador, el método viejo
es remplazado.
26
Clase Carta: CLOS (1)
(defclass carta ()
((bocaArriba :initarg :bocaArriba :accessor estaBocaArriba
:allocation :instance :initform 'true)
(palo :initarg :palo :reader palo
:allocation :instance)
(valor :initarg :valor :reader valor
:allocation :instance)
(dueno
:allocation :class :reader dueno
:initform 'vivi))
(:documentation “una clase de ejemplo"))
27
Clase Carta: CLOS(2)
(defclass carta ()
((bocaArriba :initarg :bocaArriba :accessor estaBocaArriba
:allocation :instance :initform 'true)
(palo :initarg :palo :reader palo
:allocation :instance)
(valor :initarg :valor :reader valor
:allocation :instance)
(:default-initargs :suit „espada :rank 1))
(defmethod voltear ((c carta))
(setf (estaBocaArriba c) (not (estaBocaArriba c))))
(defmethod color ((c carta))
(if (or (eq (palo c) „corazon) (eq (palo c) „brillo))
„rojo „negro))
28
Instanciación e Inicialización
“make instance”
Carta
brillo, 9, false
(una Carta)
palo: brillo
valor: 9
bocaArriba: false
color: rojo
“make instance”
Carta
espada, 4, true
(otra Carta)
palo: espada
valor: 4
bocaArriba: true
color: negro
“make-instance”
<clase>
<valores iniciales>
29
Paso de Mensajes
(una Carta)
palo: brillo
Valor: 9
bocaArriba: false
voltear()
“send message”
<objeto>
<selector mensaje>
< argumentos>
true
Carta
Barajar()
30
Creación e Inicialización
•
•
•
•
•
•
•
•
Alocación en Stack versus Heap
Variables Automáticas versus Dinámicas
Creación Implícita versus Explicita
Recuperación de Memoria Manual versus
Recolección de Basura Automática
Punteros Explícitos versus Implícitos
Creación Inmutable versus Asignación simple
de instancias a variables o slots.
Inicialización y valores por defecto;
Constructores
Recuperación de Memoria; Destructores
31
Paso de Mensajes
• El paso de mensajes es el proceso dinámico de
pedir a un objeto que realice una acción.
• Los mensajes tienen un receptor (el objeto al
cual se envía el mensaje), un selector del
mensaje (el nombre del mensaje) y
argumentos.
• La búsqueda de método es un mecanismo para
determinar el método a utilizar en respuesta a
un mensaje recibido.
• La diferencia entre lenguajes estáticos y
dinámicos es importante para el paso de
mensajes.
32
Creación e Inicialización en C++
• Existen variables automáticas y dinámicas
• Una variable automática es asignada al espacio cuando
se introduce el bloque que contiene la declaración. La
declaración no necesita estar al comienzo del bloque.
La instancia que se convierte en el valor de la variable
es creada implícitamente y el espacio es reservado en
la pila. El espacio es liberado una vez que el control
sale del bloque.
• Una variable dinámica implica el uso de manera
explícita de puntero y una creación de instancia
explícita a través del operador new. Se separa espacio
en el heap y debe ser luego liberado a través del
operador delete.
33
Creación e Inicialización en C++
• La inicialización en C++ se facilita a través del uso de
constructores; estos son métodos que son invocados
implícitamente cada vez que un objeto de una clase es
creado ya sea implícita como explícitamente.
• Los mecanismos de sobrecarga de operadores en C++
soporta el uso de múltiples constructores en una clase
para permitir múltiples estilos de inicialización.
• Hay soporte sintáctico para el uso de constructores en
la declaración de variables automáticas y en el
operador new.
• Los constructores pueden pasar argumentos a
constructores de objetos encapsulados.
34
Creación e Inicialización en C++
• Cuando el cuerpo del constructor es un conjunto simple
de asignaciones a variables de instancia, este puede
ser remplazado por cláusulas de inicialización en el
encabezado de la función.
• Los valores pueden ser declarados como inmutables a
través de la palabra reservada const. Las variables de
instancia que son declaradas como constantes deben
ser inicializadas con una cláusula de inicialización
porque no pueden ser blanco de una sentencia de
asignación.
• La limpieza de memoria en C++ esta facilitada a través
del uso de destructores; estos métodos son llamados
implícitamente cada vez que un objeto de la clase es
borrado ya sea implícita como explícitamente.
35
Paso de Mensajes en C++
• Un método en C++ es llamado una función miembro;
pasar un mensaje a un objeto es conocido como la
invocación de una función miembro.
• La sintaxis es similar a aquella usada para acceder
miembros de datos con los argumentos en una lista.
Se debe usar paréntesis así no haya argumentos.
• La seudo-variable this esta asociada con un puntero a
la clase receptora del mensaje; esta puede ser usada
para enviar mensajes subsecuentes al receptor o para
pasar el receptor como un argumento a otro mensaje.
36
Ejemplo de Carta: C++
carta lacarta(brillo, 9)
carta * micarta
micarta = new carta(corazon, 9)
lacarta.dibujar(win1,10,10)
if(lacarta.estaBocaArriba())
micarta->dibujar(win1,10,10)
if(micarta->estaBocaArriba())
colores carta::color()
{ if (this->palo()==corazon || this->palo()==brillo)
return rojo;
else return negro; }
void carta::haceralgo (int i)
{ hacerotracosa(this,i); }
37
Creación e Inicialización en Java
• Java utiliza recolección de basura automática. El
programador no tiene que tratar con el mantenimiento
de memoria.
• Todas las variables de algún tipo de objeto son
inicialmente asignadas el valor null.
• Los objetos son creados con el operador new; se
alocaciona espacio en el heap, los punteros son
siempre implícitos.
• La sintaxis del operador new requiere el uso de
paréntesis aún cuando los argumentos no son
necesarios.
• Los constructores en Java, no soportan cláusulas de
inicialización.
• Los constructores en Java pueden llamar a otros
constructores en la misma clase usando la palabra this;
esto permite factorizar el comportamiento común.
38
Creación e Inicialización en Java
• El destructor en Java es representado por la
función finalize. Esta función es llmada cada
vez que se recupera la memoria a través del
recolector de basura.
• El operador new en Java puede tomar como
argumento una cadena. Esto permite
determinar en tiempo de ejecución el tipo del
objeto que será alocacionado dado que la
cadena puede ser construida con una
expresión.
• Las variables de instancia que no pueden ser
reasignadas pueden ser creadas a través de la
palabra reservada final.
39
Paso de Mensajes en Java
• La única diferencia notable con C++ radica en
el hecho de que las variables declaradas como
un tipo de clase tienen los objetos como valor
(el puntero esta implícito); esto es también el
caso de la seudo-variable this
carta micarta = new carta(corazon, 9)
micarta.dibujar(win1,10,10)
if(micarta.estaBocaArriba())
colores carta::color()
{ if (this.palo()==corazon || this.palo()==brillo)
return rojo;
else return negro; }
40
Creación e Inicialización en CLOS
• CLOS usa un mecanismo de recolección de
basura.
• La función genérica make-instance es usada
para crear e inicializar nuevas instancias de una
clase.
• El primer argumento para make-instance es el
nombre del objeto clase, el resto de
argumentos forman la lista de inicialización.
• Los argumentos de inicialización que son
proporcionados son combinados con las
inicializaciones por defecto para los slots que
no tienen.
• Almacenamiento para las instancias se reserva
en el heap, los slots se llenan con los valores y
una instancia anónima del objeto se devuelve. 41
Creación e Inicialización en CLOS
• La inicialización en CLOS es muy sofisticada:
– El mismo nombrer initarg puede ocurrir en múltiples
slots
– Un slot puede tener múltiples nombre initarg
– Dos nombres initarg que son sinónimos pueden
ocurrir en la misma inicialización.
– Se pueden escribir métodos para controlar
explicitamente la inicialización. Initargs can be used
to supply arguments to methods
• La inicialización en CLOS no soo ocurre cuando
se crea una nueva instancia sino que :
– Cuando se reinicializa una instancia.
– Cuando se actualiza una instanaic cuando su clase
se actualiza
– Cuando se actualiza una instancia para cambiar su
clase a otra clase.
42
Paso de Mensajes en CLOS
• Los métodos son definidos relativamente a una clase
específica. Para cada conjunto de métodos con un
nombre dado hay una función genérica. Qué método
será llamado cuando se reciba un mensaje dependerá
de los argumentos de clase pasados a la función
genérica
• Sintácticamente un mensaje enviado en una llamada
funcional. El receptor del mensaje es el primer
argumento de la llamada a función y pude ser usado
naturalmente para enviar mensajes subsecuentes al
receptor o pasar el receptor como un argumento de un
mensaje subsiguiente.
43
Ejemplo de Carta: CLOS (1)
? (setf test (make-instance 'carta :palo „espada :valor 9))
#<CARD #x22F0A0E>
? (valor test)
9
? (palo test)
ESPADA
? (owner test)
VIVI
?(estaBocaArriba test)
T
?(setf (valor test) 10)
> Error: Undefined function SETF::|COMMON-LISP-USER::RANK|
called with arguments (10 #<CARD #x550B06E>) .
44
Ejemplo de Carta: CLOS(2)
?(slot-value test „valor)
9
? (setf (slot-value test „valor) 10)
10
? (valor test)
10
? (estaBocaArriba test)
T
? (voltear test)
NIL
? (estaBocaArriba test)
NIL
? (color test)
NEGRO
45
Ejemplo de Carta: CLOS(3)
? (setf other (make-instance 'carta :palo „espada :valor 2 :bocaArriba
'false))
#<CARD #x22F289E>
? (valor other)
2
? (bocaArriba other)
FALSE
? (owner other)
VIVI
46
Jerarquía de Clases
Carta
Palo: {brillo, trebol, corazon, espada}
Valor: integer
Voltear() <algun código>
Carta Mágica
Cambiar-Valor(R: integer) <algun código>
“define class”
<nombre>
<superclases>
<definición datos>
<definición métodos
&declaraciones>
“define class”
Carta Mágica
Carta
...
...
47
Herencia
• Herencia es la propiedad por las cuales las
instancias de una clase hija o una subclases
pueden acceder tanto a los datos como al
comportamiento (métodos) asociados con una
clase padre o superclase.
• La Herencia se puede ver como Extensión: el
comportamiento y datos asociados con la clase
hija es mayor al conjunto de comportamientos y
datos asociados con la clase padre.
48
Herencia
• La Herencia se puede ver como Especialización:
la clase hija es más especializada o restringida
que la clase padre.
• La Herencia es transitiva
• Las subclases pueden sobreescribir el
comportamiento de la superclase
49
Herencia & Asociación de
Mensaje
(una Carta Mágica)
Palo: brillo
Valor: 9
1
bocaArriba: false
(una Carta Mágica)
Palo: brillo
Valor: 9
bocaArriba: false
Cambiar-Valor(1)
(una Carta)
Palo: brillo
Valor: 9
bocaArriba: false
Cambiar-Valor(1)
Voltear()
true
50
Subclase, Subtipo y Substitución
• Subclase y subtipo no es exactamente la
misma cosa.
• El principio de sustitución dice que dadas dos
clases A y B, donde B es una subclase de A,
una instancia de B puede sustituir a una
instancia de A sin ningún efecto observable.
• El término subtipo se usa frecuentemente para
referirse a una relación de subclase en la cual
se mantiene el principio de la sustitución para
distinguirlo de una relación más amplia de
subclase que no necesariamente satisface ese
principio.
• Los lenguajes estáticos ponen mucho énfasis
en el principio de sustitución que lenguajes
dinámicos.
51
Formas de Herencia
• La Herencia se usa en una variedad
sorprendente de maneras. Algunas categorías
generales serían:
–
–
–
–
–
–
–
–
Especialización
Especificación
Construcción
Generalización
Extensión
Limitación
Variación
Combinación
52
Especialización (Subtipo)
Ventana
Mover
Resize
VentanaTexto
Editar
• La clase hija es una
especialización de la
clase padre pero
satisface las
especificaciones de la
clase padre
completamente.
• El principio de
substitución se mantiene.
• Es una forma ideal de
herencia, los buenos
diseños tienden a ella
53
Especificación
Abstract
Objeto Gráfico
Mover //no impl
Dibujar //no impl
Cubo
Circulo
Mover
Dibujar
Mover
Dibujar
• Las clases hijas
implementan el
comportamiento descrito
pero no implementado en el
padre.
• Es un caso especial de
especialización
• La clase padre es una clase
abstracta; no se permite
crear instancias de ella.
• Es usada para garantizar
que dos clases mantengan
una interfase común. (ej. Los
mismos métodos)
54
Construcción
Lista
Constantes
Set
Pila
Insertar
Push
• Las clases hijas obtienen
la mayoría de su
funcionalidad de la clase
padre solamente
cambiando los nombre de
los métodos o
modificando los
argumentos.
• Rompe el principio de
sustitución
intencionalmente
• Es una vía fácil para la
construcción de nuevas
abstracciones de datos.
55
Generalización
Ventana
Mostrar
Ventana-Coloreada
Color
Mostrar //sobreesc.
• La clase hija modifica
o extiende la clase
padre para obtener
una clase más
general de objeto.
• Usado cuando se
construye sobre una
base de clases
difíciles de modificar.
• Debe ser evitado a
favor de invertir la
jerarquía de clases y
utilizar
56
especialización
Extensión
Set
Insertar
Es-elemento?
String-Set
Buscar-Prefijo
• Añade habilidades
totalmente nuevas a una
subclase: nuevos
métodos son añadidos
con una funcionalidad
que no se relaciona
directamente con los
métodos existentes.
• Debido a que la
funcionalidad del padre
permanece, obedece el
principio de sustitución.
57
Limitación
Doble-Cola
PushFrente
PushAtras
Pila
PushAtras //error
• La subclase modifica
o sobrescribe
métodos de la clase
padre para eliminar la
funcionalidad
dejando una
subclase con un
comportamiento más
restringido.
• Es una contravención
explícita del principio
de sustitución.
• Se usa cuando un
grupo base de clases
es difícil de modificar.
58
Variación
Ratón
Marcar-Posicion
Seleccionar
Tableta Gráfica
Marcar-Posicion
• Es usada cuando dos
clases tienen una
implementación similar
pero no una jerarquía
conceptual
• Una de las dos clases se
selecciona arbitrariamente
como el padre; el código
en común se hereda y el
código específico se
sobrescribe.
• Usualmente una mejor
alternativa es crear una
59
clase padre común.
Combinación
Profesor
Sueldo
Ayudante
Estudiante
Matricula
Materias
• Usada para dar a una
subclase la
combinación de las
características de dos
o más clases.
• Conocido también
como herencia
múltiple.
• Se vuelve un
problema cuando
ocurren
ambigüedades.
60
Herencia en C++ (1)
• La herencia se indica en la definición de la clase
enumerando las clases padre; se soporta la herencia
múltiple.
• La palabra reservada public y private en el encabezado
de la clase configura al visibilidad de los miembros
heredados. Privado se usa para herencia por
construcción.
class TablePile : public CardPile { ... };
class Set : private List { ... };
• Los constructores de las clases hijas pueden invocar
explícitamente los constructores de las clases padres
con una cláusula de inicialización:
TablePile :: TablePile(int x, int y, int c)
: CardPile (x, y)
//inicializar padre
{ ... };
//inicializar hijo
61
Herencia en C++ (2)
• Protected es una palabra reservada adicional
que puede ser usada con clases formadas por
herencia. Los miembros protegidos de una
clases son accesible en los métodos de la
subclase pero no a las clases cliente.
• La palabra reservada virtual indica que la
función miembro es muy probablemente
sobrescrita por una subclase o es de hecho
sobrescribiendo un método de la superclase (es
opcional en la subclase)
• La semántica de sobescritura en C++ es sutil
pero importante; mucho depende en como el
receptor ha sido declarado y en el uso de la
palabra virtual.
62
Herencia en Java (1)
• Las sublclases son declaradas usando la palabra
reservada extends
class TablePile extends CardPile { ... };
• Todas las clases son dervidas de un objeto raíz
llamado Object; si no se menciona una superclase, se
asume Object.
class CardPile extends Object { ... };
class CardPile { ... };
• Las interfaces son conceptos nuevos en Java, ellas
definen un protocolo pero no una implementación.
public interface Storing {
void WriteFrom (Stream s);
void ReadFrom (Stream s);
};
63
Herencia en Java (2)
• Una clase puede indicar que implementa una interfaz;
las instancias de dicha clases pueden ser valores de
una variable declarados del tipo de la interfaz:
public class BitImage implements Storing {
void WriteOut (Stream s) {...};
void ReadOut(Stream s) {...};
};
• Solo herencia simple es soportada pero una clase
puede implementar varias interfaces; las interfaces por
otra parte pueden extender múltiples interfaces.
• La palabra reservada protected tiene el mismo
significado que en C++
64
Herencia en Java (3)
• La idea de subclase por especificación es
soportada en Java por el modificador abstract.
No es permitido crear instancias de una clase
abstracta, solo subclases. Definir una clase
como abstracta asegura que será usada
solamente como una especificación, no como
una implementación.
• Un método también puede ser declarado
abstract y así no necesita implementación;
debe ser sobrescrito por las subclases.
• El modificador final usado dentro de una clase
indica que la clase no puede tener subclases; el
modificador final usado en un método indica
que el método no puede ser modificado por un
65
refinamiento.
Herencia en Java (4)
• El constructor de una clase hija puede siempre
invocar al constructor de la clase padre, pero
esta invocación siempre toma lugar antes de
ejecutar el código del constructor.
• Si el constructor del padre necesita
argumentos, la seudovariable super es usada
como se fuera una función; si no existen
llamadas explicitas a super, el constructor por
defecto (el que no tenga argumentos) es usado.
Class DeckPile extends CardPile {
DeckPile (int x, int y, int c) {
super(x,y);
//initialise parent
...
//initialise child
};
66
Herencia en CLOS
• La herencia es indicada por la macro defclass al
enumerar las clases padres después del nombre de la
clase; la herencia múltiple se soporta.
(defclass TablePile (CardPile) ...)
• Ambos, slots locales y compartidos son heredados y
pueden ser sombreados; todos los especificadores de
slots son heredados y combinados.
• Las opciones de clase, incluyendo la inicialización por
defecto son heredadas y combinadas.
• Un Lista de precedencia de Clases es calculada para
controlar la combinación.
• Los métodos se heredan también.
67
Beneficios de la Herencia
• Reusabilidad del Software & Compartición de Código:
cuando el comportamiento se hereda de otra clase, el
código no necesita ser re-escrito; esto ahorra dinero,
reduce el tamaño e incrementa la confiabilidad.
• Consistencia de Interfaz: cuando muchas clases
heredan de una misma superclases es más fácil
garantizar que las interfaces a objeto similares son,
efectivamente, similares.
• Componentes
• Prototipos Rápidos
• Frameworks
68
Costo de la Herencia
• Velocidad de Ejecución: los métodos heredados, que
deben tratar con subclases arbitrarias, pueden ser más
lentos que código especializado.
• Tamaño del Programa: el uso de componentes de
software de una librería impone un costo en el tamaño
del programa.
• Sobrecarga de Paso de Mensajes: el paso de
mensajes es más costoso que el llamado a un
procedimiento.
• Complejidad del Programa: el abuso de la herencia
puede obscurecer el flujo del programa.
69
Mecanísmos para Reuso de Software
• Los dos mecanismos más comunes para el reuso de
software son la composición y la herencia.
• La relación “tiene-un” entre dos conceptos se sostiene
cuando uno es un componente del otro pero los dos en
cualquier caso no son la misma cosa.
• La relación “es-un” entre dos conceptos se sostiene
cuando uno es una instancia especializada del otro.
• El sentido común nos ayuda en la mayoría de casos:
un carro tiene un motor
un carro es un motor
un motor es un carro
un perro es un mamífero
un perro tiene un mamífero
un mamífero tiene un perro
70
Composición y Herencia:
el área gris
Lista
Lista
Cons
Car
Longitud
Miembro
Cons
Car
Length
Miembro
Conjunto
Conjunto
Elementos: Lista
Añadir
Tamaño
Miembro
Añadir
Tamaño
71
Composición
• Para reusar una abstracción de datos existente una
porción del estado de un nuevo tipo de datos es una
instancia de la estructura existente.
• Las operaciones en el nuevo tipo de datos son
implementadas usando las operaciones del tipo
existente.
• No existen promesa alguna de sustitución. Los dos
tipos de datos son completamente distintos.
• La composición puede existir tanto en lenguajes
orientados a objetos como no orientados a objetos; la
única diferencia significativa entre los lenguajes es
como la abstracción de datos encapsulada es creada e
inicializada.
72
Herencia
• Para reusar una abstracción de datos existente, el
nuevo tipo de datos se declara como una subclase de
un tipo de dato existente.
• Las operaciones en el tipo de datos existente son
heredadas al nuevo tipo de datos; la nueva clae puede
añadir nuevos datos y añadir o sobrescribir métodos.
• La herencia lleva la asunción implícita que las
subclases son de hecho subtipos; las instancias de una
nueva abstracción reaccionan de manera similar a las
instancias de la abstracción existente.
• La creación apropiada y la inicialización de la
abstracción padre depende del lenguaje.
• La herencia privada en C++ es apropiada pero rompe
el principio de sustitución.
73
Composición y Herencia
Contrastadas (1)
• La composición es más simple; se puede saber por la
declaración nada más que operaciones son soportadas por
el tipo de dato
• A través de la herencia las operaciones del nuevo tipo de
datos son un superconjunto de las operaciones del tipo de
dato original; dos o más declaraciones deben ser
inspeccionadas para conocer el conjunto completo.
• Las abstracciones de datos construidas a través de la
herencia son más cortas, las implementaciones también.
• La herencia no previene a los usuarios de manipular el
nuevo tipo de datos con las operaciones viejas violando el
principio de ocultamiento de la información y dejando
valores desprotegidos
74
Composición y Herencia
Contrastadas (2)
• En composición, los tipos de datos existentes se
convierten en mecanismos de almacenamiento para
nuevos tipos de datos y así en un detalle de
implementación. Es más fácil reimplementar el tipo de
dato con otra técnica con un impacto mínimo en los
usuarios del tipo de dato.
• La comprensión y mantenimiento son difíciles de
juzgar. La herencia tiene la ventaja de la brevedad del
código pero el programador tiene que comprender
ambas clases. El código de composición, a pesar de
ser más largo, es el único que el programador debe
entender.
• Las estructuras de datos implementadas a través de
herencia tiene una pequeña ventaja en tiempo de
ejecución dado que una llamada a función es evitada
(usando funciones en línea en C++)
75
Subclases y Subtipos
• El concepto de subclase es: una manera de
construir nuevos componentes de software a
partir de componentes ya existentes.
• El concepto de subtipo es más abstracto y tiene
que ver con el comportamiento, no con la
estructura. Lo que importa es la sustitución.
76
Subclases y Subtipos
• Los conceptos de subtipo y subclases no están
necesariamente relacionados. Dos clases pueden
responder al mismo conjunto de mensajes sin ningún
ancestro o implementación común. Si su respuesta a
los mensajes es suficientemente similar, pueden
substituirse el uno al otro.
• Los lenguajes de tipo estático eliminan esta distinción:
asumen que todas las subclases son subtipos cuando
esto no es completamente cierto cuando las subclases
sobrescriben el comportamiento de su padre.
77
Variables y Valores
“definevariable”
variable”
“define
My-Card
Mi-Carta
Card
Carta
“assign”
“make instance”
Mi-Carta
Magic Card
spade, 4, true
“make instance”
Carta Mágica
Espada, 4, true
“define variable”
<nombre>
<tipo>
Mi-Carta: Carta
(una Carta Mágica)
Palo: spade
Valor: 4
bocaArriba: true
“assign”
<variable>
<valor>
78
Variables Polimórficas
• En los lenguajes estáticos, el tipo estático de la variable
es fijo desde la declaración mientras que en los
dinámicos lo importante es el tipo que tiene el
contenido de la variable.
• Una de las características más importantes de los
lenguajes OO estáticos es que el tipo estático y
dinámico de una variable no son necesariamente el
mismo: una variable de tipo/clase A puede contener
instancias de cualquier subtipo/subclase de A. Estas
variables son llamadas polimórficas.
• En lenguajes dinámicos, todas las variables se puede
decir que son polimórficas.
79
Unión de Método:
Estatico ≠ Dinámico
Mi-Carta: Carta
(una Carta Mágica)
Palo: espada
Valor: 4
bocaArriba: true
• ¿Un mensaje debe ser
unido a un método basado
en el tipo estático o
dinámico de una variable
que es el objeto que recibe
el mensaje?
Cambiar-Valor(1)
80
Unión de Método
Ventana
MouseDown(int,int)
VentanaTexto
MouseDown(int,int)
• La diferencia es importante
cuando un método es
sobrescrito en una subclase
• La mayor parte del tiempo
se espera la unión al tipo
dinámico pero lo contrario a
veces es útil.
Ventana W;
VentanaTexto T();
W = T;
W.MousedDown(3,5);
81
Polimorfísmo Reverso
List
Set
Stack
List X; Set Y; Stack Z;
Set A();
Stack B();
X = A; Y = X;
X = B; Z = X;
• ¿Puede una instancia de una
subclases que es asignada una
variable con un tipo estático de su
superclase se asignada
nuevamente a una variable con el
tipo estático de la subclase?
• Existen dos preguntas: (1)
podemos establecer cuando el
valor de la variable es una
instancia de la subclase y (2) que
mecanismos son necesarios para
asignar el valor
• Es una pregunta importante
cuando se consideran las
82
colecciones.
Búsqueda de Método en C++ (1)
• Uno de los objetivos primarios de C++ es
eficiencia en espacio y tiempo. Por lo tanto
muchas de las características son estáticas en
vez de dinámicas.
• Las variables pueden ser polimórficas solo
cuando se utilizan punteros o referencias.
• Para variables “normales” el método de unión
es estático.
• Para punteros y referencias el método de unión
es dinámico cuando se utiliza la palabra clave
virtual en la declaración del método.
• Aún en este caso, el compilador debe verificar
la legalidad de los mensajes enviados usando
la clase estática del receptor.
83
Búsqueda de Método en C++ (2)
class Mammal {
public: void speak () {printf ("can't speak") };
};
class Dog1 : public Mammal {
public: void speak () { printf("woef") };
void bark () { printf("woef also") };
};
class Dog2 : public Mammal {
public: virtual void speak () { printf("woef") };
};
84
Búsqueda de Método en C++ (3)
Mammal fred;
Dog1 lassie;
Mammal * fido1 = new Dog1;
Mammal * fido2 = new Dog2;
fred.speak()
lassie.speak()
fido1->speak()
fido2->speak()
fido1->bark()
"can't speak"
"woef"
"can't speak"
"woef"
compiler error
85
Polimorfismo Reverso en C++
• Un dynamic_cast puede convertir entre una clase base
polimórfica a una clase derivada o hija; el operando
debe ser polimórfico porque la información del tipo en
tiempo de ejecución es necesaria y no esta disponible
para variables “normales”
fido2 = dynamic_cast <Dog2 *> (fido1)
• El operador typeid es usado para descubrir el tipo
exacto de un objeto; devuelve una referencia a un tipo
predefinido type-info typeid(*fido1)
• Para salida de diagnóstico, el nombre de la clase
puede ser recobrado como una cadena de la siguiente
manera.
typeid(*fido1).name()
86
Búsqueda de Método en Java
• Los mensajes son siempre unidos a los métodos
basados en el tipo dinámico del receptor.
• Los campos de datos pueden ser sobrescritos en Java
pero para accederlos Java se basa en el tipo estático.
• Las interfaces definen una organización jerárquica
similar, pero independiente de la jerarquía de clases.
Las interfaces pueden ser usadas como tipo sen la
declaración de variables. Las instancias de clases que
implementan una interfaz pueden ser asignadas a
estas variables. El tipo estático entonces es el tipo de
la interfaz mientras que el tipo dinámico es el tipo de la
clase. La unión de método usa el tipo dinámico.
87
Polimorfismo Reverso en Java
• En Java todas las variables conocen su tipo
dinámico; es posible probar el tipo con el
operador instanceOf
List L
if (L instanceOf Stack) ...
• Se permite el polimorfismo reverso con una
cast explícito. Cuando el cast es invalido una
excepción es arrojada.
Stack S
S = (Stack) L
88
Búsqueda de Método en CLOS
• Cuando una función genérica es llamada, el conjunto de
métodos asociados con la función genérica es ordenado
para encontrar/crear un método efectivo. Tres pasos son
llevados a cabo:
– Seleccionar los métodos aplicables: el parámetro specialiser es
una clase y el argumento deber ser una instancia de esa clase o
de una de sus subclases.
– Ordenar los métodos aplicables: cada clase tiene un lista de
precedencia de clases que ordena la clase y sus subperclases, la
lista de precedencia de clases de los argumentos es usada para
ordenar los métodos aplicables encontrados.
– Aplicar combinación de métodos para los métodos encontrados
89
Lista de precedencia de clases
food
fruit
beverage
sauce
alcohol
tomato worchester
bloody-mary
(bloody-mary tomato fruit
worchester sauce food alcohol
beverage standard-object t)
• Una lista de precedencia de
clases es calculada usando
las siguientes reglas:
– Una clase precede a su
superclase directa.
– El orden en la definición
defclass es mantenido en le
caso de herencia múltiple
– Cada superclase aparece
solo una vez.
• Si la jerarquía contiene un
ciclo, se retorna un error.
90
Combinación de Métodos (1)
• La combinación de métodos estándar distingue
métodos primarios y tres clases de métodos auxiliares
que modifican la acción. Estos métodos son indicados
con los calificadores :before :after :around
• Cuando todos los métodos aplicables son primarios:
– El método más específico es llamado primero
– Cuando otros métodos primarios están disponibles estos
pueden ser llamados explícitamente usando la función callnext-method la cual en cada paso invocara al siguiente
método primario más específico.
– El predicado next-method-p verifica si existe otro método
– El valor devuelto por el método primario más específico es el
valor devuelto por la función genérica.
91
Combinación de Métodos (2)
• Cuando existen métodos primarios y auxiliares:
– El método :around más específico es llamado y callnext-method puede ser usado para invocar el
siguiente método :around más específico.
– Cuando no hay más métodos :around, el método
call-next llamará a cada método :before en orden
– Luego del último método :before el método primario
más específico es llamado, el cual a su vez usa el
método call-next- para invocar otros métodos
primarios aplicables.
– Cuando ya no existen métodos primarios, todos los
métodos :after son llamados en orden
– Los métodos :before y :after son usados para
efectos colaterales, el valor devuelto es ignorado.
92
Combinación de Métodos
R1
R1
R2
R2
B1
B2
B3
P1
P3
P2
P2
Llamada a call-next-method
Retorno de call-next-method
Métodos llamados en secuencia
P3
A2
A1
Ri :around
Ai: after
Bi :before
Pi: primario
93
Ejemplo de Combinación de
Métodos (1)
(defclass person ()
((name :initarg :name :reader name)))
(defclass phd-holder (person) ())
(defclass professor (phd-holder) ())
(defmethod printname ((p person))
(format t (name p)))
(defmethod printname ((p phd-holder))
(format t "Dr. ")
(call-next-method))
(defmethod printname ((p professor))
(format t "Prof. ")
(call-next-method))
94
Ejemplo de Combinación de
Métodos (2)
? (setf bob (make-instance 'person
:name "Bob Brown"))
#<PERSON #x202A6D6>
? (setf viv (make-instance 'professor
:name "Viviane Jonckers"))
#<PROFESSOR #x202A876>
? (printname bob)
Bob Brown
NIL
? (printname viv)
Prof. Dr. Viviane Jonckers
NIL
95
Ejemplo de Combinación de
Métodos (3)
(defclass employe ()
((name :initarg :name :reader name)
(salary :initarg :salary :accessor salary)
(department :initarg :department
:accessor department)))
(defclass department ()
((name :initarg :name :reader name)
(expenses :initarg :expenses
:accessor expenses :initform 0)
(manager :initarg :manager
:accessor manager)))
96
Ejemplo de Combinación de
Métodos (4)
(defmethod give-raise ((e employe) amount)
(setf (salary e) (+ (salary e) amount)))
(defmethod give-raise :around ((e employe) amount)
(if (> (+ (salary e) amount) (salary (manager (department e))))
(format t "cannot give raise")
(call-next-method)))
(defmethod give-raise :before ((e employe) amount)
(setf (expenses (department e)) (+ (expenses (department e))
amount)))
(defmethod give-raise :after ((e employe) amount)
(format t "raise is given"))
97
Ejemplo de Combinación de
Métodos (5)
? (setf sales (make-instance 'department :name 'sales))
#<DEPARTMENT #x202D476>
? (setf bill (make-instance 'employe :name "bill gates" :salary
40000 :department sales))
#<EMPLOYE #x202D66E>
? (setf bob (make-instance 'employe :name "bob brown" :salary
30000 :department sales))
#<EMPLOYE #x202D866>
? (setf (manager sales) bill)
#<EMPLOYE #x202D66E>
98
Ejemplo de Combinación de
Métodos (6)
? (give-raise bob '5000)
raise is given
35000
? (salary bob)
35000
? (expenses sales)
5000
? (give-raise bob '10000)
cannot give raise
NIL
99
Reemplazo y Refinamiento
• Cuando una subclase simplemente añada datos o
métodos a una superclase, los datos y métodos del
padre y del hijo son diferentes.
• Cuando una subclase sobrescribe los datos o métodos
de la clase padre tanto la semántica del refinamiento y
reemplazo son posibles.
• El reemplazo de métodos implica que el código del
padre nunca se ejecuta cuando se manipulan
instancias del hijo.
• Con el refinamiento de métodos, el método heredado
del padre es ejecutado como parte de la ejecución del
método del hijo.
• El refinamiento de datos puede significar diferentes
cosas: tener ambos o combinar valores por defecto y
100
otras opciones.
Reemplazo y Sustitución
• La semántica del reemplazo no encaja con el principio
de sustitución: cuando las subclases son libres de
sobrescribir los métodos existentes con métodos que
pueden realizar acciones arbitrarias, no existe garantía
de que el comportamiento de la clase hija sea igual al
comportamiento de la clase padre.
• La mayoría de lenguajes ignora este problema y deja al
programador realizar las decisiones correctas.
• En Eiffel un programador puede añadir aserciones
(condiciones acerca del estado del objeto) a un
método. Las aserciones son heredadas y controladas
aún cuando el método se sobrescriba.
101
Refinamiento
• La Semántica del refinamiento soluciona el
conflicto entre sobrescritura y sustitución: en
vez de remplazar el código de la clase padre, la
acción descrita en la clase hijo es combinada
con las acciones descritas en la clase padre,
asegurando un mínimo nivel de compatibilidad.
• La utilidad de la semántica de refinamiento se
nota especialmente en la creación de nuevos
objetos: las inicializaciones tanto en la clase
padre y la clase hijo deben tener lugar.
• La mayoría de lenguajes soportan la semántica
de refinamiento a través de los mecanismos
que permiten que un método sobrescrito
102
invoque al mismo método de la clase padre.
Reemplazo en C++
• La sobrescritura en C++ es complicada por su
relación con la sobrecarga y la declaración de
métodos virtuales/no virtuales.
• Reemplazo simple ocurre solamente cuando los
argumentos de una clase hija no son los
mismos en tipo o número que los argumentos
en la clase padre y cuando el método es
declarado virtual.
• Sin la primera condición, la sobrecarga toma
lugar: hay dos métodos distintos.
• Sin la segunda condición la unión de método es
estática, así que el método de la clase padre el
103
que se ejecuta de cualquier manera.
Refinamiento en C++
• En C++ la invocación de un método puede ser calificada a
precisar específicamente la clase desde la cual el método
es derivado. La calificación se escribe como
nombredeclase::nombredemetodo
• La calificación puede ser usada para simular mecanismos
de refinamientos en la sobrescritura. Un método
sobrescrito puede explícitamente invocar el de la clase
padre para asegurar que los dos se ejecuten.
• Los constructores siempre usan refinamiento en vez de
reemplazo: el constructor de una clase padre puede ser
invocado explícitamente a través de una cláusula de
inicialización en el constructor de la clase hija, sino el
constructor por defecto del padre es invocado.
104
Reemplazo y Refinamiento en
JAVA
• El reemplazo ocurre cuando un método tiene la
misma firma que un método en la clase padre;
sino se produce sobrecarga.
• Los campos de datos pueden ser remplazados
o sombreados pero el reemplazo no es
dinámico, el campo de datos seleccionado será
determinado por el tipo estático de la variable,
no por su tipo dinámico.
• La palabra reservada final puede explícitamente
deshabilitar la sobrescritura.
• La seudo-variable super provee un mecanismo
para simular el refinamiento.
105
Reemplazo y Refinamiento en
CLOS
• El mecanismo de combinación de métodos
explicado anteriormente puede simular la
semántica del refinamiento a través de uso de
call-next-method. Sin la llamada a call-nextmethod, la semántica de reemplazo ocurre
debido a que el método más específico será el
llamado.
• Tanto slots locales como compartidos pueden
ser sobrescritos o sombreados. Las opciones
de slots y las opciones de clase son
apropiadamente combinadas cuando las
instancias son creadas.
106
Herencia múltiple
• Varias clasificaciones son posibles para una misma
entidad: Ecuatoriano, hombre, profesor, padre, etc…
• La herencia múltiple es apropiada cuando la relación
“es-un” se mantiene: un pintor de retratos es un pintor y
es un artísta
• La herencia simple se realiza frecuentemente para
especializar, la herencia múltiples se realiza para
combinar.
• La herencia múltiple es una característica útil y
poderosa en los lenguajes, pero crea muchos
problemas para el implementador del lenguaje.
107
Ambigüedad de Nombres (1)
MazoCarta
ObjetoGráfico
Dibujar
Dibujar
MazoGráfico
• Los dós significados
de dibujar chocan:
dibujar
• El problema lo tiene
la clase hija, no las
clases padre
• Una combinación de
renombre y
redefinición es la
solución estándar
en este caso.
108
Ambigüedad de Nombres (2)
Profesor
Estudiante
ImpTitulo
ImpTitulo
Ayudante
• Un solo significado
conceptual para
ImpTitulo pero un
método diferente
para cada clase
padre
• Renombrar no es la
solución, se necesita
un mecanismo que
permita al la clase
hija el decidir entre
precedencia o
combinación.
109
Herencia de Ancestros Comunes
Persona
Nombre
ImpTitulo
Profesor
Estudiante
ImpTitulo
ImpTitulo
Ayudante
• Cuando solamente el
comportamiento es
heredado de un ancestro
común , la técnica de
resolución usual es usada.
• Cuando los campos de
datos son los
involucrados, se decidir si
el campo de datos es
heredado una o dos veces
y si los constructores
deben ser invocados una
o dos veces.
110
Herencia Múltiple en C++
• Tanto renombrar y combinación de métodos se
pude suar a través de la calificación.
class GraphicalDeck: public GraphicalObject, public CardDeck {
public: void Draw() { GraphicalObject::Draw() };
Card ChooseCard() { CardDeck::Draw() };
...
class TeachingAssistant: public Teacher, public Student {
public: PrintTitle() { Teacher::PrintTitle();
Print(" ")
Student::PrintTitle(); };
...
111
Herencia Múltiple y Sobrecarga
Paramétrica en C++
• En C++ los métodos con el mismo nombre pero
diferentes firmas son distintos. El compilador
escoge el correcto basado en el los argumentos
en la llamada.
• Cuando dos clases padres definen un método
con el mismo nombre pero diferentes
parámetros se espera que la clase hija hereda
ambos métodos.
• Pero el compilado no podrá decidir entre el
métodos que encaje con el tipo del argumento y
el método encontrado es el primero encontrado
y es aplicable con conversión implícita de
112
parámetros; sino un error es arrojado.
Herencia Múltiple en Java
• Java no soporta herencia múltiple como tal,
pero:
– Una clase puede implementar múltiples interfaces
– Una interfaz puede extender múltiples interfaces
– Una clase puede extender otra clase e implementar
una interfaz
• Cuando una clase implementa múltiples
interfaces, los choques en nombre no afectan
dado que la implementación del método esta
dada en la clase de cualquier manera.
113
Herencia Múltiple en CLOS
• El método de combinación explicado
anteriormente implementa precisamente una
manera estándar de combinación de métodos.
• Cuando la combinación estándar de métodos
no produce el resultado esperado, nuevos tipos
de combinación de métodos pueden ser usados
a través de (muy sofisticado, pero muy
complicado) define-method-combination macro
114
Distribución en Memoria y Herencia
Ventana
int altura
int ancho
VentanaTexto
char *contenido
int ubicacion
• ¿Cuanto espacio debe ser
reservado para una
variable de tipo Ventana?
• Tres posibles respuestas:
– Espacio estático mínimo
– Espacio estático máximo
– Reserva dinámica de
memoria (usar punteros)
115
Reserva de Espacio MínimoMáximo Estático
• Reservar espacio para los
datos de la clase base
solamente.
• Usada en C++ para variables
que no usan punteros o
referencias que son
almacenadas en el stack
• Cuando se asigna una
instancia de una subclase
(más grande) a una variable
del tipo de la clase base se
pierden datos.
• Esta pérdida no es problema
ya que la unión de métodos
es estática para estas
variables.
• Reservar el espacio máximo
que la variable puede
contener (uniones y registros
variant)
• Este acercamiento no se usa
en ninguno de los principales
lenguajes OO. Debido a que
el tamaño máximo solo puede
ser conocido cuando el
programa completo es
escaneado, cualquier tipo de
compilación separada es
imposible.
116
Reserva Dinámica de Memoria
• Solo se crea espacio para un puntero en el
stack; el espacio para las instancias es
reservado en el heap donde las instancias son
creadas.
• Es usado en Java, SmallTalk, CLOS y en C++
cuando las variables son punteros o
referencias.
• En este acercamiento la asignación estándar
generalmente usa semántica de punteros, el
puntero, en vez del valor es transferido.
• Hacer copias reales, tanto superficiales como
profundas deben ser hechas explícitamente (ej.
Sobrecarga en C++ o el método clone de Java)
• La igualdad se vuelve un problema (ej. ==
sobrecarga en C++ o equal/eq en LISP)
117
Revisando el Polimorfismo
• El Polimorfismo (significa “muchas formas”)
tiene muchas caras:
–
–
–
–
–
–
–
–
–
Variables polimórficas
Sobrecarga de Operadores y Coerción
Sobrecarga Paramétrica
Polimorfismo Ad hoc
Sobrescritura
Métodos diferidos
Polimorfismo puro
Funciones genéricas y estructura de datos.
Plantillas y genéricos
118
Variables Polimórficas
• Término usado en lenguajes OO estáticos (C++, Java)
para referirse a variables en las cuales el tipo dinámico
de la variables puede se cualquier subclase del tipo
estático.
• En Java todas las variables de una clase son
polimórficas, en C++ solo los punteros y referencias a
variables son polimórficas.
• En lenguajes dinámicos cualquier variable es
polimórfica, aquí la relación de subclase no entra en
juego, las variables simplemente no tienen tipo
estático.
119
Sobrecarga
• Un procedimiento, función, método u operador esta
sobrecargado si existe una o algunas
implementaciones alternativas (cuerpos ) para ellos.
• Ocurre en lenguajes OO (Java, C++), en lenguajes
imperativos (Ada) y lenguajes funcionales (Lisp)
• Ante la llamada a procedimiento los mecanismos de
resolución determinan la implementación a usar.
• Las diferentes implementaciones asociadas con el
mismo nombre no necesariamente compartir
similaridad semántica. Por lo tanto el término
polimorfismo ad hoc es usado en algunas ocasiones.
120
Sobrecarga basada en ámbito
• La sobrecarga puede estar basada en el ámbito: una
función f definida dentro de una función g es distinta de
una función f definida dentro de una función :h, el
método :enviarflores en la clase florista es distinta del
método :enviarflores en la clase esposa.
• Para un procedimiento o función el ámbito de la
llamada será usado en el proceso de resolución; esto
se realiza típicamente en el tiempo de compilación
dada que la mayoría de lenguajes definen el ámbito por
la sintaxis del lenguaje.
• Todos los lenguajes OO permiten que existan métodos
con el mismo nombre en casos no relacionados. Es la
clase receptora del mensaje la que determina el
método que va a ser ejecutado. En el caso de clases
no relacionadas esto es también una decisión de
tiempo de compilación.
121
Sobrecarga Paramétrica
• Un estilo de sobrecarga donde los procedimientos en el
mismo contextos se permite que compartan un nombre
y se puede desambiguar por el número y tipo de los
argumentos
• En lenguajes estáticos el proceso de resolución, que es
la selección del cuerpo que encaje con una invocación
particular, es realizado en tiempo de compilación
basado en el tipo estático y los argumentos. El mejor
ejemplo son los operadores matemáticos.
• El principio de sustitución introduce una nueva forma
de coerción de objetos en lenguajes OO.
• Resolver el nombre de un procedimiento sobrecargado
puede ser muy complejo cuando la coerción automática
es soportada; errores en tiempo de compilación
ocurrirán cuando el proceso de resolución tenga
problemas.
122
Redefinición
• Ocurre cuando una clase hija define un método con el
mismo nombre que un método en la clase padre, pero
con una firma diferente.
• Los lenguajes de programación utilizan el modelo de
mezcla (Java) o el modelo jerárquico (C++) para
resolver la redefinición de nombre.
• El modelo de mezcla une todas las diferentes
implementaciones encontradas en todos los ámbitos en
una sola colección y el que más se asemeje es
seleccionado.
• En el modelo jerárquico cada ámbito es examinado en
turno y tan pronto como en un ámbito es encontrada
una implementación que se asemeje, esa será la
seleccionada.
123
Sobrescritura
• Un mecanismo de lenguajes OO donde en una
subclase un método es definido con el mismo nombre y
firma de un método que es heredado de la clase padre.
• Ambas semánticas, de reemplazo y refinamiento,
pueden ser aplicables.
• La sobrescritura es usualmente transparente para el
usuario de la subclase y las dos funciones son a
menudo vistas semánticamente como una sola entidad.
• La semántica de reemplazo hace que la semántica de
la similaridad no este necesariamente presente.
• Algunos lenguajes requieren que la sobrescritura sea
especificada explícitamente con una palabra reservada
124
en el padre, el hijo o en ambos.
Métodos Diferidos
• Es un caso especial de sobrescritura. El
comportamiento definido en la clase padre es
esencialmente nulo, toda la actividad útil es definida en
la clase hija.
• Se conoce en C++ como métodos virtuales puros
(virtual ... = 0), en Java como métodos abstractos
• La primera ventaja es conceptual: el método puede ser
introducido a un nivel mayor de abstracción que en el
que puede ser implementado.
• En lenguajes estáticos tiene un propósito práctico
también: el compilar puede verificar estáticamente los
mensajes enviados a una variable polimórfica.
125
Polimorfismo Puro
• Este término se reserva para situaciones donde una
función pude ser usada en con un variedad de
argumentos y donde el mismo cuerpo de función es
ejecutado en cada caso.
• Ocurre frecuentemente en lenguajes dinámicos (Lisp,
Scheme) ej. Funciones que manipulan una lista arbitraria
de parámetros. En este contexto los términos funciones
genéricas y estructuras de datos genéricas son usados.
• Ocurren en lenguajes OO cuando un método es definido
en una superclases, el método como tal es heredado por
las subclases pero el comportamiento es personalizado
cuando este envías más mensajes al receptor que
entonces se unen dinámicamente.
126
Genéricos y Plantillas
• Proveen una manera de parametrizar una clase
con un tipo.
• Introducidos en lenguajes estáticos para
permitir la creación y manipulación de
estructura de datos genéricas tal como un
contenedor reusable.
• Existe en C++ bajo el nombre de plantillas, es
usado extensamente en la librería de plantillas
estándar. También se conoce en Ada como
paquetes generales.
• Es el compilador el que genera el código
correcto para cada “instancia” de una plantilla;
así el cuerpo ejectudo es no genérico sino
127
específico.
Plantillas C++
template <class T> class List {
public:
void add(T);
T firstElement;
List<T> * nextElements;
};
List<int> A;
List<double> B;
template <class T> int length(List<T> & X) {
if (X==0) return 0;
return 1 + length(X.nextElements);
};
128
Clase Contenedor: Caso de Estudio
• Las estructuras de datos estándar como listas, colas,
conjuntos, árboles y diccionarios deberían ser
reutilizables entre diferentes proyectos.
• Al explorar el problema de desarrollar clases
contenedoras reusables, se necesita contestar a 3
preguntas:
– ¿Es posible construir un contenedor de propósito general que
sea independiente del tipo de sus elementos?
– ¿Puede este contenedor mantener solo un tipo de valores
(contendor homogéneo) o pude mantener diferentes tipos
(contenedor heterogéneo)?
– ¿Es posible dar acceso a los elementos contenidos sin
remover los elementos o sin exponer los detalles de
implementación interna del contenedor?
129
Soluciones Pre OO para el
Contenedor
• Tanto abstracciones homogéneas como
heterogéneas pueden ser construidas bastante
fácilmente en lenguajes dinámicos
• En lenguajes estáticos los contenedores se
pueden construir, pero:
– El tipo de elementos del contenedor es fijo, así que
su reusabilidad es limitada
– Registros Variant o uniones ayudan hasta cierto
punto a construir contenedores heterogéneos. Pero
solo se permite un número finito de alternativas y es
complicado probar y extraer los valores
– Problemas ocultando los detalles de
implementación. Ej. Al crear un lazo que recorra el
contenedor se debe crear una variable que revele la
estructura de la implementación interna.
130
Soluciones OO para el
Contenedor
• En lenguajes OO estáticos se pueden construir
contenedores homogéneos y heterogéneos, pero:
– El lenguaje soporta sustitución y todos los valores a ser
contenidos son instancias de subclases con una superclase
común
– Los objetos conocen su propio tipo (dinámico); pruebas
explícitas y conversiones son todavía necesarios para extraer
los valores pero puede estar ocultos tras la subclase.
• Iteradores explícitos pueden ser introducidos para
recorrer los elementos individuales del contenedor sin
necesidad de exponer la estructura de la
implementación del contenedor.
• Para construir listas homogéneas las plantillas son LA
solución.
131
Visibilidad y Dependencia
• La naturaleza interconectada del software es uno de
los mayores obstáculos en construir componentes de
software reusables. Ambas, visibilidad y dependencia,
se relacionan con al interconectividad.
• La visibilidad es una caracterización de nombres, ej.
Ámbito de las variables. Esta relacionada con la
conectividad porque cuando la visibilidad de los
nombres es controlada y reducida, la manera en que
un objeto puede ser usado se controla también.
• La dependencia ocurre cuando una unidad de software
no pude existir significativamente sin otra unidad. Se
relaciona con la conectividad porque cuando la
segunda unidad cambia, la primera deja de funcionar.
132
Aparejamiento y Cohesión
• El aparejamiento y la cohesión fueron introducidas en
el contexto de la programación modular y revisadas
luego en el contexto de la programación OO.
• El aparejamiento describe las relaciones entre módulos
o clases, la cohesión describe las relaciones dentro de
ellos.
• La reducción de la interconectividad entre módulos o
clases se logra a través de una reducción del
aparejamiento.
• Por otro lado, módulos y clases bien diseñadas
deberían tener un propósito: en un buen diseño los
elemento dentro de una clase o módulo deben tener
cohesión interna.
133
Variedades de Aparejamiento (1)
• (-) Interno de datos: una clase modifica
directamente ls valores locales de otra clase.
• (-) Dato Global: dos o más clases están unidas
a través de su dependencia de una estructura
de datos global
• (-) Control (o secuencia): una clase debe
realizar alguna operación en un orden
específico, pero otra clase es responsable de la
activación de estas operaciones.
134
Variedades de Aparejamiento (2)
• (-) Componente: una clase mantiene un campo
de datos que es una instancia de otra clase (+
si una sola vía)
• (+) Parámetros: una clase invoca los servicios
de otra y las únicas relaciones entre sus
miembros son el número y tipo de los
argumentos y el valor de retorno.
• (+) Subclase: la relación de una clase con su
padre
135
Variedades de Cohesión (1)
• (-) Coincidental: los elementos en un módulo
estan agrupados sin razón aparente, muchas
veces el resultado de “modularizar” un
programa grande.
• (±) Lógica: existe una conexión lógica entre los
elementos de un módulo pero no hay conexión
en sus datos o control, ej. Una librería de
funciones matemáticas.
• (±) Temporal: los elementos de un módulo
están agrupados porque todos ellos deben ser
usados aproximadamente al mismo tiempo. Ej.
El módulo que permite la inicialización del
136
programa.
Variedades de Cohesión (2)
• (±) Comunicación: los elementos de un módulo están
agrupados porque todos accesan los mismos datos o
dispositivos. Ej. El manejador de un dispositivo.
• (±) Secuencial: los elementos están agrupados en un
módulo porque necesitan ser activados en un oren
particular, muchas veces el resultado de evitar el
aparejamiento secuencial.
• (+) Funcional: todos los elementos del módulo o clase se
relacionan con la ejecución de una sola función.
• (+) Datos: cuando el módulo define un conjunto de valores
y exporta los operaciones para manipular esos datos, ej.
Cuando se implementa tipos de datos abstractos.
137
Aparejamiento y Cohesión en
Sistemas OO
• Una clase puede ser vista como una extensión de un
“módulo pequeño” con una diferencia importante, un
programa OO es habitado por un número de instancias
de las diferentes clases presentes en el programa.
• Las guías de diseño para módulos pueden traducirse
fácilmente en guías de diseño para objetos.
• Los objetos de distintas clases deberían tener tan poco
aparejamiento como sea posible.
• Por otra parte cada objeto debería tener un propósito
definido y cada método debería colaborar con ese
propósito de alguna manera.
138
La ley de Demeter (1)
• La ley de Demeter es una guía práctica que
trata de reducir el grado de aparejamiento entre
objetos al limitar sus interconexiones.
• En una primera forma la ley dice lo siguiente:
En un método M adjunto a una clase C, solo
métodos definidos por las siguientes clases
pueden ser usados:
(1) Las clases variables de instancia de C
(2) Las clases de los argumentos de M
(incluyendo C misma, objetos
globales y cualquier objeto creado
lógicamente en M)
139
La ley de Demeter (2)
• Cuando se rescribe en término de instancias en
vez de método, la ley dice:
Dentro de un método M es permitido
acceder o enviar mensaje a los siguientes
objetos:
(1) Los argumentos de M (incluido M)
(2) Variables de instancia del receptor del
método
(3) Variables globales
(4) Variables temporales creadas dentro del
método
• En su versión fuerte las variables de instancia
de la superclases son excluidas
140
Visibilidad a nivel de clase versus
a nivel de objeto
Point
X : int
Y : int
A:Point
X:2
Y:3
B:Point
X:1
Y:5
• Visibilidad a nivel de clase
permite que un objeto tenga
acceso al estado interno de
objetos hermano. La
visibilidad es controlada a
nivel de la clase
• Visibilidad a nivel de objeto no
permite que el estado interno
de objetos hermano, la
visibilidad esta controlada a
nivel de instancia
141
Subclases Clientes y Usuarios
Clientes
Window
Move
Resize
TextWindow
Edit
Resize
• Una clase tiene una cara
Game
pública: los datos y métodos
W: Window
que pueden ser accedidos y
Move
usados por clientes de esa
clase.
• Una clase tiene su propia cara
privada: los datos y métodos
que pueden ser accedidos
dentro de la clase solamente.
• Una clase tiene una tercera
cara: las características que
pueden ser accesible de
subclases pero no de usuarios
142
Control de Acceso en C++
• Las palabras reservadas public, private y protected son
los especificadores de acceso estándar; la palabra
clave final añade protección contra cambios de los
valores de los miembros de datos.
• El mecanismo de acceso protege contra accidentes, no
contra usuarios maliciosos; el uso de punteros y
referencias por ejemplo pueden fácilmente violar el
sistema de protección.
• Los especificadores de acceso operan a nivel de clase;
miembros privados y protegidos de instancias
hermanas pueden ser accedidos.
• Una función, clase o método puede ser declarado
friend de una clase y puede tener acceso a miembros
privados y protegidos.
143
Acceso y Visibilidad en C++
• Los especificadores de acceso controlan el
acceso, no la visibilidad; en el siguiente ejemplo
el control de la visibilidad da como resultado
una actualización de la variable global I y no un
error por ser I privado.
Int I:
// global variable
class A {
private: int I;
};
class B : public A {
public: void f () { I++ };!! error A::I is private
};
144
Espacio de Nombres en C++
• El espacio de nombres es una funcionalidad
que reduce la proliferación de nombres
globales. Antes la palabra reservada static
podía ser usada para limitar el ámbito de un
nombre a un archivo, pero cuando un nombre
necesitaba ser compartido entre dos o más
archivos, tenía que ser global.
• Estos nombres ahora pueden ser encerrados
en una definición de espacio de nombres y el
espacio de nombres puede ser incluido por una
directiva explícita en otro archivo o un ítem
sencillo puede ser importado.
Namespace myLib
class A {
... };
};
{
using namespace myLib;
using myLib::A;
using myLib::A test;
145
Control de Acceso en Java
• Las palabras reservadas public, private y protected son
los especificadores estándar, la palabra reservada final
añade protección adicional.
• Los especificadores trabajan a nivel de clase;
miembros privados y protegidos de instancias
hermanas pueden ser accedidos
• Un package agrupa clases e interfaces. Es usado
como un espacio de nombres. Un paquete puede ser
importado como un todo o clases o interfaces
individuales pueden ser importadas.
package myLib;
class A {
... };
import myLib.*;
import myLib.A;
new myLib.A();
146
Control de Acceso en CLOS
• Las opciones de slot :reader, :writer y :accessor
pueden ser usadas para controlar el acceso; sin
embargo la función del sistema slot-value da
acceso abierto a todos los valores.
• Los métodos son siempre “public”
147
Descargar