Está en la página 1de 14

Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

Parte

Programación
Orientada a 4
Programación B
Objetos
- Parte 1 -

Temario:
• Introducción
• Características
• Clases y Objetos
• Encapsulación, herencia, polimorfismo,
asociación y composición
• Implementación de objetos en Delphi
• Grupos de objetos: TList, TObjectList
• Ejemplo integrador de conocimientos

En este módulo se introducen los conceptos de diseño y programación orientada a


objetos, sus características y sus aplicaciones.

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

¿Qué es la Programación Orientada a Objetos?


El término de Programación Orientada a Objetos indica más una forma de diseño y una
metodología de desarrollo de software que un lenguaje de programación, ya que en realidad se puede
aplicar el Diseño Orientado a Objetos (En inglés abreviado OOD, Object Oriented Design), a cualquier
tipo de lenguaje de programación.

El desarrollo de la OOP empieza a destacar durante la década de lo 80 tomando en cuenta la


programación estructurada, a la que engloba y dotando al programador de nuevos elementos para el
análisis y desarrollo de software.

Básicamente la OOP permite a los programadores escribir software, de forma que esté
organizado en la misma manera que el problema que trata de modelizar. Los lenguajes de
programación convencionales son poco más que una lista de acciones a realizar sobre un conjunto de
datos en una determinada secuencia. Si en algún punto del programa modificamos la estructura de los
datos o la acción realizada sobre ellos, el programa cambia.

La OOP aporta un enfoque nuevo, convirtiendo la estructura de datos en el centro sobre el que
pivotan las operaciones. De esta forma, cualquier modificación de la estructura de datos tiene efecto
inmediato sobre las acciones a realizar sobre ella, siendo esta una de las diferencias radicales respecto
a la programación estructurada.

Para quienes no están familiarizados con la programación estructurada diré que una de las bases
de esta escuela de programación parte del diseño arriba – abajo. En esta forma de diseño se
descomponen los requerimientos del programa paso a paso, hasta llegar a un nivel que permite
expresarlos mediante procedimientos y funciones. La OOP estructura los datos en objetos que pueden
almacenar, manipular y combinar información.

En resumen, la programación estructurada presta atención al conjunto de acciones que


manipulan el flujo de datos (desde la situación inicial a la final), mientras que la programación
orientada a objetos presta atención a la interrelación que existe entre los datos y las acciones a realizar
con ellos.

Muchos habrán oído comentarios sobre la incidencia de la OOP sobre la programación


convencional. Se ha llegado a decir que el cambio introducido por la OOP es similar al producido por la
aparición del ensamblador sobre el código de máquina.

Ventajas de la POO
Uniformidad. Ya que la representación de los objetos lleva implica tanto el análisis como el
diseño y la codificación de los mismos.

Comprensión. Tanto los datos que componen los objetos, como los procedimientos que los
manipulan, están agrupados en clases, que se corresponden con las estructuras de información que el
programa trata.

Flexibilidad. Al tener relacionados los procedimientos que manipulan los datos con los datos a
tratar, cualquier cambio que se realice sobre ellos quedará reflejado automáticamente en cualquier
lugar donde estos datos aparezcan.

Estabilidad. Dado que permite un tratamiento diferenciado de aquellos objetos que permanecen
constantes en el tiempo sobre aquellos que cambian con frecuencia permite aislar las partes del

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

programa que permanecen inalterables en el tiempo.

Reusabilidad. La noción de objeto permite que programas que traten las mismas estructuras de
información reutilicen las definiciones de objetos empleadas en otros programas e incluso los
procedimientos que los manipulan. De esta forma, el desarrollo de un programa puede llegar a ser una
simple combinación de objetos ya definidos donde estos están relacionados de una manera particular.

Mantenibilidad. Cualidad que indica que un programa o sistema debe ser fácilmente modificable.
Es decir que los cambios en las condiciones externas (como la definición de una nueva variable)
implicarán modificaciones pequeñas en el programa / sistema. El concepto de mantenibilidad implica que
un programa, al igual que un ser vivo debe ser capaz de adaptarse a un medio ambiente siempre
cambiante.

Modelo de Objetos
Abstracción: “Supresión intencionada, u ocultamiento, de algunos detalles de un proceso o
artefacto, con el objeto de destacar de manera más clara otros aspectos, detalles o estructuras”

