Factory (объектно-ориентированное программирование)

редактировать
Заводской метод в LePUS3

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

Содержание
  • 1 Мотивация
  • 2 Терминология
  • 3 Использование
    • 3.1 Создание объекта
  • 4 Примеры
  • 5 Синтаксис
  • 6 Семантика
  • 7 Шаблоны проектирования
  • 8 Приложения
  • 9 Применимость
  • 10 Преимущества и варианты
    • 10.1 Описательные имена
    • 10.2 Инкапсуляция
      • 10.2.1 Java
      • 10.2.2 PHP
  • 11 Ограничения
  • 12 Примечания
  • 13 Ссылки
Мотивация

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

Фабрики могут быть вызваны различными способами, чаще всего вызовом метода (фабричный метод ), иногда вызываемым как функция, если фабрика является функциональным объектом (заводская функция). В некоторых языках фабрики являются обобщением конструкторов, то есть конструкторы сами по себе являются фабриками, и они вызываются таким же образом. В других языках фабрики и конструкторы вызываются по-разному, например, с использованием ключевого слова newдля вызова конструкторов, но обычного вызова метода для вызова фабрик; в этих языках фабрики - это абстракция конструкторов, но не строго обобщение, поскольку конструкторы сами по себе не являются фабриками.

Терминология

Терминология отличается в отношении того, является ли сама концепция фабрики шаблоном проектирования - в основополагающей книге Шаблоны проектирования нет «шаблона фабрики», но вместо этого два шаблона (шаблон фабричного метода и абстрактный фабричный шаблон ), которые используют фабрики. Некоторые источники называют эту концепцию фабричным шаблоном, в то время как другие считают саму концепцию идиомой программирования, зарезервировав термин «фабричный образец» или «фабричный образец» для более сложных шаблонов, которые использовать фабрики, чаще всего паттерн фабричного метода; в этом контексте саму концепцию фабрики можно назвать простой фабрикой. В других контекстах, особенно в языке Python, используется сама «фабрика», как в этой статье. В более широком смысле «фабрика» может применяться не только к объекту, который возвращает объекты из вызова некоторого метода, но и к подпрограмме , которая возвращает объекты, как в фабричной функции (даже если функции не являются объектами) или заводской метод. Поскольку во многих языках фабрики вызываются путем вызова метода, общую концепцию фабрики часто путают с конкретным шаблоном проектирования фабричного метода.

Использование

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

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

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

Например, в Python у класса collections.defaultdictесть конструктор, который создает объект типа defaultdict, значения по умолчанию которого создаются путем вызова фабрики. Фабрика передается в качестве аргумента конструктору и сама может быть конструктором или любым предметом, который ведет себя как конструктор - вызываемым объектом, который возвращает объект, то есть фабрику. Например, используя конструктор listдля списков:

# collections.defaultdict ([default_factory [,...]]) d = defaultdict (list)

Создание объекта

Фабричные объекты используются в ситуациях, когда получение объекта определенного типа является более сложным процессом, чем простое создание нового объекта, особенно если требуется сложное выделение или инициализация. Некоторые из процессов, необходимых для создания объекта, включают определение того, какой объект создать, управление временем жизни объекта и управление специализированными задачами создания и удаления объекта. Объект фабрики может решить создать объект class (если применимо) динамически, вернуть его из пула объектов , выполнить сложную настройку объекта и т. Д. Точно так же, используя это определение, синглтон , реализованный с помощью шаблона синглтона, является формальной фабрикой - он возвращает объект, но не создает новые объекты, кроме одного экземпляра.

Примеры

Простейшим примером фабрики является простая фабричная функция, которая просто вызывает конструктор и возвращает результат. В Python фабричная функция f, которая создает экземпляр класса A, может быть реализована как:

def f (): return A ()

Простая фабричная функция, реализующая шаблон singleton:

def f (): if f.obj is None: f.obj = A () return f.obj f.obj = None

Это создаст объект при первом вызове и всегда будет возвращать тот же объект после этого.

Синтаксис

Фабрики могут быть вызваны различными способами, чаще всего вызовом метода (фабричный метод), иногда вызываемым как функция, если фабрика является вызываемым объектом (фабричная функция). В некоторых языках конструкторы и фабрики имеют идентичный синтаксис, в то время как в других конструкторы имеют особый синтаксис. В языках, где конструкторы и фабрики имеют идентичный синтаксис, таких как Python, Perl, Ruby, Object Pascal и F #, конструкторы могут быть прозрачно заменены фабриками. В языках, где они различаются, их нужно различать по интерфейсам, а переключение между конструкторами и фабриками требует изменения вызовов.

Семантика

В языках, где объекты выделяются динамически, как в Java или Python, фабрики семантически эквивалентны конструкторам. Однако в таких языках, как C ++, которые позволяют статически выделять некоторые объекты, фабрики отличаются от конструкторов для статически распределенных классов, поскольку у последних может быть выделение памяти, определяемое во время компиляции, в то время как распределение возвращаемых значений фабрик должно определяться в время выполнения. Если конструктор может быть передан в качестве аргумента функции, то вызов конструктора и выделение возвращаемого значения должны выполняться динамически во время выполнения и, таким образом, иметь аналогичную или идентичную семантику для вызова фабрики.

Шаблоны проектирования

Фабрики используются в различных шаблонах проектирования, в частности в шаблонах создания, таких как. Были разработаны специальные рецепты для их реализации на многих языках. Например, несколько «шаблонов GoF », таких как «шаблон фабричного метода », «Builder » или даже «Singleton "являются реализациями этой концепции. Вместо этого «Абстрактный шаблон фабрики » представляет собой метод построения коллекций фабрик.

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

Приложения

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

