2009-03-31 3 views
16

Я считаю, что шаблон шаблона фабричного метода подходит для того, что я пытаюсь сделать, но я не уверен, сколько ответственности (знаний о подклассах, которые он создает), чтобы дать его. Пример использования factory method pattern в Википедии описывает ситуацию, я почти точно:Как фабрика знает, какой тип объекта создать?

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. 
     } 
    } 
} 

мой вопрос, что же функция figureOutImageType выглядеть? В этом конкретном примере я бы предположил, что он проверяет заголовок файла в InputStream, чтобы определить, в каком формате изображения находятся данные. Я хотел бы знать, знает ли он, что сам файл ImageReaderFactory знает, как разбирать заголовки файлов и определять, является ли тип файла GIF , JPEG и т. Д., Или если он вызывает функцию внутри каждого класса Reader, который позволяет ему узнать, какой тип изображения он есть. Что-то вроде этого, может быть:

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

Похоже, имея завод знать, как анализировать изображения брейков инкапсуляции и позволяя подклассы решить, какой должны быть созданы является частью методы фабрики шаблона проектирования. Тем не менее, также кажется, что функция figureOutImageType просто добавляет некоторый избыточный код, потому что почему бы не просто выполнить каждый подкласс для проверки на InputStream в функции getImageReader и пропустить регистр коммутатора?

У меня не было опыта использования фабрик раньше, и я надеялся получить некоторое представление от некоторых людей, которые использовали их в прошлом, чтобы наилучшим образом справиться с этой проблемой. Хорошо ли знать фабрику о внутренних разработках своих подклассов или они должны нести ответственность за то, чтобы дать фабрике знать, что создавать, и как вы ее организуете?

Спасибо!

+0

Отлично, хорошо продуманный и сформулированный вопрос. +1, больше, если это было возможно! – hmcclungiii

+0

Согласовано. Отличный вопрос. – Ankur

ответ

6

Завод должен иметь некоторое представление о выборе фактического объекта для создания. Например, метод WebRequest.Create в .NET должен иметь возможность выбирать между различными клиентами протокола, проверяя часть протокола Uri. Ему не нужно разбирать все это. Просто часть, необходимая для того, чтобы отличить, какой класс будет отвечать за нее (в вашем примере это, вероятно, будет только заголовок файла).

Что касается вашего вопроса о нарушении инкапсуляции, не совсем ... В большинстве случаев завод жестко запрограммирован и уже знает о различных типах классов и их возможностях. Это уже зависит от функциональности, предлагаемой известным набором классов, поэтому вы не много добавляете к ней. Вы также можете инкапсулировать часть обнаружения на заводе в другом вспомогательном классе, который может использоваться как на заводе, так и на подклассах (в режиме sprit of DRY).

+0

Спасибо за ваш вклад. Поэтому вы можете рекомендовать использовать общий класс helper/utility для идентификации типа файла из InputStream в функции getImageReader и просто включить возвращаемое перечисление? – Venesectrix

+0

Да, это хороший подход. –

0

У меня был бы статический метод CanReadFrom (или что-то еще) в общем интерфейсе ImageReader (не уверен, что это возможно - FIXME). Используйте рефлексию, чтобы захватить всех исполнителей и вызвать функцию. Если кто-то возвращает true, верните экземпляр класса.

1

Для расширяемости вы можете выделить некоторые из этих зависимостей, которые вы упомянули. Как выяснить, какой файл он представляет или сопоставить тип файла с классом, который его обрабатывает. Внешний реестр (т. Е. Файл свойств) будет хранить, скажем, GIF -> GifReader или лучше GIF -> GifMetadataClass. Тогда ваш код может быть общим и не иметь зависимостей от всех классов, плюс вы можете продлить его в будущем, или третьи стороны могут его расширить.

+0

Я думаю, вы говорите, что я могу использовать карту, чтобы связать тип файла с классом, который обрабатывает этот тип файла, правильно? Как бы фабрика определяла, какой тип файла она была основана на InputStream в этом случае? Я не уверен, что вы можете настроить карту из InputStream на тип файла. – Venesectrix

+0

Из входного потока многие из этих классов изображений имеют магические числа в начале или, по крайней мере, что-то общее, которое вы могли бы пройти, чтобы извлечь тип изображения. –

+0

Это правда. Я вижу, что вы говорите о том, чтобы сделать его универсальным и расширяемым с помощью ассоциаций, что, безусловно, нужно учитывать. В моем конкретном случае это может не стоить того, потому что у меня есть только несколько различных объектов, которые создаются, и не ожидайте добавления большего количества. – Venesectrix

1

Если это для окон, я попытаюсь угадать тип контента, а затем использовать заводскую настройку. На самом деле я сделал это некоторое время назад.

Вот класс угадать тип содержимого файла:

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

Имеют ли они какие-либо функции поиска MIME в .Net или Windows API называют единственным решением, не содержащим вашей собственной таблицы поиска. – James

+0

Насколько мне известно, в .NET нет обнаружения типа mime. –

2

Оба являются действительными выборы в зависимости от контекста.

ЕСЛИ вы разрабатываете расширяемость - скажем, модель плагина для разных ImageReaders - тогда ваш класс Factory не может знать обо всех возможных ImageReaders. В этом случае вы отправляетесь по маршруту ImageReader.CanRead(ImageStream) - спрашиваете каждого исполнителя, пока не найдете тот, который может его прочитать.

Остерегайтесь, что иногда заказ имеет значение здесь. У вас может быть GenericImageReader, который может обрабатывать JPG, но Jpeg2000ImageReader, который лучше на нем. Прогулка реализаторов ImageReader остановится в зависимости от того, что наступит раньше. Вы можете посмотреть сортировку списка возможных ImageReaders, если это проблема.

В противном случае, если список ImageReaders конечен и находится под вашим контролем, вы можете перейти к более традиционному подходу Factory. В этом случае Factory решает, что создавать. Это уже связано с конкретными реализациями ImageReader с помощью ctor, поэтому добавление правил для каждого ImageReader не увеличивает сцепление. Если логика выбора ImageReader в основном принадлежит самому ImageReader, то, чтобы избежать дублирования кода, вы все равно можете пойти по маршруту ImageReader.CanRead(ImageStream), но он может быть просто жестко запрограммирован, какие типы вы идете.

+0

Спасибо за ответ! Я на самом деле использую C++, и я считаю, что у меня есть только возможность вручную использовать типы ImageReader. Я не знаю, как определить, какие подклассы существуют для базового класса, за исключением того, что они отслеживают их конкретно в коде (жестко закодированы или добавлены в массив и т. Д.), – Venesectrix

Смежные вопросы