9  Introducción CyO

En esta sección se hará una revisión de lo que son clases y objetos (CyO), a manera de una pequeña introducción a la programación bajo el paradigma orientado a objetos.

En esta revisión se hace mención a temas de clases y objetos relacionados con encapsulamiento, ocultamiento de la información, herencia, polimorfismo y sobrecarga de operadores.

Se espera que al finalizar las actividades de esta sección, el estudiante tenga claras las principales características de clases y objetos, ya sea para su uso dentro del paradigma de programación orientada a objetos (POO), o como un elemento adicional que se puede utilizar dentro del paradigma de programación imperativa estructurada procedimental (PIEP).

9.1 Mecanismos de abstracción

Los primeros programadores de software han utilizado diferentes métodos de abstracción antes de llegar al paradigma de la programación orientada a objetos. Desde una perspectiva histórica, el uso de la abstracción por la programación orientada objetos no es más que la progresión natural de la abstracción luego de ir desde funciones hasta tipos abstractos de datos.

9.1.1 Funciones y procedimientos

Las funciones y procedimientos son los mecanismos de abstracción más antiguos y más ampliamente utilizados por los programas. Las funciones permiten que determinadas tareas se puedan utilizar en muchos sitios, incluso en diferentes aplicaciones, se recogen en un lugar y se reutilizan. Los procedimientos permiten a los programadores organizar tareas repetitivas de una sola vez. Estas dos abstracciones evitan duplicaciones de código.

Las funciones y procedimientos permiten a los programadores la capacidad de implementar el ocultamiento de la información. Un programador escribe una función o conjunto de funciones que se utilizarán por muchos otros programadores. Otros programadores que no necesitan conocer los detalles exactos de la implementación, sólo necesitan conocer la interfaz (es decir, lo que debe entrar y lo que debe salir del subprograma respectivo). Desgraciadamente, las funciones no llegan a ser el mecanismo más eficiente para un completo ocultamiento de información.

9.1.2 Tipos de datos abstractos

Un tipo abstracto de datos es un tipo de dato definido por el programador que se puede manipular de un modo similar a un tipo de dato predefinido o preexistente. Los programadores pueden crear instancias de tipos abstractos de datos asignando valores válidos a las variables de dichos tipos. Además se pueden utilizar las funciones para manipular los valores asignados a las variables.

En síntesis, los tipos datos abstractos permiten las siguientes tareas:

  1. Extender un lenguaje de programación añadiendo tipos de datos definidos por el usuario.
  2. Poner a disposición de otro código, un conjunto de funciones definidas por el programador, que se utilizan para manipular los valores de las variables de los tipos definidos por el programador.
  3. Proteger (ocultar) los datos asociados a las instancias con el tipo y limitar el acceso a los datos, haciendo que sólo por las funciones definidas por el programador puedan acceder a los datos.
  4. Hacer tantas distancias como se desee del tipo de dato definido por el programador.

9.2 Modelado del mundo

Uno de los problemas (y tal vez el más importante) del paradigma procedimental es que la disposición independiente de datos y funciones realiza un pobre modelado de las cosas tangibles e intangibles del mundo real. En el mundo físico, se interactua con objetos tales como personas y autos. Dichos objetos no son ni como los datos ni como las funciones. Los objetos complejos del mundo real tienen atributos, comportamiento e identidad.

Un objeto no sólo encapsula su estado (variables) sino también su comportamiento. El comportamiento de un objeto se describe en términos de servicios (operaciones) proporcionados por ese objeto que modifican o inspeccionan el estado del objeto. Estos servicios se invocan enviando mensajes, del objeto que solicita el servicio, al objeto sobre el que se envía el mensaje. Normalmente el único medio para acceder a un objeto es a través de las operaciones. Estas operaciones, por consiguiente, actúan como una interfaz al objeto.

9.2.1 Atributos

Los atributos son las propiedades de los objetos (a veces denominados características). Por ejemplo, en el caso de personas, el color de los ojos, el título de un empleado, y en el caso de autos, la potencia, el número de puertas o el modelo. A su vez los atributos del mundo real son equivalentes a los datos de un programa, tienen un cierto valor específico, tal como azul (color de lo ojos), 150 CV (potencia en caballos de vapor en automóviles) o cinco (para el número de puertas).

Los atributos representan la identificación y propiedades descriptivas. A nivel de lenguaje de programación los objetos que tienen el mismo conjunto de atributos se dice pertenecen a la misma clase.