Фабрики определяют фактический конкретный тип объекта, который будет создан, и здесь фактически создается объект. Поскольку фабрика возвращает объекту только абстрактный интерфейс, клиентский код не знает - и не обременен - ​​фактическим конкретным типом только что созданного объекта. Однако абстрактной фабрике известен тип конкретного объекта. В частности, это означает:

  • Клиентский код не имеет никаких сведений о конкретном типе, и ему не нужно включать какие-либо файлы заголовков или класс <90.>объявления, относящиеся к конкретному типу. Клиентский код имеет дело только с абстрактным типом. Объекты конкретного типа действительно создаются фабрикой, но клиентский код получает доступ к таким объектам только через их абстрактный интерфейс.
  • Добавление новых конкретных типов осуществляется путем модификации клиентского кода для использования другой фабрики, модификация, которая обычно представляет собой одну строку в одном файле. Это значительно проще, чем изменение клиентского кода для создания экземпляра нового типа, что потребовало бы изменения каждого места в коде, где создается новый объект.
Применимость

Фабрики можно использовать, когда:

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

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

Параллельные иерархии классов часто требуют, чтобы объекты из одной иерархии могли создавать соответствующие объекты из другой.

Заводские методы используются в разработке, управляемой тестированием, чтобы разрешить тестирование классов. Если такой класс Fooсоздает другой объект Dangerous, который не может быть помещен в автоматизированные модульные тесты (возможно, он взаимодействует с производственной базой данных, которая не всегда available), то создание Dangerousобъектов помещается в виртуальный фабричный метод createDangerousв классе Foo. Затем для тестирования создается TestFoo(подкласс Foo) с переопределением метода виртуальной фабрики createDangerousдля создания и возврата FakeDangerous, поддельный объект. Затем модульные тесты используют TestFooдля проверки функциональности Foo, не вызывая побочных эффектов использования реального объекта Dangerous.

Преимущества и варианты

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

Описательные имена

Заводской метод имеет собственное имя. Во многих объектно-ориентированных языках конструкторы должны иметь то же имя, что и класс, в котором они находятся, что может привести к неоднозначности, если существует более одного способа создания объекта (см. перегрузка ). Заводские методы не имеют такого ограничения и могут иметь описательные имена; их иногда называют альтернативными конструкторами . Например, когда комплексные числа создаются из двух действительных чисел, действительные числа можно интерпретировать как декартовы или полярные координаты, но с использованием фабричных методов смысл ясен, как показано в следующем примере на C #.

сложный общественный класс {общественный двойной реальный; общественное двойное воображаемое; общедоступный статический комплекс FromCartesian (двойное действительное, двойное воображаемое) {return new Complex (реальный, мнимый); } общедоступный статический комплекс FromPolar (двойной модуль, двойной угол) {вернуть новый комплекс (модуль * Math.Cos (угол), модуль * Math.Sin (угол)); } частный комплекс (двойное действительное, двойное воображаемое) {this.real = real; this.imaginary = воображаемый; }} Сложный продукт = Complex.FromPolar (1, Math.PI);

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

Инкапсуляция

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

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

Каждый раз, когда программа считывает изображение, ей необходимо создать считыватель соответствующего типа на основе некоторой информации в файле. Эта логика может быть инкапсулирована в фабричный метод. Этот подход также называют простой фабрикой.

Java

открытый класс ImageReaderFactory {общедоступный статический ImageReader createImageReader (ImageInputStreamProcessor iisp) {if (iisp.isGIF ()) {вернуть новый GifReader (iisp.getInputStream ()); } else if (iisp.isJPEG ()) {вернуть новый JpegReader (iisp.getInputStream ()); } else {throw new IllegalArgumentException ("Неизвестный тип изображения."); }}}

PHP

class Factory {сборка общедоступной статической функции ($ type) {$ class = "Format". $ type; if (! class_exists ($ class)) {throw new Exception ("Отсутствует класс формата."); } вернуть новый $ class; }} интерфейс FormatInterface {} класс FormatString реализует FormatInterface {} класс FormatNumber реализует FormatInterface {} try {$ string = Factory :: build ("String"); } catch (исключение $ e) {echo $ e->getMessage (); } попробуйте {$ number = Factory :: build ("Number"); } catch (исключение $ e) {echo $ e->getMessage (); }
Ограничения

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

  • Первое ограничение заключается в том, что рефакторинг существующего класса для использования фабрик нарушает работу существующих клиентов. Например, если класс Complex был стандартным классом, у него могло бы быть множество клиентов с таким кодом, как:
    Complex c = new Complex (-1, 0);
Как только мы понимаем, что необходимы две разные фабрики, мы меняем класс (на код, показанный ранее ). Но поскольку конструктор теперь является частным, существующий клиентский код больше не компилируется.
  • Второе ограничение состоит в том, что, поскольку шаблон полагается на использование частного конструктора, класс не может быть расширен. Любой подкласс должен вызывать унаследованный конструктор, но этого нельзя сделать, если этот конструктор является закрытым.
  • Третье ограничение состоит в том, что если класс должен быть расширен (например, сделав конструктор защищенным - это рискованно но выполнимо), подкласс должен обеспечивать собственную повторную реализацию всех фабричных методов с точно такими же сигнатурами. Например, если класс StrangeComplexрасширяет Complex, то, если StrangeComplexне предоставляет свою собственную версию всех фабричных методов, вызов
    StrangeComplex.FromPolar (1, Math.Pi);
    даст экземпляр Complex(суперкласс), а не ожидаемый экземпляр подкласса. Функции отражения некоторых языков позволяют избежать этой проблемы.

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

Примечания
Ссылки
Последняя правка сделана 2021-05-20 08:53:11
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте