Предупреждение: Данный пост пропах субъективной оценкой и холиварными суждениями.
Вы часто задумывались про пакеты и их именование? А про именование модулей?
Или может вы все складываете в модуль utils
- “и так сойдет”?
А вишенкой на торте - в один модуль запихнуть с десяток классов, процедур и методов.
Давайте вместе подумаем как сделать лучше, проще и удобней.
Для начала давайте перестать все хранить в utils.
Допустим, у нас есть в utils 3 модуля:
date_operations.py
(как-то преобразовываем даты)currency_operations.py
(преобразовываем валюты)service_code_converter.py
(а тут какие-нибудь системные коды преобразуем в человеко-читаемое нечто)
Что можно с ними сделать? Для начала давайте разобъем на гипотетические классы, с учетом ухода от абстракций к реальным сущностям. Получим:
DateClass
CurrencyClass
ServiceCodesClass
Почему добавляется Class - объясню позже.
Отлично, теперь мы получили набор гипотетических классов, которые надо правильно разложить по пакетам и модулям.
- для
DateClass
формируем пакетdate
- для
CurrencyClass
формируем пакетcurrency
- для
ServiceCodesClass
формируем пакетservice_codes
Теперь, импортируя эти классы, будем получать что-то вроде:
from currency import CurrencyClass
Отлично, осталось разложить по правильным пакетам. Но как это сделать? Давайте попробуем пакет назвать по названию модуля. Получим что-то вроде:
from currency.currency import CurrencyClass
Выглядит как в песне “В доме, который построил Джек”. В целом - не очень. Но что же делать в таком случае?
А в таком случае - устанвливайте принадлежность данных модулей.
Вот, например, ServiceCodesClass
- скорее всего принадлежит обработке какого-нибудь ответа от смежного сервиса.
А точнее результату его парсинга.
Отлично, тогда можно сделать так:
from message.response.service_codes import ServiceCodesClass
CurrencyClass
, впрочем как и DateClass
- скорее всего будут относиться к преобразованию данных в запросах/ответах:
from message.currency import CurrencyClass
from message.date import DateClass
Итого мы получили следующее:
from message.currency import CurrencyClass
from message.date import DateClass
from message.response.service_codes import ServiceCodesClass
И сравним с предыдущим вариантом:
from utils import date_operations, currency_operations, service_codes_operations
Что же нам это дало?
1) мы теперь четко знаем принадлежность конкретного класса к задаче
2) мы четко видим структуру приложения
3) мы не можем накидать абстрактных методов и процедур в utils
, как в помойку
Итого, новый разработчик, придя на проект - сразу будет понимать 3 основных вещи:
- Что это?
- Зачем это?
- Откуда это?
Более того, через полгода вам не придется шариться по utils, выискивая ту самую процедуру get_id
или format_all
.
Да, я забыл рассказать почему название класса DateClass
, а не Date
.
По хорошему - вы не должны возвращать примитивы, такие как bool или int. Вы должны возвращать объекты. В будущем вам будет проще добавить еще одно свойство, чем поменять примитив.
Допустим была у вас функция, которая возвращала True или False. Но тут, вам понадобилось третье состояние! А давайте быстро заменим на циферки, будет возвращать 0, 1, -1. Почему бы и нет, можем же!
Вы спешно поменяли, сделали рефактор силами idea или сами полазили по коду, нашли все использования этой функции и поменяли обработчики. Довольные выкатили на какой-нибудь слой для теста ииии…. Все сломалось. Колесо кармы дало новый оборот.
Если вы будете возвращать объект - вы можете изменить количество его аттрибутов, сделать наследника класса, формирующий объект, заоверрайдить его методы.
В итоге рядом с DateClass
будет лежать DateResponse
.
Да, это нарушит конракт. Но при этом вы сохраните обратную совместимость. Ну и это не является аксиомой. Но именование должно быть унифицировано для всего проекта. А лучше для всей команды.
Впрочем. это тема для отдельного разговора.
Вот такие мысли сегодня ночью.
Надеюсь подискутируем в этот раз в комментариях.