Я переусердствовал его со своим Методом фабрики?

Часть нашего основного продукта является веб-сайтом 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 значений, что метод фабрики довольно долог. Каждый раз, когда я создаю новый тип виджета, я иду для добавления еще нескольких строк к методу, и немного предупреждения звучит в моей голове, что, возможно, это не лучший способ сделать вещи. Я склонен просто игнорировать то предупреждение, но это становится громче.

Так, я делаю его неправильно? Существует ли лучший способ обработать этот сценарий?

5
задан skaffman 19 December 2009 в 12:40
поделиться

7 ответов

I think the question you should ask yourself is: Why am I using a Factory method here?

If the answer is "because of A", and A is a good reason, then continue doing it, even if it means some extra code. If the answer is "I don't know; because I've heard that you are supposed to do it this way?" then you should reconsider.

Let's go over the standard reasons for using factories. Here's what Wikipedia says about the Factory method pattern:

[...], it deals with the problem of creating objects (products) without specifying the exact class of object that will be created. The factory method design pattern handles this problem by defining a separate method for creating the objects, whose subclasses can then override to specify the derived type of product that will be created.

Since your WidgetFactory is Private, this is obviously not the reason why you use this pattern. What about the "Factory pattern" itself (independent of whether you implement it using a Factory method or an abstract class)? Again, Wikipedia says:

Use the factory pattern when:

  • The creation of the object precludes reuse without significantly duplicating code.
  • The creation of the object requires access to information or resources not appropriate to contain within the composing object.
  • The lifetime management of created objects needs to be centralised to ensure consistent behavior.

From your sample code, it does not look like any of this matches your need. So, the question (which only you can answer) is: (1) How likely is it that you will need the features of a centralized Factory for your widgets in the future and (2) how costly is it to change everything back to a Factory approach if you need it in the future? If both are low, you can safely drop the Factory method for the time being.


EDIT: Let me get back to your special case after this generic elaboration: Usually, it's a = new XyzWidget() vs. a = WidgetFactory.Create(WidgetType.Xyz). In your case, however, you have some (numeric?) typeId from a database. As Mark correctly wrote, you need to have this typeId -> className map somewhere.

So, in that case, the good reason for using a factory method could be: "I need some kind of huge ConvertWidgetTypeIdToClassName select-case-statement anyway, so using a factory method takes no additional code plus it provides the factory method advantages for free, if I should ever need them."

As an alternative, you could store the class name of the widget in the database (you probably already have some WidgetType table with primary key typeId anyway, right?) and create the class using reflection (if your language allows for this type of thing). This has a lot of advantages (e.g. you could drop in DLLs with new widgets and don't have to change your core CMS code) but also disadvantages (e.g. "magic string" in your database which is not checked at compile time; possible code injection, depending on who has access to that table).

15
ответ дан 18 December 2019 в 05:49
поделиться

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

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

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

4
ответ дан 18 December 2019 в 05:49
поделиться

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

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

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

В качестве альтернативы вы можете определить карту как словарь,

5
ответ дан 18 December 2019 в 05:49
поделиться

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

2
ответ дан 18 December 2019 в 05:49
поделиться

try categories your widgets, maybe based on their functionality. if few of them are logically depending on each other, create them with single construction

0
ответ дан 18 December 2019 в 05:49
поделиться

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

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

2
ответ дан 18 December 2019 в 05:49
поделиться

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

1
ответ дан 18 December 2019 в 05:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: