15.7.- Los Streams Su traduccion mas cercana es flujos o corrientes. Es la forma como turbo vision trata los datos en objetos. Es posible separar los datos de los metodos y luego guardar la informacion como algo separado, pero esto seria un paso para atras puesto que hemos trabajado para unir los datos y los procedimientos, mejor seria guardar los datos como integrados a los objetos pero esto no esta soportado en forma directa. Nuestro objetivo se logra automaticamente con el uso de la unidad objects, cierto que se deben cumplir ciertos requisitos, pero esto no es nada comparado con el trabajo que nos salvara despues.Vamos a ver el mismo programa anterior pero simplificado, para aislar lo mas posible a las streams. PROGRAM ARCHIVOS; USES OBJECTS,DOS,CRT; TYPE PMATRIZ = ^TMATRIZ; TMATRIZ = OBJECT (TOBJECT) NOMBRE : STRING; LONGITUD : STRING; HORA : STRING; CONSTRUCTOR INIT (NOM,LON,HOR:STRING); PROCEDURE IMPRIME; VIRTUAL; CONSTRUCTOR CARGAR (VAR S : TSTREAM); PROCEDURE SALVAR (VAR S : TSTREAM); VIRTUAL; END; CONSTRUCTOR TMATRIZ.CARGAR (VAR S:TSTREAM); BEGIN S.READ (NOMBRE,SIZEOF (NOMBRE)); { SE UTILIZA SIZEOF } S.READ (LONGITUD,SIZEOF (LONGITUD)); { PARA NO TENER NINGUN } S.READ (HORA,SIZEOF (HORA)); { PROBLEMA AL CAMBIAR } END; { EL TIPO DE DATO. } PROCEDURE TMATRIZ.SALVAR (VAR S:TSTREAM); BEGIN S.WRITE (NOMBRE,SIZEOF (NOMBRE)); S.WRITE (LONGITUD,SIZEOF (LONGITUD)); S.WRITE (HORA,SIZEOF (HORA)); END; CONSTRUCTOR TMATRIZ.INIT (NOM,LON,HOR:STRING); BEGIN NOMBRE := NOM; LONGITUD := LON; HORA := HOR; END; PROCEDURE TMATRIZ.IMPRIME; VAR A : INTEGER; FECHA : DATETIME; CONV : LONGINT; BEGIN WRITE (NOMBRE); GOTOXY (20,WHEREY); WRITE (HORA); GOTOXY (32,WHEREY); VAL (LONGITUD,CONV,A); UNPACKTIME (CONV,FECHA); WRITE (FECHA.YEAR,'/',FECHA.MONTH,'/',FECHA.DAY, ' ',FECHA.HOUR,':',FECHA.MIN); WRITELN; END; PROCEDURE IMPRESION (COLECTA: PCOLLECTION); PROCEDURE IMPRIMIR (PUNTERO:PMATRIZ);FAR; BEGIN PUNTERO^.IMPRIME; END; BEGIN WRITELN; WRITELN('LISTA DE FILES EN EL SUBDIRECTORIO:'); WRITELN; WRITELN ('NOMBRE LONGITUD FECHA HORA'); WRITELN; COLECTA^.FOREACH (@IMPRIMIR); END; CONST RMATRIZ : TSTREAMREC = ( OBJTYPE: 100; VMTLINK: OFS(TYPEOF(TMATRIZ)^); LOAD: @TMATRIZ.CARGAR; STORE: @TMATRIZ.SALVAR); VAR LISTA_DE_ARCHIVOS : PCOLLECTION; ARCHIVO : TBUFSTREAM; INFORMACION : SEARCHREC; CAD1,CAD2 : STRING; CH : CHAR; DIRECTORIO : STRING; PROCEDURE REGISTRAR; BEGIN REGISTERTYPE (RCOLLECTION); REGISTERTYPE (RMATRIZ); END; BEGIN CLRSCR; REGISTRAR; WRITE ('VER ANTIGUO ARCHIVO (S/N) : '); CH := UPCASE (READKEY);WRITELN; IF CH = 'N' THEN BEGIN WRITE ('INGRESE DIRECTORIO A GRABAR : '); READLN (DIRECTORIO); LISTA_DE_ARCHIVOS := NEW (PCOLLECTION,INIT(50,20)); WITH LISTA_DE_ARCHIVOS^ DO BEGIN FINDFIRST (DIRECTORIO,ARCHIVE,INFORMACION); STR (INFORMACION.TIME,CAD1); STR (INFORMACION.SIZE,CAD2); INSERT (NEW (PMATRIZ,INIT (INFORMACION.NAME,CAD1,CAD2))); WHILE DOSERROR = 0 DO BEGIN FINDNEXT(INFORMACION); STR (INFORMACION.TIME,CAD1); STR (INFORMACION.SIZE,CAD2); INSERT(NEW(PMATRIZ,INIT(INFORMACION.NAME,CAD1,CAD2))); END; END; ARCHIVO.INIT ('A:\SUBDIR.DAT',STCREATE,1024); ARCHIVO.PUT (LISTA_DE_ARCHIVOS); END ELSE BEGIN ARCHIVO.INIT ('A:\SUBDIR.DAT',STOPEN,1024); LISTA_DE_ARCHIVOS := PCOLLECTION(ARCHIVO.GET); END; IMPRESION (LISTA_DE_ARCHIVOS); ARCHIVO.DONE; DISPOSE(LISTA_DE_ARCHIVOS,DONE); END. Lo primero que se hace es declarar un constructor para la carga (tmatriz.cargar) y un procedimiento virtual para la grabacion (tmatriz.salvar). Hay que remarcar que estos son necesarios solo cuando hay data adicionada en alguno de los descendientes, si lo hubiera debera colocar el constructor que cargar y el virtual que salva en ellos, llamar al antecesor para grabar datos anteriores y luego grabar los nuevos campos que se hayan adicionado. El proceso descrito es mecanico, siempre se llama al antecesor y luego se salvan los demas datos (o se cargan segun sea el caso), el sizeof se utiliza para que no existan problemas si se decide a cambiar el tipo de dato que manejara su objeto. Luego de todo lo conocido puede encontrar lo siguiente: CONST { REGISTRO EN LA VMT } RMATRIZ : TSTREAMREC = ( OBJTYPE: 100; VMTLINK: OFS(TYPEOF(TMATRIZ)^); LOAD: @TMATRIZ.CARGAR; { EL CONSTRUCTOR QUE CARGA } STORE: @TMATRIZ.SALVAR); { EL VIRTUAL QUE SALVA. } Existe la complicacion en el hecho que las corrientes son polimorficas, debido a que no se sabe cuanta data habra que leer. Afortunadamente cada instancia de los objetos posee un numero de registro que los identifica, esto sera el punto principal en un programa que utilice esta tecnica y se declara de la manera antes vista. Observe que el nombre de registro no es tmatriz sino rmatriz, esto es convencional. En objtype debe colocar un numero mayor o igual a 100 puesto que turbo vision reserva de 0-99 para registrar sus objetos, por esta misma razon no tendra que registrar los objetos de turbo vision cuando quiera salvarlos, es preciso que cada objeto tenga un numero diferente. El resto en el registro es muy mecanico. El siguiente paso es registrar todos los objetos, esto generalmente se hace en un procedimiento como sigue: PROCEDURE REGISTRAR; BEGIN REGISTERTYPE (RCOLLECTION); { NO NECESITA REGITRO EN VMT } REGISTERTYPE (RMATRIZ); END; y se llama desde el programa principal con: REGISTRAR; Recien ahora estamos en condiciones para abrir el fichero pero antes de poder abrir un fichero este tendra que ser declarado como un flujo a algo, este algo puede ser un archivo, una corriente en memoria expandida o en donde sea (es polimorfico!).La inicializacion se lleva a cabo asi: VAR ARCHIVO : TBUFSTREAM; { UN STREAM CON BUFFER } BEGIN ARCHIVO.INIT ('SUBDIR.DAT',STOPEN,1024); y luego podemos grabarlo en una disco: ARCHIVO.PUT (LISTA_DE_ARCHIVOS); o podemos cargarlo con lo siguiente: LISTA_DE_ARCHIVOS := PCOLLECTION(ARCHIVO.GET); Lo cual graba el archivo y lo coloca en una colleccion. Finalmente el fichero debe cerrarse con: ARCHIVO.DONE; Con lo cual nuestro trabajo ha concluido. Habra notado el lector que para programas pequenios turbo vision pueda parecer muy engorroso pero cuando se desarrollan sistemas grandes los objetos y especial turbo vision son una herramienta inestimable para el desarrollo de aplicaciones debido a sus esquemas de ayuda, proteccion de la informacion y la velocidad con las que trata los punteros. Adicionalmente en turbo vision existen los resources, (recursos) que se basan en las colecciones y en los streams que permiten definir algo como residente en el disco y grabado cuando sea necesario. Por ejemplo un menu puede ser grabado cuando sea usado (esta tecnica la usa turbo pascal para el ide, por eso los accesos frecuentes al disco), lo cual permite el cambio de la version del programa de un idioma a otro muy facilmente y proporcionar demos de los programas limitados en su funcionalidad con toda comodidad, una tecnica muy conveniente que es usada intensamente por windows 3.0 de Microsoft. .