Я переусердствовал с моим фабричным методом? - PullRequest
5 голосов
/ 17 декабря 2009

Частью нашего основного продукта является веб-сайт CMS, который использует различные виджеты страниц. Эти виджеты отвечают за отображение контента, перечисление продуктов, обработку регистрации событий и т. Д. Каждый виджет представлен классом, который является производным от базового класса виджетов. При отображении страницы сервер извлекает виджет страницы из базы данных, а затем создает экземпляр правильного класса. Заводской метод, верно?

Private Function WidgetFactory(typeId)
    Dim oWidget
    Select Case typeId
        Case widgetType.ContentBlock
            Set oWidget = New ContentWidget
        Case widgetType.Registration
            Set oWidget = New RegistrationWidget
        Case widgetType.DocumentList
            Set oWidget = New DocumentListWidget
        Case widgetType.DocumentDisplay
    End Select
    Set WidgetFactory = oWidget
End Function

В любом случае, все в порядке, но со временем количество типов виджетов увеличилось до 50, что означает, что заводской метод довольно длинный. Каждый раз, когда я создаю виджет нового типа, я добавляю в метод еще пару строк и в голове звучит маленькая тревога, что, возможно, это не лучший способ сделать что-то. Я склонен просто игнорировать эту тревогу, но она становится громче.

Итак, я делаю это неправильно? Есть ли лучший способ справиться с этим сценарием?

Ответы [ 7 ]

15 голосов
/ 17 декабря 2009

Я думаю, что вы должны задать себе вопрос: Почему я здесь использую метод Фабрики?

Если ответ « из-за A », и A является веской причиной, продолжайте делать это, даже если это означает некоторый дополнительный код. Если ответ «, я не знаю; потому что я слышал, что вы должны делать это таким образом? », тогда вам следует пересмотреть.

Давайте рассмотрим стандартные причины использования заводов. Вот что Википедия говорит о шаблоне метода Factory:

[...], он занимается проблемой создания объектов (продуктов) без указания точного класса объекта, который будет создан. Шаблон проектирования фабричного метода решает эту проблему, определяя отдельный метод для создания объектов, чьи подклассы можно затем переопределить, чтобы указать производный тип продукта, который будет создан.

Поскольку ваш WidgetFactory равен Private, это, очевидно, не причина, по которой вы используете этот шаблон. Как насчет самого «шаблона фабрики» (независимо от того, реализуете ли вы его с помощью метода фабрики или абстрактного класса)? Опять же, Википедия говорит :

Использовать фабричный шаблон при:

  • Создание объекта исключает повторное использование без значительного дублирования кода.
  • Создание объекта требует доступа к информации или ресурсам, которые не должны содержаться в составляющем объекте.
  • Управление жизненным циклом созданных объектов необходимо централизовать для обеспечения согласованного поведения.

Из вашего примера кода не похоже, что все это соответствует вашим потребностям. Итак, вопрос (на который могут ответить только вы): (1) Насколько вероятно, что вам понадобятся функции централизованной фабрики для ваших виджетов в будущем и (2) насколько это дорого изменить все обратно на заводской подход, если он понадобится вам в будущем? Если оба значения низкие, вы можете временно отказаться от метода Factory.


РЕДАКТИРОВАТЬ: Позвольте мне вернуться к вашему особому случаю после этой общей разработки: обычно это a = new XyzWidget() против a = WidgetFactory.Create(WidgetType.Xyz). В вашем случае, однако, у вас есть (числовой?) typeId из базы данных. Как правильно написал Марк, вам нужна эта typeId -> className карта где-то .

Таким образом, в этом случае хорошей причиной для использования фабричного метода может быть: «В любом случае мне нужен какой-то огромный ConvertWidgetTypeIdToClassName оператор select-case-case, поэтому использование фабричного метода не требует дополнительного кода plus он предоставляет преимущества заводского метода бесплатно, если они мне когда-нибудь понадобятся. "

В качестве альтернативы, вы можете сохранить имя класса виджета в базе данных (у вас, вероятно, уже есть таблица WidgetType с первичным ключом typeId в любом случае, верно?) И создать класс, используя отражение (если ваш язык позволяет для этого типа вещей). Это имеет много преимуществ (например, вы можете добавить DLL-файлы с новыми виджетами и не нужно менять свой основной код CMS), но также и недостатки (например, «волшебная строка» в вашей базе данных, которая не проверяется во время компиляции; возможный код) инъекция, в зависимости от того, кто имеет доступ к этой таблице).

5 голосов
/ 17 декабря 2009

Метод WidgetFactory действительно отображает из перечисления typeId на конкретные классы. В целом, лучше, если вы можете полностью избежать перечислений, но иногда (особенно в веб-приложениях) вам нужно совершить круговую поездку в среду (например, браузер), которая не понимает полиморфизм, и вам нужны такие меры.

Рефакторинг содержит довольно хорошее объяснение того, почему операторы switch / select case являются запахами кода, но в основном это относится к случаю, когда у вас много подобных переключателей.

Если ваш метод WidgetFactory является единственным местом , где вы включаете это конкретное перечисление, я бы сказал, что вам не о чем беспокоиться. Тебе нужна эта карта где-то.

В качестве альтернативы вы можете определить карту как словарь, но количество строк кода не уменьшится значительно - возможно, вы сможете сократить строки кода пополам, но степень сложности останется эквивалентной.

4 голосов
/ 18 декабря 2009

Ваше применение фабричного образца правильное. У вас есть информация, которая определяет, какой из N типов создан. Фабрика - это то, что знает, как это сделать. (Это немного странно как частный метод. Я ожидаю, что он будет на интерфейсе IWidgetFactory.)

Однако ваша реализация тесно связывает реализацию с конкретными типами. Если вместо этого вы отобразите typeId -> widgetType, вы можете использовать Activator.CreateInstance(widgetType), чтобы заставить фабрику понимать любой тип виджета.

Теперь вы можете определять отображения так, как хотите: простой словарь, обнаружение (атрибуты / отражение), файл конфигурации и т. Д. Вы должны знать все типы в одном месте, но у вас также есть возможность составить несколько источников.

2 голосов
/ 18 декабря 2009

Правильно это или нет, я всегда считал, что время использовать Factory - это когда решение о том, какой тип объекта создавать, будет основано на информации, которая недоступна до времени выполнения.

В последующем комментарии вы указали, что тип виджета хранится в базе данных. Поскольку ваш код не знает, какие объекты будут создаваться до времени выполнения, я думаю, что это совершенно правильное использование шаблона Factory. Имея фабрику, вы позволяете своей программе откладывать решение о том, какой тип объекта использовать, до момента, когда это решение действительно может быть принято.

2 голосов
/ 17 декабря 2009

Классический способ реализации фабрики - не использовать гигантский переключатель или if-ladder, а вместо этого использовать карту, которая отображает имя типа объекта на функцию создания объекта. Помимо всего прочего, это позволяет изменять фабрику во время выполнения.

1 голос
/ 22 декабря 2009

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

0 голосов
/ 17 декабря 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...