Si hay un patrón que reconozco a kilómetros de distancia es Fábrica, también llamado Factoría. O para ser más cool en su nombre original en inglés: Factory. Me lo he estudiado tanto y por tanto tiempo que ya me sale instantáneo.
Como ha propuesto Héctor Randolph, el patrón aplica a estos tipos de casos. El enfoque de Wilson es más que interesante, y seguro que funciona. Pero como yo ando siempre disfrazado del Hoplita con mi espada UML y el escudo Patrones propongo seguir la sintonía de Hector y emplear Fábrica.
Hay variadas maneras de encarar al
patrón. Y admito que el estilo que he aprendido del maestro roman es uno de los mejores que he visto. Voy a imitarlo:
1) En una unidad UFactory dispondremos una variable de tipo TStringList que hará de fábrica:
var
Factory: TStringList;
Como tal será nuestro punto de acceso para poder crear los diferentes forms (para este caso. El patrón es general y aplica a cualquier grupo de objetos a crear).
2) Esta variable será de acceso "global" y actuará de Singleton. Por tanto agregamos las secciones Initialization y Finalization a la Unit para definir su tiempo de vida:
Initilization
Factory := TStringList.Create;
Finalization
Factory.Free;
3) Ya tenemos nuestra fábrica. Ahora hay que disponer los productos que va a crear. Primero, todos estos forms van a heredar de uno base. Así que comencemos con eso:
type
TFormBase = class(TForm)
....
end;
4) Ya con esta clase la magia es aprovechar las referencia de clases. Por tanto añadiremos una para esta clase base:
type
TFormBaseClass = class of TFormBase;
5) Ahora necesitamos una función que haga justamente el trabajo sucio, crear la clase apropiada según la referencia:
function CreateForm(NameForm: string): TFormBase;
var
FormClass: TFormBaseClass;
Idx: integer;
begin
// Buscamos en la fábrica el form correspondiente por el nombre y aprovechamos la referencia de clase.
Idx := Factory.IndexOf(NameForm);
FormClass := TFormClass(Factory.Objects[Idx]);
// Ya con la referencia, creamos una instancia del form correspondiente
result := FormClass.Create;
end;
6) Para que funcione cada form pensado que se va a heredar del base se va a registrar en la fábrica:
Form Heredado:
TFormHeredadoX = class(TFormBase)
...
end;
Nos registramos:
Factory.AddObject('NombreDadoFormX', TObject(TFormHeredadoX));
Lo ideal es que cada form a heredar esté en su propia unit, por cuestiones de comodidad y limpieza de código. Cuantos más forms se tenga a crear, más código tendrá la Unit UFactory por lo que es mejor dejar a cada form heredado fuera y agregar en su sección Uses a UFactory.
Por "NombreDadoFormX" es el nombre que vamos a pasar en la función CreateForm.
¿Cuándo registrar cada form? Cualquier momento. Aunque para hacerlo fácil quizá sea más apropiado si se destinara la sección Initialization de la clase cliente (la que hará uso de los forms y pedirá a la fábrica que los cree), o Tener un procedimiento RegisterForms que haga esto:
uses UFactory, UFormHeredado1, ..., UFormHeredadoN;
...
Factory.AddObject(FormHeredado1, TObject(TFormHeredado1));
//...
Factory.AddObject(FormHeredadoN, TObject(TFormHeredadoN));
Naturalmente se puede disponer en UFactory un método que nos haga el trabajo de registrarlo. y no estar repitiendo trabajo:
function RegisterForm(FormName: strings; FormClass: TFormBaseClass): integer;
begin
result := Factory.AddObject(FormName, TObject(FormClass));
end;
Y entonces con este hipotético RegisterForm en lugar de estar buscando por el nombre también podríamos recuperar por el índice.
Ya con esto está la lección dada. Espero que se haya entendido el objetivo. No he probado el código, lo escribí de memoria pero creo que anda. Esta es una de las tantas formas de hacer uso del patrón... "para complicarlo" más se puede disponer de una clase propia TFactory que encapsule dentro de esta un StringList.
La ventaja de esta forma es que es extensible y no se limita a una cierta cantidad de productos posibles a generar. Muy útil cuando hay muchos, pero si son apenas unos pocos quizá sea más directo tener algo directamente como:
TFactory = class
private
FForm1: TFormHeredado1;
FForm2: TFormHeredado2;
FFormN: TFormHeredadoN;
...
end;
Es decir no manejarse con listas y directamente tener cada form como atributo y entonces el método CreateForm simplemente regresa la instancia en cuestión.
Repito, como he dicho: el patrón se puede usar de mil formas. La idea central es justamente centrar la creación de los objetos en una clase. Luego las clases clientes simplemente le piden a ésta que les regrese el objeto cuando sea necesario.
Ya que ando en modo profe, va pregunta de examen para la comunidad. ¿Que 3 patrones, y/o conceptos, escenciales son los que dan forma al patrón Fábrica?
Saludos,