Комментарии 7
Кажется, что пример про UserCollection
получился не python-way. Или мне одному в коллекции хочется видеть стандартный интерфейс __contains__
, а не includes
?
Если у нас есть .get, то нам всё равно у какого он типа. Если у метода есть .next(), то мы можем его итерировать.
Пример с is_password_valid выглядит очень уродливо. Для таких вещей есть @staticmethod, а функция нас не «обманывает», так как в нормальном режиме (как член класса) она имеет ещё и self в аргументах. Если же self не нужен (для удобства тестирования) — то как раз @staticmethod поможет.
90% процентов джавовских паттернов разработки — всего лишь борьба с уродствами самой java. В питоне оно из коробки или реализуется в пол-пинка. В том числе опциональные аргументы, keyword-аргументы, или вот это, вот, с «интерфейсами».
Вот с этим соглашусь, а вот что написал автор в статье, я не особо-то и понял. Как-то не связанно и сумбурно повествует.
Например, у нас имеется простой класс:
class ColourMix:
def __init__(self, colours):
self.colours = colours
def print_colours(self):
for colour in self.colours:
print(f"Colour RGB: ({colour.red}, {colour.green}, {colour.blue})")
При разработке другой части приложения клиент не хочет раскрывать реализацию и опирается только на интерфейс. Но интерфейс не говорит ничего о том, чем же является colours
, и что он содержит внутри. Тогда единственным способом работы с данным классом является чтение его реализации и поиск всех методов, которые он дергает у colours
и элементов colours
. Это неэффективно и замедляет разработку. Если явно сказать, что colours — это список, содержащий объекты-значения, то в реализацию не нужно раскрывать:
from typing import NamedTuple, List
class Colour(NamedTuple):
red: int
green: int
blue: int
class ColorMix:
def __init__(self, colours: List[Colour]):
self.colours = colours
def print_colours(self):
for colour in self.colours:
print(f"Color RGB: ({colour.red}, {colour.green}, {colour.blue})")
Но при этом мы не лишаемся возможностей, которые предоставляет нам утиная типизация, только теперь нам известно, что для работы ColourMix
нужен объект, который реализует интерфейс списка, причем не обязательно наследованием от списка.
Если у нас есть .get, то нам всё равно у какого он типа. Если у метода есть .next(), то мы можем его итерировать.
Это, конечно, верно. Статья о том, что интерфейс объекта должен явно говорить о наличии .get или .next.
Для таких вещей есть @staticmethod, а функция нас не «обманывает», так как в нормальном режиме (как член класса) она имеет ещё и self в аргументах.
self
в аргументах ничем не поможет, если в конструкторе будет внешняя зависимость:
from utils import DatabaseConfig
# DatabaseConfig предоставляет доступ к конфигу,
# который хранится в БД
class PasswordUtils:
def __init__(self):
self.min_length = DatabaseConfig().password_min_length
def is_password_valid(self, password: str) -> bool:
return len(password) > self.min_length
Инстанцирование класса в "неправильном" окружении снова возбудит ошибку. Функция или метод класса — это инструменты для демонстрации примера внешней зависимости, которая раскрывает реализацию. Суть примера не в применяемых инструментах, а в необходимости явной передачи всех внешних зависимостей.
Паттерны разработки — это борьба со сложностью программ. Если в приложении в нескольких местах есть повторяющийся код инстанцирования и настройки объекта, я вынесу его в фабрику — единое место для получения "готового к работе" инстанса — и не важно, на каком языке я программирую. Необходимость инкапсулировать сложное создание объекта не зависит от языка, а вот реализация зависит. Паттерны рассказывают о том, как увидеть эту необходимость, а не о том, какой конкретно код нужно писать.
Второй пример, с password_min_length — либо у нас это статичное, и мы пишем self.min_length = DatabaseConfig.password_min_length, либо, если это берётся из базы данных, то мы пишем так:
def __init__(self, db):
self.min_length = db.password_min_length
Но я всё ещё не понимаю, при чём тут паттерны. Лично у меня от всего этого есть ощущение, что для борьбы со сложностью авторы разводят ещё большую сложность.
Интерфейс vs interface