PDF de programación - Modularidad y orientación a objetos. Traducción a C++ de diseños modulares

Imágen de pdf Modularidad y orientación a objetos. Traducción a C++ de diseños modulares

Modularidad y orientación a objetos. Traducción a C++ de diseños modularesgráfica de visualizaciones

Publicado el 15 de Abril del 2021
308 visualizaciones desde el 15 de Abril del 2021
33,7 KB
12 paginas
Creado hace 16a (26/02/2008)
1 Modularidad y orientación a objetos. Traducción a C++ de

diseños modulares

En esta asignatura sólo aplicaremos la orientación a objetos a fin de aprovechar mejor los meca-
nismos que el lenguaje C++ ofrece para codificar nuestros diseños modulares. Sin embargo, este
estilo de programación conlleva un modo particular de abordar la estructuración de programas.
Veamos en primer lugar algunos aspectos conceptuales del mismo para pasar, seguidamente, a
aplicarlos en nuestro contexto.

1.1 Clases y objetos

Considerado como unidad de estructuración de programas, el concepto de clase equivale esen-
cialmente al de módulo. Por tanto, sus fases de diseño son las mismas, si bien la forma de
especificar, utilizar e implementar difiere ligeramente entre un estilo y el otro. Las clases y los
objetos representan una manera alternativa de modelizar los datos (la información) de un progra-
ma.

En el estilo modular clásico, un módulo de datos define un tipo con un dominio de valores y
un conjunto de operaciones que trabajan con diversos parámetros, de los que al menos uno es del
propio tipo. En programación orientada a objetos, una clase de datos define una estructura de
atributos y operaciones que se particulariza (también se suele decir que se “instancia”) en cada
objeto de la misma que se crea. En este aspecto, una clase también hace el papel de “tipo” de
dichos objetos.

En el diseño modular clásico, una variable posee un valor del tipo al que pertenece y sobre
ella se aplican las operaciones de dicho tipo. Podemos decir que una variable contiene la infor-
mación. Sin embargo, cada vez que se crea un objeto de una clase de datos, éste se convierte
en un representante de la estructura definida en tal clase, de forma que recibe como componen-
tes sus atributos y sus operaciones (también llamadas métodos). En este caso el objeto es la
información.

La mayoría de lenguajes orientados a objetos introducen maneras de trabajar que pueden re-
sultar sorprendentes si previamente se ha estudiado diseño modular, pero que son perfectamente
consistentes con la mencionada diferencia conceptual entre ambos estilos.

Por ejemplo, las operaciones de una clase se conciben para ser aplicadas sobre un objeto
concreto de la clase, y por eso se parametrizan de una manera especial, de tal manera que el
objeto que es creado, modificado o consultado por una operación no aparece explícitamente
en la lista de paramétros de la misma. Se mencionará sólo en las llamadas a dicha operación,
mientras que en el código de la misma es referido de forma implícita.

Otra práctica habitual es que las clases no se tratan igual que los tipos simples respecto a la
operación de asignación. Cuando se desea producir dos objetos distintos con la misma infor-
mación, no se habla de asignar un objeto a otro sino de copiar (o también, clonar) un objeto en
otro. De hecho, en la mayoría de lenguajes orientados a objetos se permite realizar asignaciones
entre objetos, pero con una semántica muy distinta a la de la asignación clásica. Al realizarse
tal asignación entre objetos, el resultado es que la misma información pasa a tener dos nom-

1

bres (situación habitualmente denominada “aliasing”), lo que puede dar lugar a efectos laterales
difícilmente controlables. Por ello, en nuestro contexto limitaremos estas asignaciones a un redu-
cido número de casos particulares que describiremos en su momento, aunque en otros contextos
puede resultar útil explotar esta compartición de información.

En la práctica, esta filosofía de trabajo da lugar a una serie de aspectos metodológicos nuevos

que presentamos en los apartados siguientes, aplicados de forma particular al lenguaje C++.

2 Especificación y uso de clases

Una vez que se ha especificado un cierto módulo, lo traducimos a C++ generando un fichero
de cabeceras, también llamado fichero de especificación o fichero de definición (con extensión
.hpp). En tal fichero definimos el nombre de la clase e incluimos toda la información que el
usuario de la clase (y su compilador) necesita para escribir programas que usen dicha clase. El
resto de los componentes de la clase, en especial el código de las operaciones, se escribirá en un
fichero de implementación (con extensión .cpp).

Un fichero de especificación se divide en dos partes. La parte pública es la que el usuario
puede utilizar y contiene las cabeceras de las operaciones de la clase. La parte privada está
formada por los campos de la clase, que el usuario no puede consultar ni modificar directamente,
sino sólo por medio de las operaciones.

Lo ideal sería que la parte privada no tuviera que ser mostrada, pero el compilador necesita
saber los tipos de los campos de la clase para realizar reservas de espacio y otras gestiones de
bajo nivel. Existen maneras de ocultar dicha información y veremos un ejemplo, pero no os
pediremos que programéis así vuestras clases.

Por ejemplo, si queremos ocultar los campos de la clase Estudiante, el fichero Estudian-

te.hpp podría tener el siguiente aspecto:

#ifndef ESTUDIANTE_HPP // para evitar la doble declaración
#define ESTUDIANTE_HPP // si se realizan varios includes

struct estOculto; // estructura que oculta los campos de la clase

// su definición se realiza en el fichero Estudiante.cpp

