2009-03-31 12 views
16

Creo que el patrón de diseño de método de fábrica es apropiado para lo que estoy tratando de hacer, pero no estoy seguro de cuánta responsabilidad (conocimiento de las subclases crea) para darle. El ejemplo del uso del factory method pattern en Wikipedia describe la situación que estoy en casi exactamente:¿Cómo sabe una fábrica qué tipo de objeto crear?

public class ImageReaderFactory 
{ 
    public static ImageReader getImageReader(InputStream is) 
    { 
     int imageType = figureOutImageType(is); 

     switch(imageType) 
     { 
      case ImageReaderFactory.GIF: 
       return new GifReader(is); 
      case ImageReaderFactory.JPEG: 
       return new JpegReader(is); 
      // etc. 
     } 
    } 
} 

Mi pregunta es, ¿qué aspecto tiene la función figureOutImageType como? En este ejemplo específico, supongo que comprueba un encabezado de archivo en el InputStream para determinar en qué formato de imagen están los datos. Me gustaría saber si el ImageReaderFactory sabe cómo analizar los encabezados de los archivos y determinar si el tipo de archivo es GIF. , JPEG, etc., o si llama a una función dentro de cada clase Reader que le permite saber qué tipo de imagen es. Algo como esto, tal vez:

int figureOutImageType(InputStream is) 
{ 
    if(GifReader.isGIF(is)) 
     return ImageReaderFactory.GIF; 
    else if(JpegReader.isJPEG(is)) 
     return ImageReaderFactory.JPEG; 
    // etc. 
} 

Parece como tener la fábrica sabe cómo analizar imágenes se rompe la encapsulación, y dejar que las subclases decidir qué se debe crear uno es parte del patrón de diseño método de fábrica. Sin embargo, también parece que la función figureOutImageType simplemente agrega algún código redundante, porque ¿por qué no simplemente hacer que cada subclase realice su verificación en el InputStream en la función getImageReader y omita la caja del interruptor?

No he tenido ninguna experiencia en el uso de fábricas antes, y esperaba obtener alguna información de algunas personas que los han utilizado en el pasado sobre la mejor manera de manejar este problema. ¿Está bien que la fábrica sepa sobre el funcionamiento interno de sus subclases, o deberían ser los responsables de dejar que la fábrica sepa qué crear, y cómo lo organizas todo?

Gracias!

+0

Excelente, bien pensada y formulada pregunta. +1, ¡más si solo fuera posible! – hmcclungiii

+0

De acuerdo. Excelente pregunta – Ankur

Respuesta

6

Factory debería tener alguna idea acerca de cómo elegir el objeto real para crear. Por ejemplo, el método WebRequest.Create en .NET, debería poder elegir entre los diferentes clientes de protocolo comprobando la parte de protocolo del Uri. No necesita analizar todo. Solo la parte requerida para distinguir qué clase será responsable de ella (en su ejemplo, probablemente será solo el encabezado del archivo).

En relación con su pregunta sobre la encapsulación de ruptura, no realmente ... La mayoría de las veces, la fábrica está codificada y ya conoce los diferentes tipos de clases y sus características. Ya depende de la funcionalidad ofrecida por un conjunto conocido de clases, por lo que no le agrega mucho. También puede encapsular la parte de detección de la fábrica en otra clase auxiliar que puede ser utilizada tanto por la fábrica como por las subclases (en el principio sprit de DRY).

+0

Gracias por su aporte. Entonces, ¿podría recomendar que use una clase compartida de ayuda/utilidad para identificar el tipo de archivo del InputStream en la función getImageReader y simplemente activar la enumeración devuelta? – Venesectrix

+0

Sí, este es un buen enfoque. –

0

Tendría un método CanReadFrom estático (o algo así) en la interfaz común ImageReader (no estoy seguro si esto es posible - FIXME). Use la reflexión para agarrar a todos los implementadores y llamar a la función. Si uno devuelve verdadero, devuelve una instancia de la clase.

1

Para la extensibilidad, puede externalizar algunas de estas dependencias que menciona. Como averiguar qué tipo de archivo es o asignar el tipo de archivo a una clase que lo maneja. Un registro externo (es decir, un archivo de propiedades) almacenaría decir, GIF -> GifReader o mejor GIF -> GifMetadataClass. Entonces su código podría ser genérico y no tener dependencias en todas las clases, además podría extenderlo en el futuro, o terceros podrían extenderlo.

+0

