Создание объекта через JsonExporter() привязывает код к конкретному классу. Порождающие паттерны переносят решение «какой класс и как?» в одно место, чтобы клиенты зависели только от интерфейса. В уроке: простая фабрика, фабричный метод, абстрактная фабрика и питоничные способы их записи.
Проблема: растущая цепочка if
Каждый новый формат меняет export, а каждый вызывающий повторяет ветвление. Функция смешивает выбор формата и его реализацию (см. OCP).
import json
def export(rows, fmt):
if fmt == "json":
return json.dumps(rows)
elif fmt == "csv":
return "\n".join(",".join(str(v) for v in r.values()) for r in rows)
raise ValueError(f"unknown format: {fmt}")
rows = [{"id": 1, "name": "Ann"}, {"id": 2, "name": "Bob"}]
print(export(rows, "json"))
print(export(rows, "csv"))[{"id": 1, "name": "Ann"}, {"id": 2, "name": "Bob"}]
1,Ann
2,BobПростая фабрика
Сделайте каждый формат классом с одним методом dump, а имя в класс переводит одна функция. Словарь заменяет цепочку if, неизвестное имя даёт одну понятную ошибку. Это не паттерн GoF, но он решает большинство реальных задач.
import json
class JsonExporter:
def dump(self, rows): return json.dumps(rows)
class CsvExporter:
def dump(self, rows):
return "\n".join(",".join(str(v) for v in r.values()) for r in rows)
def make_exporter(fmt):
exporters = {"json": JsonExporter, "csv": CsvExporter}
try:
return exporters[fmt]()
except KeyError:
raise ValueError(f"unknown format: {fmt}") from None