class Estudiante {

private:

// los campos originales se ocultan en una única estructura;

estOculto * esto;

// Campo común a todos los objetos de la clase Estudiante. Representa
// la nota máxima que se le puede poner a un estudiante. Podemos
// ocultarlo, pero entonces necesitaremos una operacion consultora

2

static const int MAX_NOTA = 10;

public:

/* Constructoras */
Estudiante();

/* Consultoras */

bool tiene_nota() const;
double consultar_nota() const;
int consultar_DNI() const;

/* Modificadoras de los campos */

void crear_estudiante(int dni);
void anadir_nota(double nota);
void modificar_nota(double nota);

/* Copiadora de la clase */

void copiar_estudiante(const Estudiante &est);

/* Entrada / Salida */

void leer_estudiante();
void escribir_estudiante() const;

};

#endif

2.1 Parametrización de los métodos

Como hemos mencionado, cada objeto concreto de una clase “posee” sus propias operaciones.
En esta línea, C++ permite una simplificación sintáctica al declarar las cabeceras de los métodos,
consistente en que el objeto sobre el que se aplica un método no aparece como parámetro en
su correspondiente cabecera. Se considera que el objeto aparece de forma implícita y como tal
lo mencionaremos en la especificación del método. Notad que ello obligará también a algunos
cambios en la implementación de las operaciones como veremos en su momento.

Ejemplo: consideremos la función tiene_nota del módulo Estudiante, cuya cabecera era

funcion tiene_nota (e: Estudiante) dev b: booleano
/* cierto */
/* b = e tiene nota */

Su traducción a C++ ha sido

bool tiene_nota () const
/* cierto */
/* retorna si el parámetro implícito tiene nota */

3

Al realizar una llamada a un método, el objeto sobre el que se aplica precede al nombre del

método y al resto de los parámetros, separado por un punto.

<nombre_del_objeto>.<nombre_del_método>(<otros parámetros>)

Ejemplo: en la función anterior, el estudiante sobre el cual se realiza la consulta será un
parámetro implícito: no aparece en la cabecera y sólo se menciona al usar la operación. Si est
es un objeto ya creado de la clase Estudiante y b es una variable booleana, la instrucción

b:=tiene_nota(est);

se traduce a C++ como

b=est.tiene_nota();

Pero no acaban ahí las diferencias: el objeto al que se aplica el método funciona como un
parámetro de entrada/salida, ya que su valor inicial puede ser relevante, pero si es modificado
por el método tal modificación es permanente. Por ejemplo, al implementar en C++ la acción
modificar_nota, cuya cabecera era

accion modificar_nota(e/s est: Estudiante; ent nota: real)
/* est tiene nota y "nota" ha de estar entre 0 y 10 */
/* est conserva su DNI y su nota
pasa a ser "nota" */

podemos aprovechar la modificabilidad del parámetro implícito si la traducimos así:

void modificar_nota (double nota)
/* "nota" ha de estar entre 0 y 10; si no, se produce una excepción,

el parámetro implícito debe tener nota */

/* el parámetro implícito conserva su DNI y su nota pasa a ser "nota" */

De este modo, si est es un Estudiante con nota y x es una nota válida, para que la nota de

est pase a ser x en la notación algorítmica haríamos

modificar_nota(est,x);

y en C++ haremos

est.modificar_nota(x);

Por todo ello, hemos de tener muy claro si deseamos conservar el valor original de un objeto
después de una operación de modificación, en cuyo caso habría que obtener previamente una co-
pia del mismo. Como esta situación puede darse con cierta frecuencia, toda clase ha de disponer
de una operación de copia (o clonación) de objetos, que puede ser una acción o una función.

Sin embargo, en ocasiones sabemos que una operación jamás modificará el valor de su pa-
rámetro implícito. En ese caso es conveniente calificar la operación mediante la palabra const,
como hemos hecho con la función tiene_nota y las demás consultoras.

4

2.2 Clasificación de las operaciones

Las operaciones de una clase de datos deben clasificarse durante su especificación en tres cate-
gorías:

Creadoras de objetos. Son funciones que sirven para crear objetos nuevos de la clase,
ya sean objetos inicializados con un mínimo de información o el resultado de cálculos
más complejos. Las llamadas a estas funciones es uno de los casos particulares donde
permitimos la asignación entre objetos.
Supongamos que hemos especificado la operación de copia de estudiantes como función.
Como tal puede traducirse a C++:

Estudiante copiar_estudiante()
/* cierto */
/* el resultado es una copia del parámetro implícito */

y la podemos invocar así para hacer que est2 sea una copia de est1, que se supone ya
creado

Estudiante est2=est1.copiar_estudiante();

En C++ se puede definir un tipo particular de operaciones creadoras, denominadas también
constructor
  • Links de descarga
http://lwp-l.com/pdf19101

Comentarios de: Modularidad y orientación a objetos. Traducción a C++ de diseños modulares (0)


No hay comentarios
 

Comentar...

Nombre
Correo (no se visualiza en la web)
Valoración
Comentarios...
CerrarCerrar
CerrarCerrar
Cerrar

Tienes que ser un usuario registrado para poder insertar imágenes, archivos y/o videos.

Puedes registrarte o validarte desde aquí.

Codigo
Negrita
Subrayado
Tachado
Cursiva
Insertar enlace
Imagen externa
Emoticon
Tabular
Centrar
Titulo
Linea
Disminuir
Aumentar
Vista preliminar
sonreir
dientes
lengua
guiño
enfadado
confundido
llorar
avergonzado
sorprendido
triste
sol
estrella
jarra
camara
taza de cafe
email
beso
bombilla
amor
mal
bien
Es necesario revisar y aceptar las políticas de privacidad