La abstracción de los objetos se denomina Clase. Las clases permiten la agrupación de objetos
que comparten las mismas propiedades y comportamiento. Si bien clase y objeto suelen usarse como
sinónimos, no lo son.
El esfuerzo del programador ante una aplicación orientada a objetos se centra en la identificación
de las clases, sus atributos y operaciones asociadas
Las propiedades de cada clase deben cumplir una serie de premisas
Las propiedades deber ser significativas dentro del entorno de la aplicación es decir, deben servir
para identificar claramente y de una manera única (y univoca) a cada uno de los objetos
El número de propiedades de un objeto debe ser el mínimo para realizar todas las operaciones
que requiera la aplicación.
Supongamos que queremos representar una estructura de gente dentro de una empresa,
inicialmente podemos definir una clase Empleado, que puede ser un Obrero aunque seguramente éste
tendrá un Jefe, también habrá Administrativos, Directivos, etc.
Esta clase puede tener como atributos un nombre, apellido, edad, sueldo, fecha de ingreso,
dirección, sexo, puesto. En esta clase podemos identificar algunas acciones, como por ejemplo:
ingresar, trabajar, renunciar.
Si observamos bien, al definir que todos son empleados, ya estamos haciendo una abstracción,
todos estos puestos comparten algo en común que es ser Empleado de la empresa.

type
TEmpleado = class (TObject)
nombre : String;
apellido : String;
edad : Integer;
puesto : String;
sueldo : real;
fechaIngreso : TDateTime;
procedure ingresar();
procedure trabajar();
procedure renunciar();
end;

Si pensamos un poco sobre el sueldo de un empleado, no será constante, podrá sufrir


variaciones entre los diferentes y seguramente será diferente de acuerdo al puesto del empleado, por lo
que convendría que existe algún procedimiento que nos permita saber el sueldo de un Empleado.

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

Construcción de clases
Tal como hemos definido con anterioridad, una clase de objeto describe a un grupo de objetos
con similares:
o Propiedades (atributos)
o Comportamientos (operaciones)
o Relaciones con otros objetos

La abreviatura clase es utilizada en lugar de clase de objetos. Los objetos difieren en los valores
asociados a sus atributos definidos dentro de la clase. Cada objeto <<conoce>> cuál es su clase. La
mayoría de los lenguajes orientados a objetos pueden determinar a que clase pertenece un objeto
durante la ejecución del programa.
A continuación expondremos una serie de pasos para definir una clase.

Identificar los objetos.

Para ello examine la aplicación e identifique las distintas estructuras de datos, algunos tips a
tener en cuenta son los siguientes:
* El nombre de la aplicación a veces nos da la del nombre del objeto principal
* Los objetos software pueden imitar el mundo real, modelizando las propiedades de los
objetos a través de variables Cualquier propiedad de un objeto puede ser identificada dentro del objeto
correspondiente a través de variables.
* Los objetos no se han de corresponder siempre con objetos físicos, sino que también
pueden ser entidades que se utilizan dentro de la construcción del programa.
* Piense en el objeto en <<primera persona>>. Este truco nos puede identificar claramente
los atributos y sus operaciones asociadas: <<Soy un cuadrado y me muevo, giro, agrando y reduzco.
Las partes que me componen son los puntos de mis vértices>>.
* Una clase es un tipo de dato que puede ser usado para declarar objetos, de la misma forma
que una estructura es un tipo definido por el usuario que puede utilizarse para declarar variables.

Definir las operaciones

Defina las operaciones a partir de los objetos, examinando las distintas operaciones asociadas a
un conjunto de datos. Los atributos del objeto se deben definir de tal manera que éstos satisfagan
todos los requerimientos de cada una de las operaciones.
A estas operaciones añada dos más: Crear y Destruir. Estas operaciones nos servirán para
inicializar y borrar el objeto dentro de la aplicación.
A partir de la definición de las propiedades, un objeto siempre debe ser capaz de responder a
estas tres preguntas: ¿Qué soy ?, ¿Qué hago? ¿Qué dejo ver al resto del mundo?
Algunas de las operaciones sólo se aplicarán a determinados objetos pertenecientes a las clases.
Hemos visto que a través de la herencia podemos <<especializar>> un sub conjunto de objetos
creando una sub-clase.
Únicamente aquellas operaciones que sean comunes a todos los objetos de la clase deben
incluirse dentro de las operaciones de la clase. El resto, que corresponden a las operaciones de sub-
Grupos de objetos, se deben definir dentro de la especializaciones de la clase.

Definir los atributos de los objetos

Una vez identificados los objetos, defina los atributos de la clase. Un atributo es un valor
almacenado en los objetos de la clase.

Aplicaciones orientadas a objetos

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

A lo largo de la historia de la programación, los lenguajes y las metodologías han pasado de una
relativa simplicidad a una complejidad creciente. Los lenguajes de programación orientados a objetos
pretenden aportar simplicidad a la tarea de programación de grandes aplicaciones.
Cuando se crearon las primeras computadoras todavía no existían los lenguajes de
programación, tal como ahora los entendemos. El lenguaje ensamblador puede considerarse como el
primer lenguaje de programación propiamente dicho. Permitía al usuario un diálogo más fluido con la
máquina a través de instrucciones que tenían relación directa con el conjunto de operaciones que la
máquina podía realizar.
A partir de este momento empezó la evolución de los lenguajes de programación. _cada uno
tenía su entorno definido y aunque en realidad todos los lenguajes son polivalentes (en teoría, con
cualquiera de ellos se puede desarrollar cualquier programa de gestión o científico). Pronto apareció la
especialización funcional. Así, COBOL (Common Business Orientated Language) se introdujo como
lenguaje mainframe para el diseño de aplicaciones de gestión.; FORTRAN (Formula Translator) para el
diseño de aplicaciones científicas; APL (A Programming Language) para el cálculo matemático, etc.
A medida que el software tomaba importancia, aparecieron los primeros problemas relacionados
con la programación. Al tiempo que aumenta el volumen de un programa, disminuye el control del
mismo por parte del programador y la capacidad de este de dar mantenimiento.
En un intento de solucionar estos problemas aparecen las metodologías de programación. Una
metodología es un conjunto de reglas destinadas a simplificar las tareas de diseño, estimación de
costes, desarrollo y mantenimiento de un sistema informático. A menudo se ven acompañadas con
unas herramientas (CASE: Computer Aided Software Engeneering) que permiten la elaboración
estructurada y documentada de los sistemas informáticos.

Características de los Objetos


Encapsulación: “Proceso de almacenar en un mismo compartimento los elementos de una
abstracción que constituyen su estructura y su comportamiento”
La capacidad de presentación de información dentro de un objeto se divide en dos partes bien
diferenciadas:
Interna: La información que necesita el objeto para operar y que es innecesaria para los demás
objetos de la aplicación. Estos atributos se denominada privados y tienen como marco de aplicación
únicamente a las operaciones asociadas al objeto.
Externa La que necesitan el resto de los objetos para interactuar con el objeto que definimos .
Estas propiedades se denominan públicas y corresponde a la información que necesitan conocer los
restantes objetos de la aplicación respecto del objeto definido para poder operar.

Podemos imaginarla encapsulación como introducir el objeto dentro de una caja negra donde
existen dos ranuras denominadas entrada y salida. Si introducimos datos por la entrada
automáticamente obtendrá un resultado en la salida. No necesita conocer ningún detalle del
funcionamiento interno de la caja.
El término encapsulación indica la capacidad que tienen los objetos de construir una cápsula a su
alrededor, ocultando la información que contienen (aquélla que es necesaria para su funcionamiento
interno, pero innecesaria para los demás objetos) a las otras clases que componen la aplicación.
Aunque a primera vista la encapsulación puede parecer superflua, tengamos en cuenta que
existen muchas variables utilizadas de forma temporal: contadores y variables que contienen
resultados intermedios, etc. De no ser por la encapsulación estas variables ocuparían memoria y
podrían interferir en el funcionamiento del resto de los objetos.
La encapsulación no es exclusiva de los lenguajes de programación orientados a objetos.
Aparece en los lenguajes basados en procedimientos (PASCAL, C, COBOL, ETC) como una forma de
proteger los datos que se manipulan dentro de las funciones.
Los lenguajes OOP incorporan la posibilidad de encapsular también las estructuras de datos que
sirven como base a las funciones. Aportan por tanto un nivel superior en cuanto a protección de
5

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

información.
La encapsulación nos permite el uso de librerías de objetos para el desarrollo de nuestros
programas. Recordemos que las librerías son definiciones de objetos de propósito general que se
incorporan a los programas. Al ser el objeto parcialmente independiente en su funcionamiento del
programa en donde está definido, ya que contiene y define todo lo que necesita para poder funcionar,
es fácil utilizarlo en los mas variados tipos de aplicaciones. Si aseguramos, depurando las propiedades
y las operaciones dentro de la clase que el objeto función bien dentro de una aplicación, con una
correcta encapsulación el objeto podrá funcionar en cualquier otra.
Otra de las ventajas de la encapsulación es que, al definir el objeto como una caja negra con
entradas y salida asociadas, en cualquier momento podemos cambiar el contenido de las operaciones
del objeto, de manera que no afecte al funcionamiento general del programa.
La encapsulación está en el núcleo de dos grandes pilares de la construcción de sistemas;
mantenibilidad y reusabilidad.