Creo que está diciendo que podría usar un mapa para asociar un tipo de archivo con una clase que maneje ese tipo de archivo, ¿verdad? ¿Cómo determinaría la fábrica qué tipo de archivo estaba basado en InputStream en este caso? No estoy seguro de que pueda configurar un mapa desde el InputStream al tipo de archivo. – Venesectrix

+0

Desde el flujo de entrada, muchas de estas clases de imágenes tienen números mágicos al principio, o al menos algo genérico que podría pasar para extraer el tipo de imagen. –

+0

Eso es verdad. Veo lo que dices sobre hacerlo genérico y extensible usando asociaciones, que definitivamente es algo a considerar. En mi caso específico, puede que no valga la pena, porque solo tengo algunos objetos diferentes que se crean y no anticipo que se agreguen muchos más. – Venesectrix

1

Si esto es para Windows trataría de adivinar el tipo de contenido y luego usar fábrica. De hecho, hice esto hace un tiempo.

Aquí es una clase de adivinar el tipo de contenido de un archivo:

using System; 
using System.IO; 
using System.Runtime.InteropServices; 

namespace Nexum.Abor.Common 
{ 
    /// <summary> 
    /// This will work only on windows 
    /// </summary> 
    public class MimeTypeFinder 
    { 
     [DllImport(@"urlmon.dll", CharSet = CharSet.Auto)] 
     private extern static UInt32 FindMimeFromData(
      UInt32 pBC, 
      [MarshalAs(UnmanagedType.LPStr)] String pwzUrl, 
      [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, 
      UInt32 cbSize, 
      [MarshalAs(UnmanagedType.LPStr)]String pwzMimeProposed, 
      UInt32 dwMimeFlags, 
      out UInt32 ppwzMimeOut, 
      UInt32 dwReserverd 
     ); 

     public string getMimeFromFile(string filename) 
     { 
      if (!File.Exists(filename)) 
       throw new FileNotFoundException(filename + " not found"); 

      var buffer = new byte[256]; 
      using (var fs = new FileStream(filename, FileMode.Open)) 
      { 
       if (fs.Length >= 256) 
        fs.Read(buffer, 0, 256); 
       else 
        fs.Read(buffer, 0, (int)fs.Length); 
      } 
      try 
      { 
       UInt32 mimetype; 
       FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0); 
       var mimeTypePtr = new IntPtr(mimetype); 
       var mime = Marshal.PtrToStringUni(mimeTypePtr); 
       Marshal.FreeCoTaskMem(mimeTypePtr); 
       return mime; 
      } 
      catch (Exception) 
      { 
       return "unknown/unknown"; 
      } 
     } 
    } 
} 
+0

Tienen funciones de búsqueda MIME en .Net o la API de Windows es la única solución para almacenar su propia tabla de búsqueda. – James

+0

Que yo sepa, no hay detección de tipo MIME en .NET –

2

Ambas son opciones válidas según el contexto.

Si está diseñando para la extensibilidad, digamos un modelo de complemento para diferentes lectores de imágenes, entonces su clase de fábrica no puede conocer todos los posibles lectores de imágenes. En ese caso, vaya a la ruta ImageReader.CanRead(ImageStream), preguntándole a cada implementador hasta que encuentre uno que pueda leerlo.

Tenga en cuenta que a veces el orden importa aquí. Puede tener un GenericImageReader que pueda manejar archivos JPG, pero un Jpeg2000ImageReader que sea mejor. Caminar por los implementadores de ImageReader se detendrá en el que sea primero. Es posible que desee ver la clasificación de la lista de posibles lectores de imágenes si eso es un problema.

De lo contrario, si la lista de ImageReaders es finita y está bajo su control, entonces puede utilizar el enfoque Factory más tradicional. En ese caso, la fábrica decide qué crear. Ya está acoplado a las implementaciones concretas de ImageReader por el ctor, por lo que agregar reglas para cada ImageReader no aumenta el acoplamiento. Si la lógica para elegir un ImageReader se encuentra principalmente en el ImageReader, para evitar la duplicación de código, aún puede seguir la ruta ImageReader.CanRead(ImageStream), pero podría codificarse simplemente qué tipos camina.

+0

¡Gracias por la respuesta! En realidad estoy usando C++, y creo que solo tengo la opción de recorrer manualmente los tipos de ImageReader. No sé de una manera de determinar qué subclases hay para una clase base, excepto para hacer un seguimiento de ellas específicamente en el código (codificadas o agregadas a una matriz, etc.) – Venesectrix

Cuestiones relacionadas