El estado de un objeto agrupa los valores instantáneos de todos los atributos del mismo, donde un atributo es una información que cualifica al objeto. Cada atributo puede tomar un valor en un ámbito dado. El estado de un objeto, en un instante dado, corresponde a una selección de valores, entre todos los valores posibles para cada atributo. El estado del objeto puede cambiar con el tiempo.

Por ejemplo, un auto perteneciente a la clase Auto tiene los atributos: Marca, color, peso y potencia.

Un objeto de la clase Auto tendría ciertos valores para dichos atributos:

  • Audi
  • Azul cielo
  • 1.100 Kg
  • 150 CV

El estado del auto es variable, es decir, los valores de los atributos podrían ser unos en un momento y otros en otro momento. Algunos componentes son constantes (marca, país de su fabricación, etc.). La cantidad de litros en el tanque de gasolina varía a medida que se usa el auto; el color puede cambiar si su dueño decide pintarlo con un color distinto al de fábrica.

9.2.2 Comportamiento

El comportamiento es algo que un objeto del mundo real hace en respuesta a cualquier estímulo. Por ejemplo, si usted solicita a su empresa un aumento de sueldo, normalmente le contestarán si o no. Si se aplica una acción sobre los frenos de un coche, normalmente, se detendrá. “Aumentar el sueldo” y “detener” son ejemplos de comportamiento. El comportamiento es como una función, una función es llamada para que haga al y ese algo se hace.

El comportamiento agrupa todas las competencias de un objeto y describe las acciones y reacciones de ese objeto. Cada elemento de comportamiento se denomina operación. Las operaciones de un objeto se ejecutan a consecuencia de un estímulo externo, representado en forma de un mensaje enviado por sí mismo o por otro objeto.

9.2.3 Identidad

La identidad es una propiedad fijada por la cual se identifica a un objeto de otro. Si tiene dos tazas de café del mismo juego, se dice que son iguales pero no idénticas. Ambas son de igual tamaño, color, forma, material, están vacías (o llenas), etc, es decir, son iguales, pero no son idénticas, ya que usted puede elegir para tomar su café una u otra.

Un objeto posee una identidad que caracteriza su propia existencia. La identidad permite distinguir los objetos de una manera no ambigua, independientemente de su estado. Esto permite distinguir dos objetos en los que todos los valores de sus atributos son idénticos.

9.2.4 Paso de mensajes

Una acción se inicia por una petición de un servicio (mensaje) que se envía a un objeto específico. El paradigma de programación imperativo estructurado procedimental presta especial importancia a las funciones, mientras que el paradigma orientado a objetos proporciona especial importancia al objeto. Por ejemplo, se puede llamar a una función para que ponga un dato en un tipo de dato “cola” o bien transmitir un mensaje al objeto “cola” para que realice la acción de recibir o poner un valor en sí misma. El resultado puede llegar a ser el mismo pero el proceso para llegar a dicho resultado no es el mismo.

El paso de mensajes proporciona la capacidad de sobrecargar nombres y reutilizar software; esto no es posible utilizando el paradigmo imperativo. Implícito a la idea de mensaje es la idea de que la interpretación de un mensaje puede variar para objetos de diferentes clases. Es decir, el comportamiento dependerá de la clase de objeto que recibe el mensaje. El mensaje/servicio “poner” puede significar una cosa en objetos de la clase “cola” y otra muy distinta en cualquier otro objeto.

Con frecuencia, hay servicios/mensajes para solicitar cierta información de un objeto. Esta información puede ser relativa al estado de la instancia del objeto, pero también puede implicar un cálculo de algún tipo.

9.3 El enfoque orientado a objetos

La idea fundamental que subyace en los lenguajes orientados a objetos es combinar en una única unidad tanto datos como funciones que operan sobre esos datos, tal unidad se denomina objeto. Un objeto es una abstracción de algo en un dominio del problema a resolver.

El mecanismo/concepto fundamental de la programación orientada a objetos es el objeto. Un objeto consta de los atributos (datos) y los métodos (subalgoritmos) que actúan sobre los datos. Los datos no deben ser accesibles directamente a los usuarios del objeto. El acceso a los datos se debe garantizar únicamente por los métodos proporcionados por el objeto. La orientación a objetos se denomina así porque este método ve las cosas que son parte del mundo real como objetos. Un teléfono es un objeto; la silla en que está sentado es un objeto; el libro que está leyendo es un objeto, etc. De igual forma son objetos, una bicicleta, una póliza de seguros, etc. Los objetos individuales de una clase se llaman instancias de esa clase. Por ejemplo, Clase: Mesa. Instancias: Mi mesa, Tu mesa, etc. Estas instancias tienen los mismos atributos y posiblemente diferentes valores (es decir, con un estado diferente).

Los objetos que tienen el mismo conjunto de atributos se dice que pertenecen a la misma clase. Los objetos individuales de una clase se llaman instancias de esa clase. Las instancias tienen los mismos atributos pero con valores posiblemente diferentes (es decir, con estados posiblemente distintos).

En el paradigma de programación orientado a objetos se encapsula datos (atributos) y métodos (comportamiento) en objetos. Los datos y métodos de un objeto se conectan juntos. Los objetos tienen la propiedad del ocultamiento de la información. Esto significa que aunque los objetos pueden conocer como comunicarse con otros objetos a través de interfaces bien definidas, los objetos normalmente no están autorizados a conocer como se implementan otros objetos (los detalles de implementación se ocultan dentro de los propios objetos). Se puede conducir un auto eficientemente sin conocer los detalles de cómo funciona internamente el sistema de transmisión, los frenos o el motor.

Los programadores bajo el paradigma imperativo estructurado procedimental se centran en escribir funciones. Los grupos de acciones que realizan algunas tareas se forman en funciones y las funciones se agrupan para formar programas. Los datos son muy importantes como soporte a las acciones que se ejecutan.

Los programadores orientados a objetos se centran en crear sus “propios tipos de variables” denominados clases. Cada clase declara o especifica los datos, así como el conjunto de métodos que manipulan los datos. Igual que se llama variable a una instancia de un tipo predefinido (por ejemplo, int), una instancia de una clase se denomina objeto. Las clases se utilizan entonces para instanciar objetos que al trabajar juntos como un sistema resolverán el problema de interés.

Los métodos (funciones) de un objeto proporcionan la única manera para acceder a sus datos. Si se desean leer los datos de un objeto, se llama a una función miembro del objeto. No se debería poder acceder a los datos directamente. Los datos están ocultos, así están protegidos de modificaciones accidentales. Los datos y las funciones se dice que están encapsulados en una única entidad. Si se desea modificar los datos de un objeto, se conoce exactamente cuales métodos interactúan con dichos datos. Ninguna otra función puede acceder a los datos. Esto simplifica la escritura, depuración y mantenimiento del programa.

Listados con objetos del mundo real que se pueden corresponden con objetos en programación orientada a objetos:

Objetos físicos

  • Automóviles en un sistema de simulación de tráficos.
  • Camiones en una empresa transportista.
  • Componentes eléctricos en un programa de diseño de circuitos.
  • Países en un modelo de comercio.
  • Aviones en un sistema de tráfico aéreo.

Elementos de un sistema informático

  • Unidad central, teclado, impresora, unidad de discos, unidad de DVDCDRW, etc.
  • Menús.
  • Ventanas.
  • Objetos gráficos (líneas, rectángulos, circulos, etc.).

Estructuras de datos

  • Pilas.
  • Colas.
  • Listas enlazadas.
  • Árboles binarios.
  • Árboles binarios de búsqueda.
  • Dobles colas.

Tipos de datos definidos por el usuario

  • El tiempo en formato horario.
  • Números complejos.
  • Puntos de un plano.
  • Ángulos de un sistema.
  • Fecha de un día.

Recursos Humanos

  • Empleados.
  • Clientes.
  • Proveedores.
  • Socios.
  • Vendedores.

9.4 Clases

Casi todos los lenguajes de programación tienen tipos de datos incorporados. Por ejemplo, un tipo de dato entero (int) está predefinido en varios lenguajes, y si así se desea, se pueden declarar tantas variables de tipo int como sea necesario en su programa:

  • int día
  • int mes
  • int divisor
  • int salario

De modo similar se pueden definir muchos objetos de la misma clase. En una clase se especifica qué datos (atributos) y funciones (métodos) tendrán los objetos de esa clase. La definición de la clase no crea objetos, al igual que la simple existencia de tipos de datos no crea ninguna variable. Una clase es por consiguiente una descripción de un número determinado de objetos similares. Ricky Martin, Chayanne y Shakira son miembros de la clase “cantante latino”. No hay ninguna persona llamada “cantante latino” sin embargo hay personas específicas con nombres específicos que son miembros de esta clase si poseen unas características determinadas.