Vamos a codificar el ejemplo del Empleado esto y ver de que forma sabríamos el sueldo de un
empleado:

TEmpelado = Class(TObject)
nombre : String;
apellido : String;
edad : Integer;
puesto : String;
fechaIngreso : TDateTime;
procedure ingresar();
procedure trabajar();
procedure renunciar();
function calculaSueldo():real;
end;

function TEmpleado.calculaSueldo():real;
begin
if puesto = ‘Directivo’ then
Result := 15000;
if puesto = ‘Jefe’ then
Result := 5000;
if puesto = ‘Obrero’ then
Result := 1000;
end;

¿Qué otros atributos convendría encapsular dentro de procedimientos?

Herencia
La herencia consiste en la propagación de los atributos y las operaciones a través de distintas
sub-clases definidas a partir de una clase común.
Introduce, por tanto, una posibilidad de refinamiento sucesivo del concepto de clase. Nos
permite definir una clase principal y, a través de sucesivas aproximaciones, cualquier característica de
los objetos. A partir de ahora definiremos como sub-clases todas aquellas clases obtenidas mediante
refinamiento de una (o varias) clases principales.
La herencia nos permite crear estructuras jerárquicas de clases donde es posible la creación de
sub-clases que incluyan nuevas propiedades y atributos. Estas sub-clases admiten la definición de
nuevos atributos, así como crear, modificar o inhabilitar propiedades.
Para pensarlo de manera más fácil podemos abstraernos con el ejemplo que estuvimos
analizando. Un análisis más profundo, podría indicarnos que también existen Clientes, que no
comparten la característica de ser empleado de la empresa, también podrán existir proveedores y
otros personajes dentro de nuestro análisis.
Todos los involucrados dentro de nuestro análisis tienen en común que son Personas, en esta

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

etapa, tendríamos que identificar cuales son los atributos comunes a todas las personas: nombre,
apellido, fecha de nacimiento.
Pensemos en la jerarquía asociados a una Persona. Una persona se puede convertir en un
Obrero, que es un Empleado, y más tarde podría ascender a Jefe; o bien, una persona podría ser un
Cliente o un Proveedor. Las sub-clases aportarán sus propios atributos permitiendo la definición de
todos los posibles puestos.

PERSONA

EMPLEADO CLIENTE PROVEEDOR

OBRERO JEFE DIRECTIVO

La herencia es, sin duda alguna, una de las propiedades más importantes de la OOP, ya que
permite, a través de la definición de una clase básica, ir añadiendo propiedades a medida que sean
necesarias y, además, en el sub-conjunto de objetos que sea preciso.
La herencia permite que los objetos pueden compartir datos y comportamientos a través de las
diferentes sub-clases, sin incurrir en redundancia. Más importante que el ahorro de código, es la
claridad que aporta al identificar que las distintas operaciones sobre los objetos son en realidad una
misma cosa.

TPersona = class (TObject)


nombre : String;
apellido : String;
fechaNacimiento : TDateTime;
function calculaEdad():real;
end;

TEmpleado = class (TPersona)


puesto : String;
fechaIngreso : TDateTime;
procedure ingresar();
procedure trabajar();
procedure renunciar();
function calculaSueldo():real;
end;

TObrero = class (TEmpleado)


end;

TJefe = class (TEmpleado)


end;

TDirectivo = class (TEmpleado)


end;

Volvamos al ejemplo del cálculo del sueldo. Supongamos que hay varias acciones y cálculos que
dependen del puesto del Empleado, nos encontraremos con código lleno de sentencias “IF” que
verificarán cual es puesto y de acuerdo a él realizará algunos cálculos.
Si una vez implantado el sistema se crea un nuevo puesto en la empresa, deberemos modificar
todos los procedimientos y las funciones para tener en cuenta las diferentes formas de cálculo de

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

acuerdo al puesto.

Poliformismo
El polimorfismo es una nueva característica aportada por la OOP. Esta propiedad indica la
posibilidad de definir varias operaciones con el mismo nombre, diferenciándolas únicamente en los
parámetros de entrada. Dependiendo del objeto que se introduzca como parámetro de entrada, se
elegirá automáticamente cual de las operaciones se va a realizar.
Ya está habituado al operador <<suma>> que está presente en todos los lenguajes de
programación. Sin embargo, los operadores <<suma de fracciones>> y <<suma de números
complejos>> no existen en casi ningún lenguaje de programación.
Los lenguajes OOP permiten definir un operador <<suma>> tal que reconozca que tipo de
objeto se le está aplicando, a través de operaciones de objetos. Previamente deberá definir la fracción y
el número complejo como una clase y la operación suma como una operación de una clase.
Definiendo adecuadamente las operaciones suma de fracciones y suma de números imaginarios,
el operador suma devolverá, en el caso que los operandos sean fracciones, una fracción y , en el caso
de los números imaginarios, otros número imaginario.
Es posible extender el concepto e incluso definir operaciones como suma de bases de datos; el
operador suma de base de datos. Aunque a primera vista la expresión C= A+B, siendo A y B bases de
datos, nos pudiera parecer una extraordinaria simplificación, nos conduce a la pregunta: ¿Qué es la
suma de una base de datos?
Consideremos varias posibilidades:
Introducción de registros: Lo que exige que A y B tengan la misma estructura.
Unión de campos: Aquellos campos que aparezcan en B pero no en A serán añadidos a C

¿Alguna de estas dos opciones es verdaderamente una suma? Es decir ¿Cumple las propiedades
conmutativa, asociativa, de elemento neutro, etc.? ¿Qué ocurre si sumo dos bases de datos con
estructuras distintas?
Como puede observar, la definición de un operador sobre un tipo complejo de datos, intentando
utilizar identificadores de operadores de datos simples, puede tener resultados impredecibles.
En definitiva, lo que logra el polimorfismo es retardar la decisión sobre el tipo (o clase) del objeto
hasta el momento en que vaya a ser utilizado el método. En este sentido, el polimorfismo está asociado
a lo que se denomina vinculación tardía o vinculación en tiempo de ejecución.
Una de las ventajas más importantes, sin entrar en la redefinición de operadores es permitir la
realización de las clases que definen un programa de forma totalmente independiente al programa
donde se utilizan. Gracias a la encapsulación y el polimorfismo, aunque se utilicen los mismos nombres
con las operaciones en dos clases distintas, el programa reconoce a que clase se aplica durante la
ejecución.
Como se podrá observar el polimorfismo y la encapsulación de datos están íntimamente ligados y
nos permiten un mayor grado de mantenibilidad y reusabilidad que los lenguajes tradicionales Esta ese
precisamente una de las causas de la revolución que ha supuesto la introducción de los lenguajes
orientados a objetos dentro de la programación.

Asociación y composición
La herencia es una parte fundamental de los lenguajes orientados a objetos, hemos visto que
mediante la herencia podemos extender y especializar los objetos. Cuando sólo necesitamos ciertos
aspectos de una clase y no se utilizan todos los atributos y métodos, la herencia puede ser inapropiada
ya también expondría aquellos aspectos que no se necesitan. En estos casos es conveniente considerar
la utilización asociación o composición.
A nivel código, la asociación y la composición son similares: la principal diferencia radica en su
aplicación y en el nivel de encapsulamiento. Una consecuencia de estas diferencias es que la asociación
tiene un enlace más débil y la composición tiene un enlace más fuerte.
A diferencia de la herencia, la asociación y composición no esta soportado directamente por el
lenguaje. El programador debe crear las vías para utilizar la asociación y composición, enlazando
8

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

objetos y creando métodos para proveer acceso a las funcionalidades que serán accesibles.
En la composición, el programador debe crear los objetos que la constituyen y propagar
explícitamente las operaciones de la clase compuesta hacia las que las componen, como la liberación de
los objetos que componen el objeto liberado.
Esto significa que tanto la asociación como la composición requieren mayor programación que la
herencia pero permite una relación menos rígida y por consiguiente se simplifican los futuros cambios.
Para diferenciarlos de otra forma, podemos decir que la herencia representa una relación “es
un”; la asociación representa una relación “usa un” y la composición tiene una relación “tiene un”. La
herencia es un único objeto que tiene todas sus características más las que hereda puede realizar todas
las operaciones por si mismo; la asociación y composición encabezan un grupo de objetos que tiene la
responsabilidad de distribuir las diferentes operaciones entre los diferentes objetos
La composición acentúa la importancia del encapsulamiento, un objeto “compuesto” es dueño de
los objetos que lo constituyen, ya que debe crearlos, destruirlos y controla la vida de cada objeto.

De la teoría a la práctica
Atributos de visibilidad en Delphi
Para ampliar la característica de encapsulamiento y herencia, tenemos los diferentes niveles de
visibilidad que pueden ser aplicables tanto a los atributos como a los métodos de una clase:

