PDA

Ver la Versión Completa : una ayudita


Delphius
15-01-2005, 03:50:12
¡hola foristas!

Si bien muchos están de vacaciones (yo no soy el caso)... necesito una pequeña manito..
Me hice una unidad ListaTema (con 750 líneas de código) que me facilita enormemente el diseño, mantenimiento y edición de una playlist según una estructura definida. Hasta allí todo anda bien...
Pero el problema es que mi reproductor sería "pobre" si no es capaz de entender la estructura que maneja el Winamp para sus playlists.
Se como es dicha estructura:
#EXTM3U // es la cabecera del archivo .m3u
#EXTINF: Duracion,Artista - Tema // estas dos líneas se repiten por cada tema que tiene
Subdirectorio\NombreArchivo // la playlist

El problema es que esta es la estructura de la versión 2.7 Lite, y me he dado cuenta de que si diseño una unidad ListaWinamp que maneje dicha estructura... hay posibilidades de que para versiones posteriores del Winamp sea obsoleta, obligándome a actualizar mi unidad. Y como quiero evitarme escribir otras tantas líneas por cada vez que se le ocurra a NullSoft Inc. cambiarla, me preguntaba si existe alguna unidad o dll que me permita manejar el formato para distintas versiones del Winamp; y así simplificar mis problemas.

Desde ya muchas gracias.

roman
15-01-2005, 06:21:07
¿Y cómo imaginas que tal unidad o dll (en caso de que exista) va a manejar las distintas versiones? De una u otra forma tendrá que tener código para cada una; así que tu pregunta más bien sería:

¿Existe una unidad o dll que maneje listas de Winamp para no tener yo que programarla?

Es una pregunta válida.

Pero si quieres programarla tú mismo tendrás que manejar la distintas versiones de una u otra forma.

Esto no lo puedes evitar. Pero sí puedes diseñarla de manera que no tengas que alterar el código que ´haga uso de tal unidad.

Para empezar fíjate que la lista como tal la puedes mantener en memoria sin depender del formato en particular. Digamos que cada entrada de la lista la guardas en una clase:


type
TWinampItem = class
public
property Duration: Double;
property Artist: String;
property Theme: String;
end;


Puedes entonces usar un TObjectList (unidad Contnrs) para almacenar objetos de esta clase.

Entonces lo único que debe preocuparte es cómo leer una lista desde un archivo y como guardarla en un archivo:


type
TWinampList = class(TObjectList)
public
procedure LoadFromFile(FileName: String); virtual;
procedure SaveToFile(FileName: String); virtual;
end;


El declarar los métodos como virtuales te permite diseñar un descendiente de esta clase por cada nueva versión:


type
TWinampList27 = class(TWinampList)
procedure LoadFromFile(FileName: String); override;
procedure SaveToFile(FileName: String); override;
end;

TWinampList31 = class(TWinampList)
procedure LoadFromFile(FileName: String); override;
procedure SaveToFile(FileName: String); override;
end;

TWinampList56 = class(TWinampList)
procedure LoadFromFile(FileName: String); override;
procedure SaveToFile(FileName: String); override;
end;


Cada descendiente se ocupa del formato en particular para implementar los métodos virtuales. Tu aplicación (o aplicaciones) que requieran usar listas de Winamp lo único que requieren es que se les provea de un objeto TWinampList sin tener que saber exactamente de qué descendiente en particular se trata.

Veamos un ejemplo. Suponte que tu aplicación tiene un botón para cargar una lista de ejecución. Con este botón el usuario escoge un archivo del disco y tu aplicación carga la lista en un ListBox:


procedure LoadButtonClick(Sender: TObject);
var
I: Integer;

begin
if OpenDialog.Execute then
begin
WinampList.LoadFromFile(OpenDialog.FileName);

for I := 0 to WinampList.Count - 1 do
ListBox.Items.Add(TWinampItem(WinampList[I]).Theme);
end;
end;


donde WinampList es un objeto de tipo TWinampList (la clase base).

Lo importante aquí es que idependientemente de qué descendiente de TWinampList uses para construir el obejto WinampList, el código de tu botón no cambia, asó como no cambiará cualquier código que deba utilizar WinampList.

Lo que hay que ver entonces es cómo escoges cual descendiente usar:


WinampList := TWinampList27.Create;
WinampList := TWinampList31.Create;
WinampList := TWinampList56.Create;


sin tener que alterar el código que ya tienes. Lo más simple sería cambiar por la línea adecuada con cada nueva versión y recompilar pero a fin de cuentas no esperarás que todos los usuarios trabajen con la misma versión: quizá quieran leer una lista de un formato distinto al que tienes preparado.

Una opción elegante es usar un fábrica. En la unidad donde declares la clase base, pones una variable global que mantenga una asociación entre la versión y la clase a usar:


unit WinampList;

interface

uses
Classes, Contnrs;

type
TWinampList = class(TObjectList)
public
procedure LoadFromFile(FileName: String); virtual; abstract;
procedure SaveToFile(FileName: String); virtual; abstract;
end;

TWinampListClass = class of TWinampList;

var
Factory: TStringList;

implementation

initialization
Factory := TStringList.Create;
end.


Factory mantendría la asociación:

'ver27' --> TWinampList27
'ver31' --> TWinampList31
'ver56' --> TWinampList56

etc.

Cada nueva versión la implementas en una unidad distinta y ahí añades la correspondiente asociación:


unit WinampList27;

interface

uses
WinampList;

type
TWinampList27 = class(TWinampList)
procedure LoadFromFile(FileName: String); override;
procedure SaveToFile(FileName: String); override;
end;

implementation

initialization
Factory.AddObject('ver27', TObject(TWinampList27));
end.


Con esto, lo único que necesitas para construir WinampList adecuadamente es una cadena con el número de versión:


var
Version: String; // contiene el número de versión

...

var
WinampList: TWinampList;
WinampListClass: TWinampListClass;

begin
WinampListClass := TWinampListClass(Factory.Objects[Version]);
WinampList := WinampListClass.Create;
end;


Al requerir sólo el número de versión para construir la clase adecuada logras no tener que incluir la unidad correspondiente en el código de tu aplicación, únicamente tienes que incluir la unidad de la clase base: WinampList. Así tal código no necesita ni siquiera ser recompilado con cada versión nueva que aparezca.

Incluso puedes permitir que el usuario escriba el mismo de qué versión es la lista que va a leer y todo el formulario donde haga esto no cambia en lo absoluto con la aparición de nuevas versiones.

Tomando en cuenta que el código del botón para leer una lista desde el disco es seguramente tan sólo un ejemplo, en balance, aun cuando debes preocuparte por implementar LoadFromFile y SaveToFile para cada versión, ganas bastante al dejar intacto todo el código alrededor.

// Saludos

Delphius
15-01-2005, 17:40:52
Muchisimas gracias Roman...
Pedí una ayudita y me diste una ayuda enorme... gracias... pondré en práctica lo que me pasaste...
Eso me demuestra que me hace falta leer un poco la Cara Oculta de Delphi y no tenerlo de adorno... no sabía que se podría hacer eso

roman
15-01-2005, 20:22:49
Si te vas a poner manos a la obra te recomiendo que leas acerca de las referencias de clase (class references). Variables del tipo TWinampListClass son referencias de clase, es decir variables cuyos valores son un tipo de datos: TWinampList27, TWinampList32, etc. y te permiten construir polimórficamente un objeto. De esto puedes encontrar información en la misma ayuda de Delphi. También deberás agregar un constructor virtual a la clase base TWinampList pues creo que TObjectList no lo tiene y es imprescindible para que funcionen las referencias de clases.

Por último te recomiendo que antes de entrar a los detalles de como manejar un formato en particular hagas pruebas con toda la estructura alrededor (la definición de las clases, la fábrica, etc. para ver que se crean bien los objetos del tipo adecuado. Y hasta que tengas listo eso, te metes con los detalles técnicos de los formatos.

// Saludos

Lepe
16-01-2005, 11:24:58
OFFTOPIC:
Nunca he mirado el menú "Puntuar HIlo" y para una vez que lo miro, falta una opción por encima de "Excelente" :p

Me he quedado alucinado no sólo con la técnica explicada, sino aún más viendo la claridad con la que ha sido expuesta.

Un saludo enorme para Roman