Вы хотите действительно быстрый способ сделать объект фабрики? Тогда лямбды или другая передача функций — это то, что вам нужно! Это не только быстро, это действительно просто. Бьюсь об заклад, если вы хорошо разбираетесь в 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): passclass PenFactory(metaclass=ABCMeta): def create(self): passclass 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 bicPenScientist nicola = new Scientist(BrandPen::bicPen); |
В Python вы можете определить функцию, которая делает это, или назначить partial для этого:
|
1
2
3
4
5
6
|
def bic_pen(): return BrandPen("BiC")# orbic_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 Джейкоба Циммермана в блоге « Идеи программирования с Джейком» . |