Статьи

Функциональная фабричная модель

Вы хотите действительно быстрый способ сделать объект фабрики? Тогда лямбды или другая передача функций — это то, что вам нужно! Это не только быстро, это действительно просто. Бьюсь об заклад, если вы хорошо разбираетесь в Lambdas, у вас есть отличная идея, как это сделать, просто прочитав заголовок. Если вы один из них, останьтесь вокруг; Вы никогда не знаете, что вы могли бы узнать.

В качестве примечания: я делаю примеры кода на Java и Python. Почему? Потому что я люблю оба языка, и, конечно, не помешает сделать что-то для обоих.

Грунтовка по заводскому образцу

Если вы уже знаете, что такое Factory Design Pattern, вы можете перейти к следующему разделу.

Смысл паттерна Factory — предоставить объектам и методам способ создания экземпляра объекта без раскрытия всей (или часто любой ) логики создания экземпляров (что необходимо передать в конструктор).

пример

В качестве глупого примера, скажем, есть класс Scientist , которому нужен способ создания новых Pen чтобы записывать данные своего эксперимента, но он не хочет беспокоиться о процессе создания. Чтобы сделать это, вы бы дали Scientist PenFactory , и все, что Scientist нужно знать, — это нажать кнопку на фабрике, чтобы получить новое перо.

PenFactory — это простой объект, имеющий только метод create() который предоставляет новый экземпляр Pen при каждом его вызове. Если Scientist позаботился о том, какого цвета было Pen , вы могли бы предоставить ему ColoredPenFactory , метод create() также принимает параметр цвета. Затем ColoredPenFactory должен был бы выяснить, как снабдить перо этим цветом.

Расширение идеи фабричного образца

Фабричный шаблон — это шаблон для объектно-ориентированного кода, и поэтому он ограничен тем, как работает ОО, но мы можем взять его цель и попытаться найти способ сделать его функциональным, что фактически делает его МНОГО. Полегче.

На самом деле, большое количество шаблонов проектирования ОО было создано из-за отсутствия способности передавать функции. Большинство из них можно заменить, просто передав функцию. Короткий список из них включает Команду, Фабрику и Стратегию. Многие другие могут удалить много иерархии классов, если они принимают функции. Некоторые из них включают Шаблон и Посетитель.

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

Ручка OO

Чтобы вы могли увидеть разницу между классическим шаблоном OO и новым шаблоном функции, вот примеры классов и интерфейсов в OO Java.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface Pen {
   void write(String toWrite);
   boolean outOfInk();
}
 
public interface PenFactory {
   Pen create();
}
 
public class Scientist {
     
   private PenFactory penerator;
   private Pen pen;
     
   public Scientist(PenFactory penerator) {
      this.penerator = penerator;
      this.pen = penerator.create();
   }
     
   public void writeData(String data) {
      if(pen.outOfInk()) {
         pen = penerator.create();
      }
      pen.write(data);
   }
}

И в ОО Питон

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Pen(metaclass=ABCMeta):
 
    def write(self, text):
        pass
 
    def out_of_ink(self):
        pass
 
 
class PenFactory(metaclass=ABCMeta):
 
    def create(self):
        pass
 
 
class Scientist():
 
    def __init__(self, pen_factory):
        self.penerator = pen_factory
        self.pen = self.penerator.create()
 
    def write_data(self, data):
        if self.pen.out_of_ink():
            pen = self.penerator.create()
        pen.write(data)

Вы PenFactory как я назвал PenFactory экземпляров penerator ? Я думал, что это было глупо. Я надеюсь, вам тоже понравилось. Если нет, то ладно.

Преобразование в простой функциональный шаблон

Когда дело доходит до версии Java, вам фактически PenFactory вносить какие-либо изменения, поскольку PenFactory считается функциональным интерфейсом, но в этом нет необходимости, поскольку вы можете заменить любой экземпляр PenFactory на Supplier<Pen> . Итак, класс Scientist будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class Scientist {
     
   private Supplier penerator;
   private Pen pen;
     
   public Scientist(Supplier penerator) {
      this.penerator = penerator;
      this.pen = penerator.get();
   }
     
   public void writeData(String data) {
      if(pen.outOfInk()) {
         pen = penerator.get();
      }
      pen.write(data);
   }
}

В Python вы можете полностью удалить PenFactory и просто использовать любой вызываемый тип, который возвращает Pen . Вам также придется изменить строки в Scientist которые вызывают фабричный метод create() и просто заменить его скобками для вызова.

01
02
03
04
05
06
07
08
09
10
class Scientist():
 
    def __init__(self, pen_factory):
        self.penerator = pen_factory
        self.pen = self.penerator()
 
    def write_report(self, data):
        if self.pen.out_of_ink():
            self.pen = self.penerator()
        self.pen.write(data)

Итак, чтобы создать экземпляр Scientist с лямбдами, которые предоставляют экземпляры MyPenClass , вы должны напечатать это на Java:

1
Scientist albert = new Scientist(() -> new MyPenClass());

или это в Python:

1
2
3
albert = Scientist(lambda: MyPenClass())
# or skip the lambda by passing the "constructor"
thomas = Scientist(MyPenClass)

Фабрики для классов с зависимостями

Допустим, я хотел создать фабрику для класса, для конструктора которого требуется название марки ручек. Мы назовем этот класс BrandPen . Как бы мы сделали фабрику для этого? Что ж, написание лямбд не было бы по-другому, правда. Как насчет других способов определения вызываемых элементов, которые должны быть переданы?

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

1
2
3
4
Supplier bicPen = () -> new BrandPen("BiC");
Scientist thomas = new Scientist(bicPen);
// assuming that BrandPen has a static method called bicPen
Scientist nicola = new Scientist(BrandPen::bicPen);

В Python вы можете определить функцию, которая делает это, или назначить partial для этого:

1
2
3
4
5
6
def bic_pen():
    return BrandPen("BiC")
# or
bic_pen = partial(BrandPen, "BiC")
 
nicola = Scientist(bic_pen)

Заводы с зависимостями

О, чувак, теперь Scientist хочет иметь возможность указать цвет ручки, которую поставляет фабрика! Ну, вы могли бы дать ему разные фабрики для каждого цвета и сказать ему использовать каждую фабрику для изготовления разных ручек, но в его лаборатории просто нет места для стольких PenFactory ! Мы должны дать фабрику, которая может сказать, какой цвет использовать.

Для этого нам нужно сменить Supplier<Pen> для Java на Function<>Color, Pen> . Очевидно, вам не нужно менять тип в Python, поскольку он динамический и не требует информации о типе.

Но классы Scientist также должны изменить то, как они используют свои фабрики. В Java, где Scientist запрашивает новый экземпляр, он также должен предоставить цвет, например так:

1
pen = penerator.apply(Color.RED);

или как это в Python:

1
self.pen = self.penerator(Color.RED)

Фабрика, которую мы передаем в Scientist на Java, может выглядеть так:

1
Scientist erwin = new Scientist(color -> new ColoredPen(color, "BiC"));

Тот, который мы даем в Python, может выглядеть так:

1
2
3
4
def colored_bic_pen(color):
    return ColoredPen(color, "BiC")
 
erwin = Scientist(colored_bic_pen)

Мультиметодные заводы

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

Outro

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

Я надеюсь, что вы узнали что-то. Если вы этого не сделали, надеюсь, вам понравился хотя бы пример.

Ссылка: Функциональный шаблон фабрики от нашего партнера JCG Джейкоба Циммермана в блоге « Идеи программирования с Джейком» .