TEMA 1.14
TIPOS DE DATOS DEFINIDOS POR EL USUARIO





1.14.1 - DEFINICIÓN DE
NUEVOS TIPOS DE DATOS

Hasta ahora en una variable podíamos almacenar solo un dato (Un número entero o real, una cadena, etc...) o podíamos usar arrays para manejar gran cantidad de datos, pero siempre se trataría de una colección de datos del mismo tipo. En este tema vamos a ver como definir tipos de datos que contengan más de un valor que puede ser de tipos distintos a la vez.

Imaginemos una agenda donde necesitamos manejar nombre dirección, teléfono, y edad de las personas. Vemos que los tres primeros datos son cadenas, aunque el teléfono será siempre más corto que los otros dos, y la edad es un entero. Con lo que hemos visto hasta ahora tendríamos que usar para cada individuo cuatro variables distintas que manejaremos por separado, ya sean variables simples o vectores con subíndices para muchas personas a la vez.

Veamos un programa muy sencillo que pide los datos de dos personas, los almacena en estas nuevas variables que vamos a ver y finalmente presenta en el nombre de la que sea más joven. Que nadie se asuste, después explicaremos lo que es cada cosa.

TYPE TIPOPERSONA
   nombre AS STRING * 30
   direccion AS STRING * 40
   telefono AS STRING * 9
   edad AS INTEGER
END TYPE

DIM persona1 AS TIPOPERSONA
DIM persona2 AS TIPOPERSONA

CLS
INPUT "Nombre de la primera persona: ", persona1.nombre
INPUT "Dirección de la primera persona: ", persona1.direccion
INPUT "Teléfono de la primera persona: ", persona1.telefono
INPUT "Edad de la primera persona: ", persona1.edad
PRINT
INPUT "Nombre de la segunda persona: ", persona2.nombre
INPUT "Dirección de la segunda persona: ", persona2.direccion
INPUT "Teléfono de la segunda persona: ", persona2.telefono
INPUT "Edad de la segunda persona: ", persona2.edad
PRINT
IF persona1.edad < persona2.edad THEN
   PRINT "El más joven es "; persona1.nombre)
ELSE
   PRINT "El más joven es "; persona2.nombre)
END IF

El resultado sería:

Nombre de la primera persona: Carlos Rodríguez
Dirección de la primera persona: Carrera Espinel nº 172 - 6ºA
Teléfono de la primera persona: 952870000
Edad de la primera persona: 23

Nombre de la segunda persona: Fernando Garcia
Dirección de la segunda persona: Plaza del Ahorro nº 21 - 4ºC 
Teléfono de la segunda persona: 952879999
Edad de la segunda persona: 19

El más joven es Fernando García

Si hubiéramos usado variables simples no tendría que haber ningún problema ¿No?, pero como hemos usado un tipo definido por el usuario vamos a verlo todo paso a paso.

Lo primero que hacemos de todo es declarar que vamos a usar un nuevo tipo de datos que se va a llamar TIPOPERSONA y va a estar formado por cuatro miembros que ahora declaramos de forma explícita:

Y ya hemos terminado de declarar TIPOPERSONA

A continuación declaramos dos variables, persona1 y persona2 como de tipo TIPOPERSONA en vez de real o entero o algo así.

Después pedimos al usuario los datos de la primera persona y los vamos guardando cada uno en el lugar correspondiente de la variable persona1 usando el nombre de la variable seguido de un punto y del nombre del miembro al que nos estamos refiriendo en cada caso. El tipo de datos que podemos guardar en cada sitio depende del tipo de dato del miembro, por ejemplo en .edad no podemos guardar una palabra.

Repetimos lo mismo, pero esta vez almacenando la información en persona2.

Finalmente hacemos una comparación entre las dos edades y escribimos el nombre de la persona más joven.

Como se puede ver no es tan difícil. Lo que hemos hecho es agrupar cuatro variables independientes bajo un nombre común, pero de todas formas tendremos que manejar cada una por separado usando el nombre de la variable que hayamos declarado de este tipo seguido del punto y del nombre del miembro.

Es muy importante entender que TIPOPERSONA no es una variable, sino un tipo de datos que hemos definido en nuestro programa y se une a los tipos STRING, INTEGER, LONG, SINGLE y DOUBLE de que ya dispone QBasic y que para poderlo utilizar hay que declarar explícitamente que una variable sea de este tipo usando la instrucción DIM y que ese nombre que le demos a la variable será el que hay que utilizar en el código del programa.

Si usamos el nombre de la variable sin el punto ni el nombre de uno de sus componentes miembro se producirá un error de "Tipos no coinciden" porque QBasic no sabe a cual de los miembros nos estamos refiriendo. Si escribimos mal el nombre del componente se producirá el error "Elemento no definido". Si escribimos mal el nombre de la variable ocurrirá que QBasic se creerá que estamos usando una nueva variable y aparentemente no ocurrirá nada, pero se pueden producir errores de lógica muy difíciles de detectar, problema común en QBasic por no exigir la declaración de variables.

