Фабрика против конструкторов экземпляров - PullRequest
37 голосов
/ 02 июня 2010

Я не могу придумать никаких причин, почему одно лучше другого. Сравните эти две реализации:

public class MyClass
{
    public MyClass(string fileName)
    {
        // some code...
    }
}

вместо:

public class MyClass
{
    private MyClass(){}

    public static MyClass Create(string fileName)
    {
       // some code...
    }
}

В .Net Framework есть места, в которых для создания экземпляров используется статический метод. Сначала я думал, что регистрирует свои экземпляры, чтобы отслеживать их, но обычные конструкторы могут делать то же самое с помощью частных статических переменных.

В чем причина этого стиля?

Ответы [ 10 ]

49 голосов
/ 02 июня 2010

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

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

Например:

public abstract class BaseClass
{
    public static BaseClass Create(int parameter)
    {
        if (parameter == 1)
        {
            return new Class1();
        }
        else
        {
            return new Class2();
        }
    }
}

internal class Class1 : BaseClass
{
    //code here ...
}

internal class Class2 : BaseClass
{
    //code here ...
}

Это позволяет скрыть Class1 и Class2 от внешних сборок, в то же время позволяя потребителю иметь дело с чем-то специализированным.

19 голосов
/ 18 апреля 2012

Я исследовал этот самый момент и наткнулся на этот вопрос, но не чувствовал, что на него ответили полностью. Тем не менее, я нашел эту удобную статью - Обновление рекомендаций по проектированию: фабрики против конструкторов - Кшиштофа Квалина (главный архитектор в .NET Framework). Стоит прочитать всю статью, но вот краткое изложение основных моментов:

Наиболее распространенным и последовательным способом создания экземпляра типа является через его конструктор. Однако иногда предпочтительной альтернативой является используйте шаблон Factory.

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

Реализуйте фабричные операции как методы, а не свойства.

Возвращать экземпляры как возвращаемые значения метода, а не как параметры out.

Рассмотрите возможность использования Фабрики, если вам нужен больший контроль над созданием образцы экземпляров.

Подумайте о присвоении имен методам Factory, объединив «Create» и имя создаваемого типа.

Подумайте о присвоении имен фабричным типам, объединив имя типа создал и «Фабрику».

Не реализуйте свою Фабрику статическим методом, если строительная операция должна быть доступна для подклассов, чтобы специализироваться.

Использовать фабрику для операций в стиле преобразования.

Использовать Factory, если для операции требуется информация о параметрах, которая неестественно переходить к конструктору.

Не используйте Фабрику в ситуациях, когда операция создания будет использоваться в качестве основного сценария для создания экземпляра типа. В этих ситуации, обнаруживаемость и последовательность имеют первостепенное значение.

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

Фабрики также часто удобны для включения специализации типов через полиморфизм.

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

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

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

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

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

10 голосов
/ 01 июня 2013

Другой распространенный случай, когда помогают фабричные методы (в таких языках, как C # , которые не поддерживают вывод типов из конструктора ), - это когда нужно минимизировать спецификацию параметров типа. Рассмотрим общий случай класса Tuple. Вы можете сделать:

new Tuple<int, int>(1, 1); //constructor call

или

Tuple.Create(1, 1); //factory pattern.
7 голосов
/ 02 июня 2010

Статический метод Create может создавать и возвращать:

  • A MyClass экземпляр
  • Экземпляр любого подкласса из MyClass
  • null
3 голосов
/ 02 июня 2010

Заводской метод часто используется, чтобы скрыть точный тип создаваемого объекта. Абстрактный класс может иметь статический метод фабрики, который затем выбирает конкретный производный класс для создания экземпляра в зависимости от некоторого условия. Только абстрактный базовый класс является публично задокументированным. Это оставляет поставщику библиотеки некоторую свободу изменять конкретный тип создаваемого экземпляра, не нарушая существующий код.

Примером платформы .NET являются статические фабричные методы класса Expression.

1 голос
/ 20 марта 2015

Другая причина заключается в отсутствии зависимости от сборок со ссылкой на вашу сборку.

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

Пример:

Assembly1.dll (ссылка на BarAssembly.dll не так очевидна)

class Class1 { 
    void Main(){
        var foo = new Foo();
    }
}

Assembly2.dll (здесь нет необходимости ссылаться на BarAssembly.dll, поскольку CreateFoo и CreateFooWithBar не перегружены)

class Class2 { 
    void Main(){
        var foo = CreateFoo();
    }
}

FooAssembly.dll (требуется ссылка на BarAssembly.dll, очевидно)

class Foo { 
    public CreateFoo(){
        ...
    }
    public CreateFooWithBar(Bar bar){
        ...
    }
    public Foo(){
        ...
    }
    public Foo(Bar bar){
        ...
    }
}

BarAssembly.dll

class Bar { 
    public Bar(){
        ...
    }
}

Примечание. Наблюдается в сборке VS2013 для .NET Framework 4.5

.
1 голос
/ 31 октября 2013

Методы, которые делают строительство для вас, очень полезны для:

public class Class
    {
        public static List<Class> FromFiles(IEnumerable<String> paths)
        {
            List<Class> instances = new List<Class>();
            foreach (string path in paths)
            {
                instances.Add(new Class() { Path = path });
            }
            return instances;
        }

        public string Path { get; set; }
    }

... так как "нормальные" конструкторы не могут этого сделать.

1 голос
/ 02 июня 2010

Как вы можете видеть, myClass не следует "классическому" фабричному шаблону (где класс экземпляра неизвестен / выставлен за пределами фабрики).

Однако в этом случае команда .NET Framework может не стремиться к фабричному шаблону, но я думаю, что они не хотят, чтобы вы просто добавляли новый целевой класс напрямую с именем файла через конструктор. Фабрика может быть излишней, если предоставляется только этот класс.

Этот шаблон иногда можно увидеть в методах clone (), где объект может возвращать экземпляр, который является своего рода дубликатом самого себя.

Также, возможно, хотя класс является общедоступным, они могут захотеть выполнить некоторые проверки с использованием экземпляра, имени файла и т. Д., И если они реализуют фабрику, целевой класс все еще можно создать и вызвать, минуя проверки.

1 голос
/ 02 июня 2010

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

0 голосов
/ 05 августа 2010

Можно карри заводскими методами. Попытка карри конструктора дает вам: фабричный метод.

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

...