Private: visible dentro de toda la Unit o unidad donde la clase declarada.


Public: visible desde cualquier lado en que la clase pueda ser referenciada.
Protected: posee misma visibilidad del private pero además puede ser accedido desde cualquier
clase descendente declarada en otra Unit o unidad diferente.
Published: tiene la misma visibilidad que un miembro Public, la diferencia concreta es que un
miembro Published genera información extra denominada "Run-Time Type Information" o de manera
abreviada RTTI. Esta información se usa normalmente con el fin de mostrar las propiedades en el
Object Inspector en tiempo de diseño.

Métodos abstractos, virtuales, redefinidos y sobrecargados

Como vimos, el polimorfismo es un concepto complejo, y muy ligado a la herencia y el


encapsulamiento. Por eso, vamos a empezar estudiando algunas propiedades que se asocian al
polimorfismo para luego llegar a un análisis del mismo.

Redefinición: La redefinición es una funcionalidad de la POO que se usa cuando el


comportamiento de la clase ancestro no es exactamente igual para la descendiente.
Por ejemplo, en el caso de los empleados, el método calculaSueldo() puede no ser igual para
todas las clases, aunque todas sean Empleados. En ese caso, tendríamos un calculaSueldo() para
TObrero, otro para TJefe, otra para TDirectivo, etc. Simplemente, esto se resuelve escribiendo
diferentes métodos calculaSueldo(), y decimos que el método calculaSueldo() de una clase
descendiente redefine al método calculaSueldo() de su ancestro. Esta redefinición se debe a que
ahora, cada vez que se invoque al método calculaSueldo() para una instancia de TObrero nos
estaremos refiriendo, no ya al que se hubiera heredado de TEmpleado, sino al redefinido en TObrero.
Por lo tanto, la llamada a una misma operación dependerá de la clase a la que pertenece el
objeto. Por ejemplo:
UnObrero.calculaSueldo();
UnJefe.calculaSueldo();
UnDirectivo.calculaSueldo();
Todos se refieren a la operación calculaSueldo(), pero en realidad están invocando a
implementaciones diferentes, según la clase del objeto para el cual se llama al método.

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

Como principios generales, se debe utilizar redefinición cuando en una subclase pueda realizarse
una implementación más eficiente o más completa que la de la clase ancestro. En consecuencia, la
redefinición:
• Será obligatoria, cuando el comportamiento de una clase descendiente sea diferente del
de la ancestro. Por ejemplo, si hay un método que se llama ImprimirAtributos que
imprime todos los atributos de un objeto, éste deberá redefinirse en las clases derivadas
si queremos que funcione correctamente.
• Será optativa, cuando por razones de eficiencia o claridad queramos modificar la
implementación del método del ancestro. Por ejemplo, el método para calcular la
longitud de una elipse probablemente funcione bien con círculos, pero su implementación
es mucho más complicada.
• Debe preservar la semántica de la definición del método en el ancestro. Es decir que la
tarea debe ser la misma, aunque se haga de diferente forma.

Tengamos en cuenta que la redefinición se introdujo para cambiar comportamiento de una clase
descendiente respecto del ancestro, no para introducir cambios en la estructura. Por lo tanto, se
pueden redefinir métodos, pero no atributos.
Los métodos privados no pueden ser redefinidos. Si se lo intenta, en realidad se estará
definiendo un nuevo método. Al fin y al cabo, la redefinición sólo tiene sentido si el método es parte de
la interfaz del ancestro, y los métodos privados no son parte de la interfaz.

Sobrecarga: La sobrecarga es un concepto que poco tiene que ver con POO. Existía
previamente en lenguajes como Pascal en la biblioteca estándar, como con los subprogramas Read,
Write y demás. En Ada, desde sus primeras versiones, se podía incluso definir por el programador.
Además, no tiene nada que ver con la vinculación dinámica o el polimorfismo.
Sin embargo, hay algunas interacciones con el concepto de redefinición que nos interesa
analizar. La relación más conflictiva de la sobrecarga y la redefinición se da cuando redefinimos un
método en una clase descendiente con un encabezado distinto al del método ancestro. En este caso,
¿se efectuará una redefinición, en el sentido de ocultar la definición hecha en la clase base? ¿o más
bien se la tratará de un nuevo método que sobrecargará a los de la clase ancestro? La respuesta a esta
pregunta depende de cada lenguaje.
En Object Pascal, como dijimos, se debe poner la palabra overload en todos los métodos que se
sobrecargan a un mismo nivel. Del mismo modo, si se quiere sobrecargar un método de una clase
ancestro se debe indicar claramente con la palabra overload en la descendiente. Si no se hiciera así, se
toma el nuevo método como una redefinición, y esto impide el acceso al método de la clase ancestro
desde la descendiente.
En Java y en C++, cuando se utilizan encabezados diferentes en una clase ancestro y una
descendiente, esto no redefine el método del ancestro sino que provoca una sobrecarga. Como
corolario, los métodos que no se redefinan con el mismo encabezado de la clase base, siguen estando
disponibles para la clase descendiente.

