Каков ваш порог использования фабрики вместо конструктора для создания объекта? - PullRequest
11 голосов
/ 29 января 2009

Каков ваш порог использования фабрики вместо конструктора для создания объекта?

  1. Вы всегда используете фабрику.
  2. Вы используете фабрики, только если у вас есть инвариантные проверки, отличные от проверки на нулевые значения.
  3. Вы всегда используете конструкторы
  4. Вы редко используете фабрики ... что это за дела ??

плюсы и минусы

Обновление: я применяю фабричный шаблон из Domain Driven Design в своем проекте. И одной из причин создания фабрик является снижение шума в доменной модели.

Спасибо

Ответы [ 9 ]

13 голосов
/ 29 января 2009

Я использую фабрику, если у меня есть абстрактный базовый класс (или интерфейс) и несколько конкретных производных классов, и есть некоторая логика, в соответствии с которой создается один из конкретных классов. Я реализую эту логику на заводе.

5 голосов
/ 29 января 2009

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

3 голосов
/ 29 января 2009

Что интересно в C #, относящемся к этой теме, так это то, что ограничение new () для универсальных типов, указанных в определении класса, вынуждает типы, которые обрабатываются универсальным типом контейнера, реализовывать конструктор без параметров. Ограничение new () необходимо только тогда, когда предполагается создать экземпляр типа T, как в GenericType<T>, внутри класса. Мне кажется, что это явно в поддержку фабрик классов, особенно фабрик, которые производят универсальные типы.

Чтобы перевернуть это требование, в Windows Communication Foundation (WCF) есть класс ChannelFactory, который определяет следующий статический метод фабрики:

public static TChannel CreateChannel(Binding binding, EndpointAddress endpointAddress, Uri via)
{
    ChannelFactory<TChannel> factory = new ChannelFactory<TChannel>(binding);
    if (factory.HasDuplexOperations())
    {
        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxInvalidStaticOverloadCalledForDuplexChannelFactory1", new object[] { factory.channelType.Name })));
    }
    TChannel channel = factory.CreateChannel(endpointAddress, via);
    ChannelFactory<TChannel>.SetFactoryToAutoClose(channel);
    return channel;
}

Если вы посмотрите в Reflector на дизассемблирование класса (System.ServiceModel assembly & System.ServiceModel.Channels space), вы заметите, что «new ()» не используется в качестве ограничения.

Это потому, что метод CreateChannel использует typeof (TChannel), чтобы делегировать создание объекта дальше по цепочке ...

public virtual TChannel CreateChannel(EndpointAddress address, Uri via)
{
    TChannel local;
    bool traceOpenAndClose = base.TraceOpenAndClose;
    try
    {
        using (ServiceModelActivity activity = (DiagnosticUtility.ShouldUseActivity && base.TraceOpenAndClose) ? ServiceModelActivity.CreateBoundedActivity() : null)
        {
            if (DiagnosticUtility.ShouldUseActivity)
            {
                ServiceModelActivity.Start(activity, this.OpenActivityName, this.OpenActivityType);
                base.TraceOpenAndClose = false;
            }
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("address");
            }
            if (base.HasDuplexOperations())
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxCreateNonDuplexChannel1", new object[] { base.Endpoint.Contract.Name })));
            }
            base.EnsureOpened();
            local = (TChannel) this.ServiceChannelFactory.CreateChannel(typeof(TChannel), address, via);
        }
    }
    finally
    {
        base.TraceOpenAndClose = traceOpenAndClose;
    }
    return local;
}

Вы можете проследить цепочку делегирования еще на несколько уровней по мере того, как класс Type передается до тех пор, пока, наконец, не будет вызван следующий метод:

RemotingServices.CreateTransparentProxy(this, classToProxy, stub, stubData);

Это чрезвычайно запутанно, но это самая сложная фабрика, которую я когда-либо видел. Интересно, что все махинации заканчиваются тем, что WCF создает класс RealProxy из пространства имен System.Runtime.Remoting.Proxies.

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

2 голосов
/ 29 января 2009

Я не совсем уверен, как вы выбрали свои пороги ...

Фабрики подходят, если вы не хотите абстрагировать потребителя объекта от конструкции. Случаи, когда это может быть уместно:

  • Возможно, вы захотите включить реализацию во время выполнения. Часто это сочетается с использованием интерфейсов, а не конкретных классов. Примером в Java может служить способ получения объекта Document из DocumentBuilder в Java.
  • Возможно, вы захотите ограничить количество экземпляров объекта. Подумайте о создании пула с ограниченным количеством потоковых объектов, а не просто о создании нового постоянно

Ознакомьтесь с книгой шаблонов Книги Четырех (Gamma et. Al.) И детально изучите заводской шаблон для получения дополнительной информации о том, когда использовать этот шаблон.

1 голос
/ 25 февраля 2009

Вот радикальная мысль (я на самом деле не защищаю это, но я не думаю, что это повредит):

Всегда используйте заводские методы!

Методы фабрики более гибкие, например, они могут кэшировать результаты или возвращать дочерние классы.

Итак, вместо:

class SomeClass {
  public SomeClass(/*parameters*/) { /*...*/ }
} 

Всегда используйте:

class SomeClass {
  protected SomeClass(/*parameters*/) { /*...*/ }
  public static SomeClass New(/*parameters*/) {
    return new SomeClass(/*parameters*/);
  }
} 

Код вызывающего абонента изменяется с:

SomeClass sc = new SomeClass();

Кому:

SomeClass sc = SomeClass.New();

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

1 голос
/ 29 января 2009

Мне нравится поддерживать количество конструкторов на разумно низком уровне; больше двух или трех, и я задаюсь вопросом, насколько хорошо спроектирована конструкция объекта.

В тех случаях, когда вводятся дополнительные конструкторы для поддержки установки различных необязательных атрибутов, мне нравится использовать Builder , как описано в Effective Java (Джошуа Блох, 2-е изд.)

0 голосов
/ 29 января 2009

Я думаю, что вы путаете шаблон Builder и шаблон Factory. Я бы предложил просто использовать конструкторы и покончить с этим. Похоже, (не видя код) вы слишком много думаете или переоцениваете свой код.

0 голосов
/ 29 января 2009

Используйте фабрику, когда решение о том, какой конкретный класс создавать, зависит от клиента. например где есть несколько «семейств» объектов, и выбор семейства для использования производится в другом месте.

0 голосов
/ 29 января 2009

Я пытаюсь измерить между ними. Я думаю, что вы должны использовать фабрики, когда:

  1. Большое количество парам.
  2. Необязательный параметр. (оба используют шаблон внутреннего класса Builder)
  3. Вам придется изменить порядок параметров, чтобы выполнить другое действие из-за тех же типов параметров, но разных данных.
  4. Вам нужен одноэлементный класс (лучше с enum)

На фабриках в этом случае вы можете присвоить правильное имя возвращаемому состоянию объекта.

...