Los componentes miembro pueden ser de cualquiera de los cinco tipos de que dispone QBasic, solo que en el caso de que se declaren como cadenas tendrán que ser de longitud fija. Habrá que poner la palabra STRING seguida de un asterisco (*) y del número de caracteres que va a tener la cadena. Como vimos en el tema de tipos de datos si intentamos asignar una cadena de tamaño superior al máximo no ocurre ningún error, pero todo lo que sobre se pierde.

También podemos declarar algún miembro como de un tipo de datos ya definido por el usuario anteriormente en el mismo programa. Veamos un ejemplo:

TYPE TIPONOMBRE
	nombre AS STRING * 15
	apellido1 AS STRING * 15
	apellido2 AS STRING * 15
END TYPE
TYPE TIPODIRECCION
	calle AS STRING * 30
	numero AS INTEGER
	piso AS INTEGER
	puerta AS STRING * 1
	cp AS STRING * 5
	ciudad AS STRING * 20
	provincia AS STRING * 2
END TYPE
TYPE TIPOPERSONA
	nombre AS TIPONOMBRE
	direccion AS TIPODIRECCION
	telefono AS STRING * 9
	edad AS INTEGER
END TYPE

Si declaramos una variable de TIPOPERSONA haciendo:

DIM persona AS TIPOPERSONA

Para acceder a todos sus componentes escribiríamos cosas como:

persona.nombre.nombre
persona.nombre.apellido1
persona.direccion.calle
persona.direccion.ciudad
persona.edad

De esta forma podemos estructurar muy bien datos que tengan una estructura compleja utilizando una jerarquía de variables que puede resultar muy descriptiva, pero tampoco conviene pasarse. Normalmente no será necesario usar más de dos o tres niveles de datos definidos por el usuario porque si lo hacemos los identificadores de las variables pueden llegar a ser muy largos.

Como se puede ver los miembros de distintos tipos de datos pueden llevar nombres iguales. Como siempre vamos a usar estos nombres junto con el nombre de la variable declarada que sí va a ser distinto en cada caso no habrá ninguna confusión.

También es muy útil poder declarar arrays de un tipo definido por el usuario. Por ejemplo si usamos el TIPOPERSONA del primer ejemplo podemos declarar un vector de 50 personas haciendo:

DIM personas (1 TO 50) AS TIPOPERSONA

Y para acceder a los elementos del vector haríamos cosas como...

personas(3).nombre
personas(17).edad
personas(40).telefono

...igual que si se tratara de vectores de un tipo normal, solo que habrá que especificar cual miembro queremos usar.

Es posible declarar variables y módulos con el mismo nombre que un tipo de datos definido por el usuario, pero para evitar confusiones es recomendable evitar esto a toda costa.

Podemos definir tipos de datos dentro de procedimientos o funciones, pero si lo hacemos sólo estarán disponibles para las variables que declaremos dentro del mismo módulo. Habrá que definirlos después de la línea SUB nombre o FUNCTION nombre. Los tipos definidos en el módulo principal estarán disponibles para todos los módulos del programa, pero si después queremos exportar módulos que los usen a otros programas también habrá que copiar la definición del tipo de dato para que todo funcione correctamente.

Para la construcción de los nombres de los tipos de datos se siguen las mismas normas que para los nombres de variables y módulos. Normalmente se escribe todo en mayúsculas para que resalte más y se empieza siempre por TIPO para darse cuenta fácilmente de que no se trata de una variable o un módulo.

El uso intensivo de tipos de datos definidos por el usuario junto con las constantes ayudan a que el código del programa quede muy detallado y sea más fácil de leer.





1.14.2 - UNA APROXIMACIÓN A LA
PROGRAMACIÓN ORIENTADA A OBJETOS

Antes de nada aclarar que QBasic no es un lenguaje de programación orientado a objetos.

En el apartado anterior hemos visto como ejemplo una estructura a la que llamábamos TIPOPERSONA que tenía unos datos miembros como son nombre, edad, etc... y que nos servía como "molde" para crear una o muchas variables que tienen todas estas características.

Es fácil pensar en los tipos de datos como la definición de una CLASE de objetos que agrupa a una serie de DATOS MIEMBROS que van a tener todas las INSTANCIAS de las variables que se declaren de esa clase.

Esto puede ser la base de la Programación Orientada a objetos. Una nueva metodología de programación que crea lo que se llama "Tipos de datos abstractos" que además de datos miembros incluyen procedimientos miembros (Métodos) y se utiliza de forma que puede reutilizar datos de otras clases ya existentes creando lo que se conoce como "Jerarquía de clases". Estas clases serán usadas en los programas para definir Objetos de esa clase que tendrán todas estas características y podrán ser utilizados por los programas llamando a sus métodos.

Estas técnicas se utilizan ampliamente en lenguajes de programación mucho más avanzados como C++, Object Pascal o Java donde los programadores pueden usar objetos ya existentes o además crear los suyos propios definiendo estas clases. Si ha quedado claro el tema de tipos de datos definidos por el usuario en QBasic y los manejamos con soltura puede ser que nos resulte un poco más sencillo (o menos difícil) aprender a programar objetos en otros lenguajes más modernos de los que se utilizan en la actualidad, aunque de todas formas habrá que aprender toda una multitud de conceptos nuevos y técnicas de programación que no existen en QBasic.













CuRSo De iNTRoDuCCióN a La PRoGRaMaCióN CoN QBaSiC
© 2004 Juan M. González