Métodos virtuales: Para permitir el polimorfismo, un método puede ser declarado virtual o
dinámico en una clase base y luego sobredeclarado si es necesario en las clases derivadas.
¿Cómo se hace para que cada vez se llame al método de cálculo de sueldo correcto?
Precisamente, si se hace vinculación tardía, podemos lograr que el calculaSueldo() que se
invoque corresponda siempre al objeto para el cual se invocó.

Los métodos que son vinculados dinámicamente o tardíamente se denominan métodos virtuales.
En los ejemplos anteriores, calculaSueldo() debería ser un método virtual para garantizar la
vinculación tardía.
Hay lenguajes, como Smalltalk y Java, que toman la vinculación tardía por omisión, por lo cual
todos los métodos son virtuales. Otros, como C++ y Object Pascal, requieren que el programador
especifique el método como virtual explícitamente.
10

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

Las reglas generales para el uso de métodos virtuales son que un método debe ser virtual en
alguna de estas circunstancias:
• Cuando está redefinido y es llamado desde un método no redefinido en la clase ancestro.
• Cuando está redefinido y es invocado desde un método de una clase compuesta con
algún objeto de una clase ancestro.
Los métodos virtuales suelen consumir más recursos (memoria y/o tiempo de ejecución) que los
estáticos. Sin embargo, en orden a la reutilización, ante la duda, un método debe ser virtual.

TEmpleado = class (TPersona)


puesto : String;
fechaIngreso : TDateTime;
function calculaSueldo():real; virtual;
end;

TObrero = class (TEmpleado)


function calculaSueldo():real; override;
end;

TJefe = class (TEmpleado)


function calculaSueldo():real; override;
end;

TDirectivo = class (TEmpleado)


function calculaSueldo():real; override;
end;

Constructores y destructores virtuales


Los destructores casi siempre son virtuales. De ese modo, si se escribe un método en una clase
base que invoca a un destructor virtual, se puede asegurar que siempre llama al destructor de la clase
correcta.
Por ejemplo, en Object Pascal, el destructor Destroy es virtual, de allí que en todos nuestros
ejemplos en ese lenguaje hayamos puesto la directiva override en los Destroy redefinidos. A su vez,
como Destroy no hace una verificación de que la referencia esté sin apuntar a nada, existe un método
que llama al Destroy de TObject que se denomina Free, que hace esta verificación. A su vez, también
en TObject existe el método FreeAndNil, que hace lo mismo que Free pero poniendo la referencia en nil,
de modo que es el más seguro de los tres. La idea, entonces, es redefinir Destroy en todas las clases
en que sea necesario, pero invocar siempre a FreeAndNil, que por estar definido en TObject se pude
utilizar en todas las clases sin redefinirlo, y estamos asimismo seguros de que invoca al Destroy de
nuestra clase por ser éste virtual.
En C++ es importante que los destructores sean virtuales por las mismas razones. Los
constructores, en cambio, no suelen ser virtuales. Para empezar, un constructor se usa para crear un
objeto, parece un poco raro crear uno sin saber su clase de antemano. De hecho, ni en C++ ni en
Object Pascal se pueden declarar constructores virtuales, salvo haciendo algunos artificios.

Clases abstractas A veces es necesario definir clases para las cuales no tiene sentido crear
instancias.
Un caso podría ser nuestra ya habitual TEmpleado, dado que las clases del nivel inmediato
inferior cubren todos los casos posibles. Es decir, un empelado en particular será TObrero, TJefe o
TDirectivo, pero nunca directamente una TEmpleado. En cualquier taxonomía ocurre lo mismo.
Estas clases se denominan clases abstractas, y tienen como única finalidad la declaración de
atributos y métodos comunes que luego se utilizarán en las clases descendientes. Dicho formalmente,
se construyen para generalizar estructura y comportamiento comunes de varias clases descendientes.
En otros casos ni siquiera poseen atributos o métodos, sino que sólo se las define para luego declarar
11

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

