Паттерн проектирования Factory - Часть 1

FacebooktwittertumblrFacebooktwittertumblr

Данная статья:

  • написана командой Vertex Academy. Надеемся, что она Вам будет полезна. Приятного прочтения!
  • это одна из статей из нашего "Самоучителя по Java"
  • данная статья рассчитана на тех, кто хорошо знает ООП.

Привет! Это статья про распространенный паттерн программирования - Factory.

Зачем нужен паттерн Factory (Фабрика)?

Давайте представим, что нам нужен класс, который создает объекты разного типа. Иногда это очень удобно. Почему?

Во-первых, представим, что нам нужна "фабрика", которая выпускает разные пончики:

  • пончик с вишней
  • пончик с белым шоколадом
  • пончик с миндалем

Соответственно, под каждый пончик должен быть свой класс.

Однако все эти классы очень похожи. Все пончики будут иметь одинаковый набор параметров:

  • вес
  • калорийность
  • дату изготовления
  • начинку
  • и т.д.

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

То есть мы сможем просто сказать:

  • "Создай пончик с вишней"
  • "Создай пончик с белым шоколадом"
  • "Создай пончик с миндалем"

Для этого нам необходимо использовать паттерн Factory. Ну, что ж, давайте попробуем создать нашу первую "фабрику" (Factory). Давайте назовем ее "Фабрика пончиков" (DoughnutFactory):

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

Отлично! Теперь давайте создадим три класса пончиков, которые имплементиуют интерфейс Doughnut.

Вишневый:

Шоколадный:

И миндальный:

Отлично - классы есть, интерфейс есть. Давайте производить!

Итак, суть фабрики сводится к тому, чтобы производить объекты разного типа. Но как сделать так, чтоб Фабрика понимала какой именно тип объекта(пончика) мы хотим получить: с вишней, с белым шоколадом или с миндалем?

Для этого давайте создадим ENUM, в котором запишем все возможные типы пончиков:

Конечно, мы могли бы использовать вместо ENUM строку String с названием, но тогда ошибок гораздо сложнее избежать. Если исключить опечатки, то пришлось бы запоминать, в каком формате вводить строку - "Chocolate", "CHOCOLATE" или "ChocolateDoughnut"? В общем, все эти проблемы нам не нужны, поэтому мы создали ENUM.

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

Как видите, принимается ENUM DoughnutTypes, а возвращается любой объект, имплементирующий интерфейс  Doughnut. Таким образом, мы теперь можем "конструировать" любой пончик и возвращать его пользователю.

Итак, перейдем к сути. Определяем тип с помощью обычного switch... case:

Как Вы видите, мы создаем и возвращаем новый объект нужного типа. Если будет введено не CHERRY, не CHOCOLATE и не ALMOND (а это может быть только null), тогда мы показываем ошибку.

Хорошо, теперь протестируем наш код. Создадим main(), в котором создадим все типы поисков по очереди и вызовем на них метод eat():

В консоли получим:

Отлично! Вот мы и создали свою первую фабрику.

Какие преимущества дает паттерн Фабрика (Factory)

Благодаря паттерну Factory наша жизнь облегчается:

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

Например, создадим метод "скушать случайный пончик" - eatRandomDoughnut:

Обратите внимание - nextInt() будет генерировать целые числа в диапазоне [0; 3). То есть возможны такие опции: 0, 1 и 2. А это и есть 3 вида начинок пончиков.

Давайте запустим:

Для убедительности, можно запустить метод eatRandomDoughnut() сто раз:

В итоге получим что-то наподобие:

2. Во-вторых, мы можем "запаковать" дополнительный функционал в нашу Фабрику. Например, посчитаем, сколько пончиков каждого типа было создано конкретной фабрикой.

Сначала создаем новые переменные, которые будут хранить результат:

Нам желательно задать начальное значение для этих переменных - ноль.

Теперь, как мы будем изменять эти значения? Очень просто -дополним наш метод getDoughnut():

Отлично! Для вывода результатов в консоль напишем следущий метод:

Итого, наш класс DoughnutFactory целиком, со всеми дополнениями, будет выглядеть так:

Отлично! Теперь запустим с помощью такого кода:

В консоли получим что-то похожее на это:

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

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

3. И в-третьих, с помощью паттерна Factory мы можем генерировать сложные объекты намного проще и с меньшим количеством ошибок.

        Например, у нас есть объект с огромным количеством полей:

  • бОльшая часть этих полей заполняются типично (например, тесто "вариант 1"  для пончика с вишней и тесто "вариант 2" для пончика с белым шоколадом);
  • а часть полей будет заполняться разными значениями.

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

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

 


Надеемся, что наша статья была Вам полезна.  Можно записаться к нам на курсы по Java на сайте.

FacebooktwittertumblrFacebooktwittertumblr

FacebooktwittertumblrFacebooktwittertumblr
Самоучители--узнать детальнее--