Чем отличается фабричный метод от абстрактной фабрики
Перейти к содержимому

Чем отличается фабричный метод от абстрактной фабрики

  • автор:

Сравнение фабрик

В этой статье мы попробуем разобраться чем отличаются:

  1. Фабрика
  2. Создающий метод
  3. Статический фабричный метод
  4. Простая фабрика
  5. Фабричный метод
  6. Абстрактная фабрика

Во многих книгах и источниках определения «фабрик» даётся авторами по-своему. Это создаёт большую путаницу при чтении материалов в интернете.

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

1. Фабрика

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

Вы можете услышать слово Фабрика от других людей, когда они имеют в виду:

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

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

2. Создающий метод

Создающий метод Определён в книге Refactoring To Patterns. — это простой метод-обёртка над вызовом конструктора продукта. Выделив создающий метод, вы изолируете любые изменения в конструировании продуктов от основного кода. Например, вы можете вовсе убрать вызов конструктора из создающего метода, отдавая вместо нового какой-то существующий объект.

Многие называют его фабричным методом, только потому, что он создаёт новые объекты. Типичная логика: «этот метод создаёт объекты, а раз все фабрики создают что-то, значит этот метод — фабричный». И это вносит основную путаницу между понятием Создающего метода и паттерном Фабричный метод.

В этом примере, метод next является создающим методом:

class Number < private $value; public function __construct($value) < $this->value = $value; > public function next() < return new Number ($this->value + 1); > >

3. Статический фабричный метод

Статический фабричный метод — вариация создающего метода, объявленная как static . Если этот метод создаёт объекты своего же класса, то, по сути, он выступает в роли альтернативного конструктора. Это может быть полезно, если:

  • Требуется создать разные по функциональности конструкторы, у которых бы совпадали сигнатуры (например, Random(int max) и Random(int min) ). Это невозможно во многих языках программирования, но создав статический метод, вы можете обойти это ограничение.
  • Хочется повторно использовать готовые объекты, вместо создания новых (например, паттерн Одиночка). При вызове конструктора вы всегда создаёте новый объект. Это можно обойти, если вынести вызов конструктора в новый метод. В этом методе вы можете сначала поискать готовый объект в каком-то кеше, и только если его нет, создать новый объект.

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

class User < private $id, $name, $email, $phone; public function __construct($id, $name, $email, $phone) < $this->id = $id; $this->name = $name; $this->email = $email; $this->phone = $phone; > public static function load($id) < list($id, $name, $email, $phone) = DB::load_data('users', 'id', 'name', 'email', 'phone'); $user = new User($id, $name, $email, $phone); return $user; >>

4. Паттерн Простая фабрика

Паттерн Простая фабрика Определён в книге Head First Design Patterns. — это класс, в котором есть один метод с большим условным оператором, выбирающим создаваемый продукт. Этот метод вызывают с неким параметром, по которому определяется какой из продуктов нужно создать. У простой фабрики, обычно, нет подклассов.

Обычно, простую фабрику путают с общим понятием Фабрики или с любым из фабричных паттернов.

