Вы хотите действительно быстрый способ сделать объект фабрики? Тогда лямбды или другая передача функций — это то, что вам нужно! Это не только быстро, это действительно просто. Бьюсь об заклад, если вы хорошо разбираетесь в 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 Джейкоба Циммермана в блоге « Идеи программирования с Джейком» . |