subclases que por algún motivo se las desea tratar como de la misma familia.
En el ejemplo anterior, aunque TEmpelado no tenga instancias, nos sirvió para declarar el
método CalculaSueldo, de modo que las clases descendientes lo pudieran usar.
Las interfaces son un caso especial de clase abstracta. Por eso, si una clase abstracta no necesita
implementar métodos o atributos, habría que analizar si no debiera ser una interfaz.

Métodos abstractos Hay otro aspecto parecido al de las clases abstractas. Lo explicaremos con
el ejemplo que estamos analizando.
El método CalculaSueldo de la clase TEmpleado nunca será invocado, pues no hay instancias de
TEmpleado y está redefinido en todas las clases descendientes. Una primera solución, incorrecta, sería
suprimirlo y dejarlo solamente en las clases hijas. Sin embargo, si hacemos eso, nos encontraremos
con que el método CalculaSueldo de TEmpelado no podría ser compilado, ya que en su código fuente
invoca a CalculaSueldo, que no está declarado en la clase TEmpleado ni en ninguno de sus ancestros.
La mejor solución, entonces, sería escribir el método CalculaSueldo en TEmpleado, convirtiéndolo
en método abstracto, esto es, en un método que no puede ser invocado por ninguna instancia de la
clase TEmpleado, aunque existiera, pero que sirve para que se pueda escribir el método CalculaSueldo
de TEmpleado tal como se hizo.
Como consecuencia de lo dicho anteriormente, los métodos abstractos:
• Siempre deben ser redefinidos, y en algún nivel inferior de la jerarquía deben dejar de
ser abstractos, ya que no se los puede invocar directamente.
• Deben ser virtuales.
• No tienen código fuente ejecutable.
• Son fundamentales en jerarquías de una complejidad mediana.
• Son necesarios cuando en la clase ancestro hay que utilizarlos en la implementación de
otro método (como explicamos en el ejemplo con TEmpelado).

TEmpleado = class (TPersona)


puesto : String;
fechaIngreso : TDateTime;
function calculaSueldo():real; virtual; abstract;
end;

12

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

Ejercicio Integrador de conocimientos

Tenemos una empresa de transporte y remisería en la que se encuentran disponibles diferentes


tipos de vehículos, existen autos, camionetas, limosina, combis y camiones. La tarifa varía
dependiendo del tipo de vehículo que se utilice, el precio se estipula se cobra por kilómetro
recorrido:
• Auto $8 por km. (Terrestre, 2 ejes)
• Camioneta $ 12 por km. (Terrestre, 2 ejes)
• Limosina $20 por km. (Terrestre, 2 ejes)
• Combis $5 por km por persona que lleve (la cantidad de persona esta preestablecida
al momento de contratar el servicio) (Terrestre, 4 ejes)
• Camiones $2 por km por kilo que lleve (la carga esta preestablecida al momento de
contratar el servicio) (Terrestre, 8 ejes)

1. Identificar los objetos teniendo en cuenta la jerarquía de herencia, diseñar las clases
necesarias y crear una aplicación que utilice objetos.
2. Armar una interfaz gráfica sencilla que permita seleccionar el tipo de vehiculo a utilizar y la
cantidad de kilómetros/pasajeros/kilos de nuestro viaje, de manera que el sistema calcule el
precio del viaje.

En cualquier momento se debe poder calcular los importes facturados por los viajes realizados,
detallando el importe de comisión de la remisería teniendo en cuenta que:

3. Todos los vehículos tienen una bajada de bandera (inicio de viaje) de 5$. Modifique el
ejercicio anterior y calcule el precio de cada viaje.
4. La remisería gana el 5% de cada viaje si el mismo es superior a 100$, en caso contrario se
retiene el importe de bajada de bandera, mostrar para cada viaje cuanto cobra la remiseria.

Ampliación de aplicación:

5. La remisería agrego dos servicios aéreos: ahora tiene un helicóptero el cual sale 50$ por Km
y un Jet privado que sale $85 por Km.
5. Modificar el procedimiento de cálculo de importes recaudados y comisiones, discriminando
además el importe que la remisería debe pagar a los choferes que será del: 8% si es chofer de
un vehiculo de 2 ejes; 9,5% si es un chofer de un vehiculo de 4 ejes; 10,5% si es un vehiculo
de 8 ejes y un 12% si es un piloto de vehiculo aéreo.

13

Ingeniería Informática
Universidad FASTA
Programación B – Programación Orientada a Objetos - APUNTE PRELIMINAR -

Sumario

Programación B

Parte 4

Programación Orientada a Objetos

Ingeniería Informática
Universidad FASTA

14

Ingeniería Informática
Universidad FASTA