Если объявить класс простой фабрики абстрактным (Java, C#), это не сделает его одним и тем же, что и абстрактная фабрика!

Вот пример простой фабрики:

class UserFactory < public static function create($type) < switch ($type) < case 'user': return new User(); case 'customer': return new Customer(); case 'admin': return new Admin(); default: throw new Exception('Wrong user type passed.'); >> >

Простая фабрика находится в шаге от того, чтобы стать Фабричным методом.

5. Паттерн Фабричный метод

Паттерн Фабричный метод Определён в книге «банды четырёх» Design Patterns: Elements of Reusable Object-Oriented Software. — это устройство классов, при котором подклассы могут переопределять тип создаваемого в суперклассе продукта.

Если вы имеете иерархию продуктов и абстрактный создающий метод, который переопределяется в подклассах, то перед вами паттерн Фабричный метод.

abstract class Department < public abstract function createEmployee($id); public function fire($id) < $employee = $this->createEmployee($id); $employee->paySalary(); $employee->dismiss(); > > class ITDepartment extends Department < public function createEmployee($id) < return new Programmer($id); >> class AccountingDepartment extends Department < public function createEmployee($id) < return new Accountant($id); >>

6. Паттерн Абстрактная фабрика

Паттерн Абстрактная фабрика Определён в книге «банды четырёх» Design Patterns: Elements of Reusable Object-Oriented Software. — это устройство классов, облегчающее создание семейств продуктов.

Что такое семейство продуктов? Например, классы Транспорт + Двигатель + Управление . Вариациями этого семейства могут стать:

  1. Автомобиль + ДвигательВнутренннегоСгорания + Руль
  2. Самолет + РеактивныйДвигатель + Штурвал

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

Многие путают паттерн абстрактная фабрика с классом простой фабрики, объявленным как abstract , но это далеко не одно и то же!

Послесловие

Теперь, когда вы окончательно разобрались в терминологии, почитайте наши описания фабричных паттернов:

Refactoring.Guru

  • Премиум контент
    • Книга о паттернах
    • Курс по рефакторингу
    • Введение в рефакторинг
      • Чистый код
      • Технический долг
      • Когда рефакторить
      • Как рефакторить
      • Раздувальщики
        • Длинный метод
        • Большой класс
        • Одержимость элементарными типами
        • Длинный список параметров
        • Группы данных
        • Операторы switch
        • Временное поле
        • Отказ от наследства
        • Альтернативные классы с разными интерфейсами
        • Расходящиеся модификации
        • Стрельба дробью
        • Параллельные иерархии наследования
        • Комментарии
        • Дублирование кода
        • Ленивый класс
        • Класс данных
        • Мёртвый код
        • Теоретическая общность
        • Завистливые функции
        • Неуместная близость
        • Цепочка вызовов
        • Посредник
        • Неполнота библиотечного класса
        • Составление методов
          • Извлечение метода
          • Встраивание метода
          • Извлечение переменной
          • Встраивание переменной
          • Замена переменной вызовом метода
          • Расщепление переменной
          • Удаление присваиваний параметрам
          • Замена метода объектом методов
          • Замена алгоритма
          • Перемещение метода
          • Перемещение поля
          • Извлечение класса
          • Встраивание класса
          • Сокрытие делегирования
          • Удаление посредника
          • Введение внешнего метода
          • Введение локального расширения
          • Самоинкапсуляция поля
          • Замена простого поля объектом
          • Замена значения ссылкой
          • Замена ссылки значением
          • Замена поля-массива объектом
          • Дублирование видимых данных
          • Замена однонаправленной связи двунаправленной
          • Замена двунаправленной связи однонаправленной
          • Замена магического числа символьной константой
          • Инкапсуляция поля
          • Инкапсуляция коллекции
          • Замена кодирования типа классом
          • Замена кодирования типа подклассами
          • Замена кодирования типа состоянием/стратегией
          • Замена подкласса полями
          • Разбиение условного оператора
          • Объединение условных операторов
          • Объединение дублирующихся фрагментов в условных операторах
          • Удаление управляющего флага
          • Замена вложенных условных операторов граничным оператором
          • Замена условного оператора полиморфизмом
          • Введение Null-объекта
          • Введение проверки утверждения
          • Переименование метода
          • Добавление параметра
          • Удаление параметра
          • Разделение запроса и модификатора
          • Параметризация метода
          • Замена параметра набором специализированных методов
          • Передача всего объекта
          • Замена параметра вызовом метода
          • Замена параметров объектом
          • Удаление сеттера
          • Сокрытие метода
          • Замена конструктора фабричным методом
          • Замена кода ошибки исключением
          • Замена исключения проверкой условия
          • Подъём поля
          • Подъём метода
          • Подъём тела конструктора
          • Спуск метода
          • Спуск поля
          • Извлечение подкласса
          • Извлечение суперкласса
          • Извлечение интерфейса
          • Свёртывание иерархии
          • Создание шаблонного метода
          • Замена наследования делегированием
          • Замена делегирования наследованием
          • Введение в паттерны
            • Что такое Паттерн?
            • История паттернов
            • Зачем знать паттерны?
            • Критика паттернов
            • Классификация паттернов
            • Фабричный метод
            • Абстрактная фабрика
            • Строитель
            • Прототип
            • Одиночка
            • Адаптер
            • Мост
            • Компоновщик
            • Декоратор
            • Фасад
            • Легковес
            • Заместитель
            • Цепочка обязанностей
            • Команда
            • Итератор
            • Посредник
            • Снимок
            • Наблюдатель
            • Состояние
            • Стратегия
            • Шаблонный метод
            • Посетитель
            • C#
            • C++
            • Go
            • Java
            • PHP
            • Python
            • Ruby
            • Rust
            • Swift
            • TypeScript

            Фабричный метод против абстрактной фабрики (снова?)

            Недавно один из моих друзей спросил меня, в чем разница между фабричным методом и абстрактным фабричным дизайном, но, похоже, я не убедил его легко. Конечно, я уже читал Head First, GoF, Pattern Hatching, Refactoring to Patterns, но не все из них, и это было не совсем недавно. Поэтому я решил изучить больше и попытаться просто различия, и вот моя попытка.

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

            Фабричный метод : Определите интерфейс для создания объекта, но пусть подклассы решают, какой класс создать. Factory Method позволяет классу откладывать создание экземпляров для подклассов.

            Абстрактная фабрика : предоставляет интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.

            AF_DP

            Различия

            Один продукт против множества

            Picture1

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

            Наследование против композиции

            Picture2

            Это, пожалуй, самый запутанный (поскольку оба, кажется, используют наследование). Фабричный метод зависит от наследования, чтобы решить, какой продукт будет создан. В классе Creator (на структурной диаграмме) у него есть и другие методы (реализованные для управления продуктом), которые используют единственный абстрактный метод FactoryMethod () для создания продукта и могут быть реализованы / изменены только подклассами .

            Здесь класс Creator также действует как клиент, который зависит только от одного метода для создания продукта. Мы должны создать подкласс всего класса Creator / Client, чтобы создать новый продукт. Нет отдельного и выделенного класса для создания Продукта, если бы мы могли использовать его с композицией, где мы можем передать объект фабрики клиенту, а клиент может использовать его, не попадая в иерархию наследования.

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

            Если мы думаем только о средствах создания продукта и клиенте, который его использует, то ясно, что в Factory Method мы ограничены в использовании наследования (на основе классов), а в Abstract Factory у нас есть гибкость композиции (на основе объектов) для создания конкретные продукты.

            //Factory Method class Client  protected Product factoryMethod() > class NewClient extends Client  > //Abstract Factory class Client  public void anOperation()  > interface Factory  //concrete factories also, implementing Factory interface 

            Метод против полного класса (объекта)

            Picture3

            Фабричный метод — это просто метод, а абстрактный Фабрика — это объект . Цель методы фабрики класса , имеющее это не только создавать объекты, это делает другую работу также, только метод отвечает за создание объекта. В Абстрактной Фабрике вся цель класса — создать семейство объектов .

            Уровень абстракции

            уровня из-абстракции-640x390

            Абстрактная Фабрика на один уровень выше в абстракции, чем Фабричный Метод. Фабричный метод абстрагирует способ создания объектов , в то время как Abstract Factory также абстрагирует способ создания фабрик, который в свою очередь абстрагирует способ создания объектов.

            Один внутри другого

            Picture4

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

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

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

            Счастливые шаблоны, пока.

            «Фабричный метод» и «Абстрактная фабрика» во вселенной «Swift» и «iOS»

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

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

            А на написание этого очерка меня вдохновил Джошуа Керивски (глава «Industrial Logic»), а точнее, его книга «Refactoring to Patterns», которая вышла в начале века в рамках серии книг, основанной Мартином Фаулером (именитым автором современной классики программирования – книги «Рефакторинг»). Если кто-то не читал или даже не слышал о первой (а я знаю таких много), то обязательно добавьте ее себе в список для чтения. Это достойный «сиквел» как «Рефакторинга», так и еще более классической книги – «Приемов объектно-ориентированного проектирования. Паттерны проектирования».

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

            Абстрактная фабрика

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

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

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

            Чтобы быть ближе к теме разработки под «iOS», удобно упражняться на подклассах UIViewController . И действительно, это точно один из самых распространенных типов в «iOS»-разработке, почти всегда «наследуется» перед применением, а конкретный подкласс при этом зачастую даже и не важен для клиентского кода.

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

            Подробный пример

            Предположим, мы в приложении торгуем средствами передвижения, и от типа конкретного средства зависит отображение: мы будем использовать разные подклассы UIViewController для разных средств передвижения. Помимо этого, все средства передвижения различаются состоянием (новые и б/у):

            enum VehicleCondition < case new case used >final class BicycleViewController: UIViewController < private let condition: VehicleCondition init(condition: VehicleCondition) < self.condition = condition super.init(nibName: nil, bundle: nil) >required init?(coder aDecoder: NSCoder) < fatalError("BicycleViewController: init(coder:) has not been implemented.") >> final class ScooterViewController: UIViewController < private let condition: VehicleCondition init(condition: VehicleCondition) < self.condition = condition super.init(nibName: nil, bundle: nil) >required init?(coder aDecoder: NSCoder) < fatalError("ScooterViewController: init(coder:) has not been implemented.") >>

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

            Конечно, пример достаточно простой, и в реальном проекте в похожем случае вводить «фабрику» будет явным «overengineering». Тем не менее, если представить, что типов транспортных средств у нас не два, а параметров у конструкторов – не один, то преимущества «фабрики» станут более очевидными.

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

            protocol VehicleViewControllerFactory < func makeBicycleViewController() ->UIViewController func makeScooterViewController() -> UIViewController >

            (Довольно краткий «гайдлайн» по проектированию «API» на языке «Swift» рекомендует называть «фабричные» методы начиная со слова «make».)

            (Пример в книге банды четырех приведен на «C++» и основывается на наследовании и «виртуальных» функциях. Используя «Swift» нам, конечно, ближе парадигма протокольно-ориентированного программирования.)

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

            В качестве «конкретных фабрик» будем использовать две реализации интерфейса абстрактной фабрики:

            struct NewVehicleViewControllerFactory: VehicleViewControllerFactory < func makeBicycleViewController() ->UIViewController < return BicycleViewController(condition: .new) >func makeScooterViewController() -> UIViewController < return ScooterViewController(condition: .new) >> struct UsedVehicleViewControllerFactory: VehicleViewControllerFactory < func makeBicycleViewController() ->UIViewController < return BicycleViewController(condition: .used) >func makeScooterViewController() -> UIViewController < return ScooterViewController(condition: .used) >>

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

            Создание нужного контроллера отныне будет выглядеть примерно так:

            let factory: VehicleViewControllerFactory = NewVehicleViewControllerFactory() let vc = factory.makeBicycleViewController()

            Инкапусляция классов с помощью фабрики

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

            Первый «кейс» связан с инкапсуляцией конкретных классов. Для примера возьмем те же контроллеры для отображения данных о транспортных средствах:

            final class BicycleViewController: UIViewController < >final class ScooterViewController: UIViewController

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

            public struct VehicleViewControllerFactory < func makeBicycleViewController() ->UIViewController < return BicycleViewController() >func makeScooterViewController() -> UIViewController < return ScooterViewController() >>

            Перемещение знаний о создании объекта внутрь фабрики

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

            Предположим, мы захотели продавать заодно уж и автомобили. А это, несомненно, более сложная техника, обладающая бóльшим числом характеристик. Для примера ограничимся типом используемого топлива, типом трансмиссии и размером колесного диска:

            enum Condition < case new case used >enum EngineType < case diesel case gas >struct Engine < let type: EngineType >enum TransmissionType < case automatic case manual >final class CarViewController: UIViewController < private let condition: Condition private let engine: Engine private let transmission: TransmissionType private let wheelDiameter: Int init(engine: Engine, transmission: TransmissionType, wheelDiameter: Int = 16, condition: Condition = .new) < self.engine = engine self.transmission = transmission self.wheelDiameter = wheelDiameter self.condition = condition super.init(nibName: nil, bundle: nil) >required init?(coder aDecoder: NSCoder) < fatalError("CarViewController: init(coder:) has not been implemented.") >>

            Пример инициализации соответствующего контроллера:

            let engineType = EngineType.diesel let engine = Engine(type: engineType) let transmission = TransmissionType.automatic let wheelDiameter = 18 let vc = CarViewController(engine: engine, transmission: transmission, wheelDiameter: wheelDiameter)

            Мы можем ответственность за все эти «мелочи» водрузить на «плечи» специализированной фабрики:

            struct UsedCarViewControllerFactory < let engineType: EngineType let transmissionType: TransmissionType let wheelDiameter: Int func makeCarViewController() ->UIViewController < let engine = Engine(type: engineType) return CarViewController(engine: engine, transmission: transmissionType, wheelDiameter: wheelDiameter, condition: .used) >>

            И создавать контроллер уже таким образом:

            let factory = UsedCarViewControllerFactory(engineType: .gas, transmissionType: .manual, wheelDiameter: 17) let vc = factory.makeCarViewController()

            Фабричный метод

            Второй «однокоренной» шаблон также инкапсулирует знания о конкретных порождаемых типах, но не за счет сокрытия этих знаний внутри специализированного класса, а за счет полиморфизма. Керивски в своей книге приводит примеры на «Java» и предлагает пользоваться абстрактными классами, но обитатели вселенной «Swift» с таким понятием не знакомы. У нас тут своя атмосфера… и протоколы.

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

            Полиморфное создание объектов

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

            Для примера вернемся к нашим контроллерам для отображения транспортных средств:

            final class BicycleViewController: UIViewController < >final class ScooterViewController: UIViewController

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

            protocol Coordinator < var presentingViewController: UIViewController? < get set >func start() >

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

            final class BicycleCoordinator: Coordinator < weak var presentingViewController: UIViewController? func start() < let vc = BicycleViewController() presentingViewController?.present(vc, animated: true) >> final class ScooterCoordinator: Coordinator < weak var presentingViewController: UIViewController? func start() < let vc = ScooterViewController() presentingViewController?.present(vc, animated: true) >>

            Предлагаемое решение – это вынести создание используемого объекта в отдельный метод:

            protocol Coordinator < var presentingViewController: UIViewController? < get set >func start() func makeViewController() -> UIViewController >

            А основной метод – снабдить базовой реализацией:

            extension Coordinator < func start() < let vc = makeViewController() presentingViewController?.present(vc, animated: true) >>

            Конкретные типы в таком случае примут вид:

            final class BicycleCoordinator: Coordinator < weak var presentingViewController: UIViewController? func makeViewController() ->UIViewController < return BicycleViewController() >> final class ScooterCoordinator: Coordinator < weak var presentingViewController: UIViewController? func makeViewController() ->UIViewController < return ScooterViewController() >>

            Заключение

            Я попытался данную несложную тему осветить, совместив три подхода:

            • классическая декларация существования приема, навеянная книгой «Банды четырех»;
            • мотивация использования, неприкрыто вдохновленная книгой Керивски;
            • прикладное применение на примере близкой мне отрасли программирования.

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

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

            Другие мои материалы на тему шаблонов проектирования:

            • «Архитектурный шаблон «Посетитель» (“Visitor”) во вселенной «iOS» и «Swift»
            • «Архитектурный шаблон «Итератор» («Iterator») во вселенной «Swift»
            • «Архитектурный шаблон «Строитель» во вселенной «Swift» и «iOS»/«macOS»
            • design patterns
            • программирование
            • swift
            • swift разработка
            • swift development
            • ios development
            • ios разработка
            • ios programming
            • Программирование
            • Совершенный код
            • Разработка под iOS
            • ООП
            • Swift

            В чем разница между ‘фабричным методом’ и ‘простой фабрикой’?

            anton_reut

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

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

            Паттерны — это же не о классах, а о взаимодействии между ними и уменьшении связности.

            Ответ написан более трёх лет назад
            Комментировать
            Нравится 5 Комментировать

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

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

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

            Например, мы открыли в Москве и Питере магазин по продаже телефонов и в классе BeelineStore есть общие методы для всех телефонов (код на java схематичный). Samsung стал поддерживать 5G интернет, поэтому сделаем тариф для этих телефонов дороже.

            spoiler

            abstract class IMobile < void prepare() < System.out.println("Подготовим документы по продаже от ООО Билайн"); >void setInternetTariff() < System.out.println("Установим стандартный тариф Билайна для интернета"); >void box() < System.out.println("Упакуем в желтый пакетик"); >> class BeelineSamsung extends IMobile < void setInternetTariff() < System.out.println("Установим отдельный тариф для 5G интернета"); >> class BeelineIPhone extends IMobile <> class BeelineStore < public IMobile orderPhone(String type) < IMobile phone = null; if (type.equals("samsung")) < phone = new BeelineSamsung(); >else if (type.equals("iphone")) < phone = new BeelineIPhone(); >phone.prepare(); phone.setInternetTariff(); phone.box(); return phone; > > // использование // var mobileStore = new BeelineStore(); // mobileStore.orderPhone("samsung"); 

            Однако при изменении, удалении или добавлении новых видов телефонов мы бы открывали наш класс BeelineStore и расширяли новыми конкретными типами. А если мы добавим модели телефонов, то дерево условий станет сложнее. Если одни аспекты системы изменяются, а другие остаются неизменными — пора заняться инкапсуляцией.

            spoiler

            class MobileFactory < public IMobile getMobile(String type) < if (type.equals("samsung")) < return new BeelineSamsung(); >else if (type.equals("iphone")) < return new BeelineIPhone(); >return null; > > class BeelineStore < MobileFactory factory; public BeelineStore(MobileFactory factory) < this.factory = factory; >public IMobile orderPhone(String type) < IMobile phone; phone = factory.getMobile(type); phone.prepare(); phone.setInternetTariff(); phone.box(); return phone; >> // использование // var mobileFactory = new MobileFactory(); // mobileStore = new BeelineStore(mobileFactory); // mobileStore.orderPhone("samsung"); 

            Окей - мы вынесли процесс создания телефонов - это и есть фабрика. И тут мы расширяемся, дела в нашей компании пошли хорошо и мы открываем филиалы на Урале. Тарифы на Урале будут дешевле, как стандартные, так и для 5G интернета. А также в Москве и Питере запускается программа по обмену старых телефонов на новые. И тут нам помогает фабричный метод.

            spoiler

            abstract class BeelineStore < // класс становится абстрактным protected abstract IMobile getMobile(String type); // возвращается метод getMobile public IMobile orderPhone(String type) < IMobile phone = getMobile(type); phone.prepare(); phone.setInternetTariff(); phone.box(); return phone; >> class CenterBeelineSamsung extends IMobile < void setInternetTariff() < System.out.println("Отдельный тариф для 5G интернета в центре России"); >> class CenterBeelineIPhone extends IMobile <> class СenterBeelineStore extends BeelineStore < public IMobile getMobile(String type) < if (type.equals("samsung")) < return new CenterBeelineSamsung(); >else if (type.equals("iphone")) < return new CenterBeelineIPhone(); >return null; > public void tradeIn(String phone) < System.out.println("Вы обменяли старый телефон на новый с доплатой"); >> class UralBeelineSamsung extends IMobile < void setInternetTariff() < System.out.println("Отдельный тариф для 5G интернета на Урале"); >> class UralBeelineIPhone extends IMobile < void setInternetTariff() < System.out.println("Отдельный тариф для стандартного интернета на Урале"); >> class UralBeelineStore extends BeelineStore < public IMobile getMobile(String type) < if (type.equals("samsung")) < return new UralBeelineSamsung(); >else if (type.equals("iphone")) < return new UralBeelineIPhone(); >return null; > > // использование // var centerBeelineStore = new СenterBeelineStore(); // centerBeelineStore.orderPhone("samsung"); // var uralBeelineStore = new UralBeelineStore(); // uralBeelineStore.orderPhone("samsung");

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *