Está en la página 1de 9

Factory Method (Mtodo de Fbrica)

ltima modificacin: Enero 10 de 1013

Descripcin
En general, todas las subclases en una jerarqua de clases heredan los mtodos implementados en la clase padre. Una subclase puede sobreescribir la implementacin de la clase padre para ofrecer diferentes tipos de funcionalidad para el mismo mtodo. Cuando un objeto de una aplicacin es consciente de la funcionalidad exacta que requiere, puede directamente instanciar la clase de la jerarqua de clases que ofrece la funcionalidad requerida. A veces, un objeto de una aplicacin solo puede saber que necesita acceder a una clase de la jerarqua de clases, pero no la conoce exactamente, es decir, no sabe exactamente qu clase de entre el conjunto de subclases debe seleccionar. La seleccin de una clase apropiada puede depender de factores tales como: El estado de la aplicacin que se ejecuta. La configuracin de la aplicacin La expansin de los requisitos o mejoras

En tales casos, un objeto de la aplicacin necesita implementar los criterios de seleccin de la clase para instanciar una clase apropiada de la jerarqua para acceder a sus servicios. En este caso, la patrn Factory Method recomienda encapsular la funcionalidad requerida, para seleccionar e instanciar la clase apropiada, dentro de un mtodo denominado factory method. De esta manera, un factory method puede ser definido como un mtodo de una clase que: Selecciona una clase apropiada de una jerarqua de clases basado en el contexto de la aplicacin y otros factores influyentes. Instancia la clase seleccionada y la retornar como una instancia tipo clase padre.

La encapsulacin de la implementacin requerida para seleccionar e instanciar una clase apropiada en un mtodo separado tiene las siguientes ventajas: Los objetos de la aplicacin pueden utilizar el mtodo de fbrica para acceder a una instancia de la clase apropiada. Se elimina la necesidad de un objeto de la aplicacin el negociar con la variacin de los criterios de seleccin de la clase. Adems de los criterios de seleccin de la clase, el mtodo de fbrica tambin implementa un mecanismo especial requerido para instanciar la clase seleccionada. Esto es aplicable si clases diferentes en la jerarqua necesitan ser instanciadas de diferentes formas. El mtodo de fbrica oculta esos detalles de los objetos de aplicacin y elimina la necesidad de tratar con esas complejidades. Debido a que el mtodo de fbrica retorna la instancia de la clase seleccionada como un objeto de tipo clase padre, un objeto de la aplicacin no tiene que ser consciente de la existencia de la clase en la jerarqua.

Ejemplo
Diseemos la funcionalidad de registro de mensajes en una aplicacin. En general, el registro de

mensajes es una de las tareas ms comunes en las aplicaciones. Esto es algo til en la depuracin y monitoreo de aplicaciones. Debido a que el registro de mensajes puede ser requerido por diferentes clientes, podra ser una buena idea mantener la funcionalidad en una clase comn para que los objetos cliente no tengan que repetir esos detalles. En general, un mensaje entrante podra ser registrado en diferentes medios, en diferentes formatos. Se puede definir una interfaz Logger utilizada por objetos clientes que registran mensajes, y clases concretas que implementan esta interface. La clase implementadora FileLogger almacena los mensajes entrantes en un archivo. La clase implementadora ConsoleLogger visualiza los mensajes entrantes en pantalla. Este diseo se puede visualizar en la Figura 1.

Figura 1: Jerarqua de mensajes de la utilidad registro de mensajes Aplicando el patrn Factory Method, la implementacin necesaria para seleccionar e instanciar un implementador Logger puede ser encapsulado en un mtodo aparte getLogger de una clase aparte LoggerFactory. El mtodo getLogger verifica la propiedad del archivo logger.properties para establecer si el registro a un archivo est activo y as decidir cual implementacin de Logger debe instanciar. Con este diseo, los objetos cliente no requieren tratar con las complicaciones de la seleccin e instanciacin del implementador Logger adecuado. Los objetos cliente no requieren conocer la existencia de diferentes implementadores de Logger y su funcionalidad asociada. Cuando un objeto cliente (LoggerTest) requiere registrar un mensaje debe: Invocar le mtodo getLogger de la fbrica. Invocar el mtodo log expuesto en la interface Logger sobre el objeto retornado.