9.4.1 Identificación y responsabilidad de una clase

Cinco grandes categorías facilitan la identificación de qué tipos de cosas son clases:

Cosas tangibles

Avión Fuente de alimentación Libro Auto
Reactor nuclear Circuito de frenos Perro Moto
Caballo de carreras Parque nacional Gato Banco
Curso Grupo de clase Fotocopia Barco

Roles jugados por personas o instituciones

Doctor Empleado Supervisor
Paciente Gerente Jefe Departamento
Enfermero Cliente Ingeniero de Sistemas
Director Socio Analista

Incidentes/Eventos

Vuelo Accidente Rendimiento
Suceso Rotura de un sistema Llamada telefónica
Salida de un tren Despegue de un avión Llegada de un avión

Interacciones

Compra Arco (entre dos nodos)
Cargo en tarjeta de crédito Reunión
Intersección Contrato

Especificaciones

Producto de seguros Artículo de un libro
Tipo de tarjeta de crédito Tipo de crédito

Cada clase de un sistema debe ser responsable de un aspecto del mismo. Las propiedades localizadas en una misma área de responsabilidad se deben agrupar en una única clase y no dividirse en diversas clases. Cada responsabilidad se asigna a una única clase. El principio de coherencia exige que todas las propiedades de una clase deben formar una conexión lógica. Si por ejemplo se desea crear una clase Cliente se debe determinar primero su responsabilidad: ¿sobre qué elementos es responsable esta clase?. Por ejemplo:

9.4.2 Representación gráfica de una clase

Una clase se representa en UML (Unified Modeling Language) con un rectángulo que se divide horizontalmente en tres bandas. La banda superior contiene el nombre de la clase; la banda central los atributos de la clase; y la banda inferior contiene las operaciones/métodos de la clase. Los atributos con un signo más (+) delante de ellos son públicos, que significa que otras clases pueden acceder a ellos. Los atributos precedidos con un signo menos (-) son privados, lo que indica que sólo la clase y sus objetos pueden acceder a ellos. Por último, los atributos protegidos rotulados con el número de signo numeral (#) pueden ser utilizados únicamente por la clase y por cualquier descendiente de la clase (en herencia hablaremos de clases descendientes).

9.4.2.1 Atributos

Un atributo es una propiedad de una clase. Describe el rango de valores que la propiedad puede tener en los objetos de esa clase. Una clase puede tener cero o más atributos. Los atributos pueden mostrar su tipo así como su valor por defecto.

9.4.2.2 Operaciones/Métodos

Una operación es algo que una clase puede hacer. Se nombra de igual forma que los atributos.

9.4.2.3 Ejemplos

  • Clase Motocicleta (atributos y operaciones/métodos).

  • Clase Factura (atributos).

  • Clase Cuenta Corriente (atributos y operaciones/métodos).

9.4.3 Declaración de clases

En pseudocódigo, se podría hacer la declaración de clases de la siguiente manera:

clase nombre_de_clase

  //Declaración de atributos

  //Declaración de operaciones (métodos y constructores)

fin_clase

El nombre/etiqueta nombre_de_clase tiene que ser un identificador válido.

9.4.3.1 Los miembros de una clase y de un objeto

Los miembros de una clase son los atributos (o variables de instancia) y los métodos. Los métodos son acciones que se realizan por un objeto de una clase. Una invocación a un método es una petición que le llega al método para que ejecute su acción dentro del objeto al que pertenece. La invocación de un método se denominaría también llamar a un método o pasar un mensaje a un objeto.

Existen dos tipos de métodos, aquellos que devuelven un valor único y aquellos que ejecutan alguna acción distinta de devolver un único valor. La diferencia entre unos y otros en pseudocódigo se destacará mediante el empleo de las palabras reservadas procedimiento y funcion. El paso de parámetros se regirá por las normas habituales descritas al tratar procedimientos y funciones. Por ejemplo, en una clase CuentaCorriente, el método depositar, que no devuelve ningún valor, se declararía:

público procedimiento depositar(real cantidad)
  …
inicio
  …
fin_procedimiento

El método obtenerSaldo, también de la clase CuentaCorriente, se supone devuelve el saldo y su declaración sería:

público real función obtenerSaldo()
  …
inicio
  …
  devolver(…)
fin_función

9.4.3.2 Método constructor

Un constructor es un método que tiene el mismo nombre que la clase, cuyo propósito es inicializar los miembros datos (atributos) de un nuevo objeto y que se ejecuta automáticamente cuando se crea un objeto de una clase. Sintácticamente es similar a un método. Dependiendo del número y tipos de los argumentos proporcionados, una función o método constructor se llama automáticamente cada vez que se crea un objeto. Si no se ha definido ningún constructor en la clase, el compilador proporciona un constructor por defecto. Cuando se define un constructor no se puede especificar un valor de retorno, un constructor nunca devuelve un valor. Un constructor puede, sin embargo, tomar cualquier número de parámetros (cero o más). A su rol como “inicializador”, un constructor puede tener otras tareas, que se ejecutarán cada vez que se cree un objeto de la clase. En pseudocódigo, la definición de un método constructor se podría hacer de la siguiente manera:

constructor nombre_de_clase[(lista_parámetros_formales)]
//declaración de variables locales
inicio
  //código del constructor
fin_constructor

Por ahora asumiremos que cuando un objeto ya no se necesita y se queda sin referencias, la memoria ocupada por ese objeto se libera automáticamente, sin necesidad de realizar una destrucción explícita del objeto (no se requerirá un método destructor).

9.4.3.3 Ejemplo

Declaración de una clase auto asociada al objeto/entidad/concepto: automóvil.

clase auto

  var
    privado entero: FechaDeFabricación
    privado real: Filometraje
    privado cadena: LicenciaDeRodamiento

  público constructor auto()
  inicio
    …
  fin_constructor

  público entero función obtenerFechaFabricación()
    …
  inicio
    …
    devolver(…)
  fin_función

  público procedimiento obtenerKilometraje()
    …
  inicio
    …
  fin_procedimiento

    …

fin_clase

9.5 Objetos

9.5.1 Representación gráfica de un objeto

La notación UML de un objeto es un rectángulo con dos compartimentos. El compartimento superior contiene el nombre del objeto y el nombre de la clase a la cual pertenece ese objeto. La sintaxis sería:

nombreobjeto: nombreclase

El compartimiento inferior contiene la lista de nombres de atributos y sus valores. Los tipos de atributos se pueden mostrar utilizando la sintaxis:

nombreatributo: tipo = valor

Por ejemplo, el objeto C1 de la clase Curso con dos atributos podría representarse así:

9.5.2 Instanciación de objetos

La siguiente sentencia define dos objetos, obj1 y obj2, de la clase NombreClase.

NombreClase : obj1, obj2

La definición de un objeto es similar o equivalente a la definición de una variable. Se reserva espacio en memoria para el objeto con todos sus atributos. La creación física del objeto se denomina instanciación. La instanciación se efectúa mediante la palabra nuevo, empleada por ejemplo en una sentencia de asignación de la siguiente forma,

//llamada al constructor por defecto (que en este caso no tiene parámetros)
obj1 <- nuevo NombreClase()

Para acceder a los elementos de un objeto (atributos y métodos) se emplea el operador punto: ..

nombre_objeto.nombre_atributo
nombre_objeto.nombre_método(valores_para_los_parámetros_del_metodo)

La llamada o invocación a un método se puede realizar de dos formas, dependiendo de que el método devuelva o no un valor.

  1. Si el método devuelve un valor, la llamada al método se trata normalmente como un valor.
  2. Si el método realiza una acción distinta a devolver un valor, una llamada al método debe ser una sentencia/instrucción.

Así dada la declaración: CuentaCorriente: miCuenta, las llamada a los métodos depositar y obtenerSaldo se podrían efectuar de la siguiente forma:

miCuenta.depositar(200000)
saldo <- miCuenta.obtenerSaldo()

9.6 Encapsulamiento y visibilidad (ocultamiento)

Una característica de los objetos es que mantienen unidos o encapsulados sus miembros, es decir sus características (atributos) y su comportamiento (métodos). Además, un principio básico en programación orientada a objetos es el ocultamiento de la información. El ocultamiento de la información significa que a uno o más miembros de un objeto, no se pueden acceder por los métodos de otros objetos (y en general, no se pueden acceder por funciones externas a la clase). Para controlar el acceso a los miembros de una clase se utilizan tres diferentes especificadores de acceso: público, privado y protegido.

El mecanismo principal para ocultar datos es ponerlos en una clase y hacerlos privados. A los atributos o métodos privados sólo se puede acceder desde dentro de la clase. Por el contrario los atributos o métodos públicos son accesibles desde el exterior de la clase. Los miembros protegidos son accesibles por métodos de la misma clase y de las clases derivadas, así como por clases amigas. Normalmente una clase debe tener todos o casi todos sus atributos privados y sus métodos públicos. Las reglas de visibilidad complementan el concepto de encapsulamiento de forma que las estructuras de datos internas utilizadas en la implementación de una clase no pueden ser accesadas directamente. Cuando una clase tiene su estructura interna oculta (es decir, privada) se facilita el mantenimiento del software, ya que se podrá mejorar la estructura de la clase sin tener que modificar los programas que la utilizan. Internamente se cambia lo que se desea pero manteniendo la misma interfaz con el exterior, de esa manera todo se mantiene igual para todo lo externo que interactue con los objetos de esa clase.

9.7 Jerarquía de clases

Generalización (o especialización) es una relación taxonómica entre un elemento general y uno especial (o viceversa), donde el elemento especial añade propiedades al general y se comporta de un modo compatible con él.

En la generalización o especialización, las propiedades se estructuran jerárquicamente. Propiedades de significado general se asignan a las clases más generales (superclases) y las propiedades más especiales se asignan a clases que están subordinadas a las clases generales (subclases). Por consiguiente, las propiedades de las superclases son conferidas/otorgadas (bestowed) a las subclases. Una subclase contiene tanto sus propias propiedades como las de sus superclases. Al heredar todas las propiedades de sus superclases, las subclases pueden modificarlas y ampliarlas, pero no pueden eliminarlas ni suprimirlas.

La diferencia entre las superclases y subclases se realiza, normalmente, mediante al menos una característica específica, denominada discriminador.

En UML se define a la generalización como herencia. De hecho, generalización es el concepto y herencia se considera la implementación del concepto en un lenguaje de programación.

9.7.1 Herencia

Es la capacidad para crear nuevas clases (descendientes) que se construyen sobre otras que ya existen. Una clase derivada hereda el código (métodos) y los datos (atributos) de las clases base, añadiendo su propio código especial y sus datos adicionales, incluso, puede modificar aquellos elementos de las clases base que necesita sean diferentes. La herencia puede ser simple o múltiple. En herencia simple, una clase derivada hereda exactamente de una sola clase base (tiene sólo un padre). La herencia múltiple implica múltiples clases bases, es decir, una sola clase derivada tiene varios padres (lo cual tiene como desventaja que puede generar ambigüedades). Para definir clases descendientes se debe especificar en la cabecera de la clase, tras las palabras hereda_de, la o las clases ascendientes o antepasado, también denominadas clases base o superclases.

clase nombre_clase hereda_de [especificador_acceso] clase_base
  // lista_de_miembros
fin_clase

  • El especificador_acceso que declara el tipo de herencia es opcional (publico, privado o protegido). La accesibilidad de los miembros heredados en la clase derivada vendrá dada por la combinación de los modificadores de los miembros de la clase base con el tipo de herencia. Así, un especificador de acceso publico, significa que los miembros públicos de la clase base son miembros públicos de la clase derivada. Con herencia privada los miembros públicos y protegidos de la clase base se vuelven miembros privados de la clase derivada.
  • La clase base (clase_base) es el nombre de la clase de la que se deriva la nueva clase (nombre_clase).
  • La lista_de_miembros consta de atributos y métodos que se adicionan a (o en donde se modifican) los miembros heredados.

La propiedad de la herencia hace las tareas de programación mucho más fáciles y flexibles, no siendo necesario describir cada una de las características explícitamente para cada clase, ya que las clases pueden heredar características de otras clases.

Como ya se comentó en una sección anterior, encapsular es una característica muy potente, y junto con el ocultamiento de la información, representan propiedades principales de los objetos. La orientación a objetos se caracteriza, además de por las propiedades anteriores, por incorporar la característica de herencia, propiedad que permite a los objetos ser construidos a partir de otros objetos. Dicho de otro modo, la capacidad de un objeto para utilizar las estructuras de datos y los métodos previstos en antepasados o ascendientes. Esto permite reutilizar código anteriormente ya desarrollado.

La herencia se apoya en el significado de ese concepto en la vida diaria. Así, las clases básicas o fundamentales se dividen en subclases. Los animales se dividen en mamíferos, anfibios, insectos, pájaros, peces, etc. La clase vehículo se divide en subclase automóvil, motocicleta, camión, autobús, etc. Es así como todos los vehículos citados tienen un motor y ruedas, que son características comunes; si bien los camiones tienen una caja para transportar mercancías, mientras que las motocicletas tienen un manillar en lugar de un volante.

La herencia supone una clase base y una jerarquía de clases. Las clases derivadas heredan atributos y métodos de su clase base, añadiendo sus propios atributos y métodos, e incluso cambiando aquellos elementos de la clase base que necesita sean diferentes. Las clases derivadas se crean en un proceso de definición de nuevos tipos y reutilización del código anteriormente desarrollado en la definición de sus clases base. Las clases que heredan propiedades de una clase base pueden a su vez servir como definiciones base de otras clases. Las jerarquías de clases se organizan en forma de árbol. La herencia es un mecanismo potente para tratar con la evolución natural de un sistema con modificación incremental.

La relación de herencia se representa mediante una flecha larga que apunta de la subclase a la superclase. Las flechas pueden ser dibujadas de dos formas: directamente de las subclases a las superclases o bien combinadas en una línea común. Como ya se ha comentado, la herencia es la manifestación más clara de la relación de generalización/especialización y a la vez una de las propiedades más importantes de la orientación a objetos. Todos los lenguajes de programación orientados a objetos soportan en su propio lenguaje construcciones que implementan de modo directo la relación jerárquica entre clases. La herencia es la relación que existe entre dos clases, en la que una clase denominada derivada se crea a partir de otra ya existente, denominada clase base. Este concepto nace de la necesidad de construir una nueva clase a partir de una existente. Así, por ejemplo, si existe una clase figura y se desea crear una clase triángulo, esta clase triángulo puede derivarse de figura ya que tendrá en común con ella un estado y un comportamiento, aunque triángulo luego tendrá sus características propias. triánguloes-un” tipo de Figura. Otro ejemplo, puede ser vendedor que “es-un” tipo de empleado.

A veces es difícil decidir cuál es la relación de herencia más óptima entre clases dentro del diseño de un programa. Una vista de los empleados basada en el modo de pago puede dividir a los empleados con salario mensual fijo; empleados con pago por horas de trabajo y empleados a comisión por las ventas realizadas. Una vista de los empleados basada en el estado de dedicación a la empresa: dedicación plena o dedicación parcial. Una vista de empleados basada en el estado laboral del empleado con la empresa: fija o temporal. Una dificultad a la que suele enfrentar el diseñador es que en los casos anteriores un mismo empleado puede pertenecer a diferentes grupos de trabajadores. Una pregunta usual es ¿cuál es la relación de herencia que describe la mayor cantidad de variación en los atributos de las clases y operaciones? ¿esta relación ha de ser el fundamento del diseño de clases? Evidentemente la respuesta adecuada sólo se podrá dar cuando se tenga presente la aplicación real a desarrollar.

Por otra parte es importante tener presente que, la clase base y la clase derivada tienen código (métodos) y datos (atributos) comunes, de modo que si se decidiera crear la clase derivada totalmente separada, por aparte o independiente de la clase base, se duplicaría mucho de lo que ya se ha escrito para la clase base.

9.7.1.1 Tipos de herencia

Existen dos mecanismos de herencia utilizados comúnmente en programación orientada a objetos: herencia simple y herencia múltiple. Herencia simple es aquel tipo de herencia en la cual un objeto (clase) puede tener sólo un ascendiente, o dicho de otro modo, una subclase puede heredar datos y métodos de una única clase. Herencia múltiple es aquel tipo de herencia en la cual una clase puede tener más de un ascendiente inmediato, o lo que es igual, adquirir datos y métodos de más de una clase. No todos los lenguajes de programación soportan herencia múltiple.

A primera vista, se puede suponer que la herencia múltiple es mejor que la herencia simple; sin embargo, como ahora comentaremos, no siempre será así. En general, prácticamente todo lo que se puede hacer con herencia múltiple se puede hacer con herencia simple, aunque a veces resulta más difícil (toca esforzarse un poquito más, lo que no debería tener nada de malo). Una dificultad surge con la herencia múltiple cuando se combinan diferentes tipos de objetos, cada uno de los cuales define métodos o campos con el mismo nombre. Supongamos dos tipos de objetos pertenecientes a las clases Gráficos y Sonidos, y se crea un nuevo objeto denominado Multimedia a partir de ellos. Gráficos tiene tres campos datos: tamaño, color y mapasDeBits, y los métodos dibujar, cargar, almacenar y escala; Sonidos tiene tres campos dato, duración, voz y tono, y los métodos reproducir, cargar, escala y almacenar. Así, para un objeto Multimedia, el método escala significa poner el sonido en diferentes tonalidades, o bien aumentar/reducir el tamaño de la escala del gráfico. Naturalmente, el problema que se produce es la ambigüedad, y se tendrá que resolver con una operación de prioridad que el correspondiente lenguaje deberá soportar y entender en cada caso (tal vez, una de las razones por las cuales no todos los lenguajes soportan la herencia múltiple. Además, la herencia múltiple es un tema controvertido que tiene simpatizantes y detractores).

9.7.1.2 Clases abstractas

Con frecuencia, cuando se diseña un modelo orientado a objetos es útil introducir clases a cierto nivel que pueden no existir en la realidad (tangible e intangible) pero que pueden ser construcciones útiles. Estas clases se conocen como clases abstractas. Las clases abstractas son clases en las que algunos o todos los miembros no tienen implementación. Los métodos sin implementación se pueden declarar especificando exclusivamente la cabecera y no pueden ser privados.

Una clase abstracta normalmente ocupa una posición adecuada en la jerarquía de clases que le permite actuar como un depósito de métodos y atributos compartidos para las subclases de nivel inmediatamente inferior. Se espera que las clases abstractas no tengan instancias directamente. Se utilizan para agrupar otras clases y capturar información que es común al grupo. Las subclases de clases abstractas, es decir las clases derivadas de una clase abstracta, se encargarán de implementar los métodos heredados y naturalmente sí tendrán instancias.

Por ejemplo, una clase abstraca Vehículo debe tener atributos (color, año de fabricación, etc.) y métodos abstractos que especifiquen datos y comportamientos comunes de todos los vehículos (arrancar, frenar, etc.). Auto, Moto y Barco representan clases que requieren implementar, por ejemplo, el método heredado frenar, entre otros.

9.7.1.3 Ejemplo

Clases asociadas a los objetos/entidades/conceptos: jugadores de futbol.

9.8 Polimorfismo

Otra propiedad importante de la programación orientada a objetos es el polimorfismo. Polimorfismo puro se produce cuando un único método se puede aplicar a una variedad de tipos o clases de objetos. En polimorfismo puro hay un método (el cuerpo del código) y un número de interpretaciones (significados diferentes). El polimorfismo supone que un mismo mensaje puede producir acciones (resultados) totalmente diferentes cuando se recibe por objetos diferentes.

El polimorfismo, en su expresión más simple, es el uso de un nombre o un símbolo (por ejemplo, un operador) para representar o significar más de una acción dependiendo de los objetos involucrados. Esta propiedad, en su concepción básica, se encuentra en casi todos los lenguajes de programación, de tal forma que es posible que el símbolo + sirva tanto para realizar sumas aritméticas como para concatenar (unir) cadenas. Cuando a un operador existente en el lenguaje, tal como +, = o *, se le asigna la posibilidad de operar sobre un nuevo tipo de dato, se dice que está sobrecargado. La sobrecarga, que puede ser de operadores y de métodos, es una clase de polimorfismo.

9.9 Ejercicios

9.9.1 Parte A

Definir y diseñar las clases, junto con sus principales atributos/características y acciones/funcionalidades, que permitan representar los siguientes objetos/entidades/conceptos:

  1. Número complejo.

  2. Vector en \mathbb{R}^3.

  3. Círculo, y con las clases derivadas para los conceptos: esfera y cilindro.

  4. Polinomio de grado 3 o inferior.

  5. Generador de valores seudoaleatorios “igualmente probables” usando el linear congruential generator.

9.9.2 Parte B

Definir y diseñar las clases, junto con sus principales atributos/características y acciones/funcionalidades, que permitan representar las variables aleatorias (que son objetos/entidades/conceptos intangibles):

  1. Con distribución Bernoulli.

  2. Con distribución binomial.

  3. Con distribución Poisson.

  4. Con distribución uniforme continua.

  5. Con distribución logística.

  6. Con distribución exponencial (parametrizada con parámetro de escala, en vez de parámetro de tasa).

  7. Con distribución Erlang (parametrizada con parámetro de escala, en vez de parámetro de tasa).

A continuación encontrarán algunas de las distribuciones más conocidas y utilizadas, junto con las relaciones que hay entre ellas http://www.math.wm.edu/~leemis/chart/UDR/UDR.html:

También para una consulta (rápida pero precavida) pueden ir a: https://en.wikipedia.org/wiki/List_of_probability_distributions.