El diseo completo se muestra en la Figura 2.

Figura 2: Diseo completo En flujo de mensajes se muestra en la Figura 3.

Figura 3: Flujo de mensajes

Cdigo Fuente en Java


A continuacin la implementacin en Java.
public interface Logger { public void log(String msg); } public class ConsoleLogger implements Logger { public void log(String msg) { System.out.println(msg); } } public class FileLogger implements Logger { public void log(String msg) { FileUtil futil = new FileUtil(); futil.writeToFile("log.txt", msg, true, true); } }

import java.io.IOException; import java.util.Properties; public class LoggerFactory { public boolean isFileLoggingEnabled() { Properties p = new Properties(); try { p.load(ClassLoader.getSystemResourceAsStream("logger.properties")); String fileLoggingValue = p.getProperty("FileLogging"); if (fileLoggingValue.equalsIgnoreCase("ON") == true) return true; else return false; } catch (IOException e) { return false; } } //Factory Method public Logger getLogger() { if (isFileLoggingEnabled()) { return new FileLogger(); } else { return new ConsoleLogger(); } } } /** * Cliente * */ public class LoggerTest { public static void main(String[] args) { LoggerFactory factory = new LoggerFactory(); Logger logger = factory.getLogger(); logger.log("A Message to Log"); } }

Contenido del Archivo logger.properties (Grabarlo dentro de la carpeta src):


FileLogging=OFF

Cdigo Fuente en C#
A continuacin la implementacin en C#. Tener en cuenta que para C# se va a seguir las convenciones de nombres del lenguaje (http://codigolinea.com/2008/05/25/estilo-de-programacion-y-convencion-denombres-ii/ ). Por ejemplo, los mtodos empiezan con maysculas. Algunas librerias que ya existen en Java tuvieron que ser creadas en C#, como el caso de la clase Properties. Cree cada clase en su propio archivo. Cree una solucin C# de Windows llamada PatronesDiseo. A esta solucin agregue un proyecto llamado Librerias. Dentro del proyecto Librerias cree una carpeta Libs. Dentro de Libs cree las siguientes clases de C#:

using System; namespace Libs { /// <summary> /// Utilidad para archivos. Lee y escribe datos a un archivo /// </summary> public class FileUtil { /// <summary> /// Constructor /// </summary> public FileUtil() { } /// <summary> /// Graga un mensaje a un archivo /// </summary> /// <param name="file">Nombre del archivo</param> /// <param name="msg">Mensaje a grabar</param> public void WriteToFile(String msg, String file) { //Falta la logica que abre y graba datos al archivo. Por ahora, un simple mensaje por consola. Console.WriteLine("El mensaje: " + msg + ", ha sido grabado con exito en el archivo " + file); } } }

using System; using System.Collections.Generic; using System.IO; namespace Libs { /// <summary> /// Permite leer los atributos de un archivo de configuracion, al estilo atributo=valor /// </summary> public class Properties { /// <summary> /// Un diccionario representa una coleccion de pares clave y valor /// </summary> private Dictionary<String, String> List; private String FileName; public Properties(String file) { Reload(file); } public String Get(String field, String defValue) { return (Get(field) == null) ? (defValue) : (Get(field)); } public String Get(String field) { return (List.ContainsKey(field)) ? (List[field]) : (null); }

public void Set(String field, Object value) { if (!List.ContainsKey(field)) List.Add(field, value.ToString()); else List[field] = value.ToString(); } public void Reload() { Reload(this.FileName); } public void Reload(String filename) { this.FileName = filename; List = new Dictionary<String, String>(); if (System.IO.File.Exists(filename)) LoadFromFile(filename); else System.IO.File.Create(filename); } private void LoadFromFile(String file) { foreach (String line in System.IO.File.ReadAllLines(file)) { if ((!String.IsNullOrEmpty(line)) && (!line.StartsWith(";")) && (!line.StartsWith("#")) && (!line.StartsWith("'")) && (line.Contains("="))) { int index = line.IndexOf('='); String key = line.Substring(0, index).Trim(); String value = line.Substring(index + 1).Trim(); if ((value.StartsWith("\"") && value.EndsWith("\"")) || (value.StartsWith("'") && value.EndsWith("'"))) { value = value.Substring(1, value.Length - 2); } try { //ignore dublicates List.Add(key, value); } catch { } } } } /// <summary> /// Metodo estatico para leeer todos los atributos de un archivo de configuracion /// </summary> /// <param name="path">ruta del archivo</param> /// <returns>Dictionary de datos</returns> public static Dictionary<string, string> GetProperties(string path)

{ string fileData = ""; using (StreamReader sr = new StreamReader(path)) { fileData = sr.ReadToEnd().Replace("\r", ""); } Dictionary<string, string> Properties = new Dictionary<string, string>(); string[] kvp; string[] records = fileData.Split("\n".ToCharArray()); foreach (string record in records) { if (record.Length == 0) break; //Para monodevelop kvp = record.Split("=".ToCharArray()); Properties.Add(kvp[0], kvp[1]); } return Properties;

} //Fin Clase Properties

} //Fin metodo GetProperties

Ahora a la solucin agregue un nuevo proyecto llamado FactoryMethod. A este proyecto agregue las referencias del proyecto Librerias: click derecho en references/agregar referencia/Proyectos/Librerias/Aceptar. Dentro de FactoryMethod cree una carpeta Ejm1. Dentro de Ejm1, agregue los siguientes archivos.
namespace FactoryMethod.Ejm1 { /// <summary> /// Interface que ser implementada por FileLogger y ConsoleLogger /// </summary> interface Logger { /// <summary> /// Registra un mensaje /// </summary> /// <param name="msg">Mensaje a registrar</param> void Log(string msg); } } using System; namespace FactoryMethod.Ejm1 { /// <summary> /// Registra un mensaje por consola /// </summary> class ConsoleLogger:Logger { public void Log(String msg) { Console.WriteLine(msg + " registrado por consola"); } } }

using System; using Libs; namespace FactoryMethod.Ejm1 { /// <summary> /// Registra un mensaje y lo graba en un archivo de texto /// </summary> class FileLogger: Logger { public void Log(String msg) { FileUtil futil = new FileUtil(); futil.WriteToFile(msg, "log.txt"); } } using System.Collections.Generic; using Libs; namespace FactoryMethod.Ejm1 { /// <summary> /// Fabrica de objetos /// </summary> class LoggerFactory { /// <summary> /// Determina si el parmetro de configuracin FileLogging=ON /// </summary> /// <returns>True si est en ON, false si esta en OFF</returns> public bool IsFileLogginEnabled() { Dictionary<string, string> properties = Properties.GetProperties(@"d:\logger.properties"); if (properties["FileLogging"].Equals("ON")) return true; else return false; } /// <summary> /// Fabrica de objetos /// </summary> /// <returns>Un objeto FileLogger un objeto ConsoleLogger, segun el criterio</returns> public Logger getLogger() { if (IsFileLogginEnabled()) return new FileLogger(); else return new ConsoleLogger(); } } } }

Finalmente cree la aplicacin cliente dentro en la raiz del proyecto FactoryMethod.


using System; using FactoryMethod.Ejm1; namespace FactoryMethod { /// <summary> /// Cliente Main /// </summary> class Program { static void Main(string[] args) { LoggerFactory factory = new LoggerFactory(); Logger logger = factory.getLogger(); logger.Log("Esto es un mensaje"); Console.ReadKey(); //Establezca el parmetro FileLogging=ON y ejecute nuevamente la aplicacin

} } }

Contenido del Archivo logger.properties (grabarlo en d:\logger.properties):


FileLogging=ON

Establezca como proyecto de inicio a FactoryMethod y ejecute la aplicacin.

To Do
1. Adicione un nuevo logger DBLogger que registre mensajes en una base de datos. 2. Cree una subclase de LoggerFactory y sobreescriba el mtodo getLogger de tal manera que tenga un criterio de seleccin diferente.