Аккуратный код

Аккуратный код

Сложнейший вопрос в объектно‑ориентированном программировании — как правильно структурировать новый код в приложении: положить в отдельный класс или просто дописать в конце существующего? Если долго складывать код неправильно — кодовая база деградирует. Чтобы правильно определять место для нового кода, нужно изучить несколько понятий — зацепление, связность и заменяемость. В этом совете поговорим о зацеплении.

Зацепление — это мера того, насколько класс в коде зависит от соседних классов. Чем больше класс зависит от соседей, тем выше зацепление. Чем зацепление ниже — тем легче читать и дорабатывать код.

Представим бэкенд для магазина, который продаёт электронные курсы. В коде у него есть класс «Заказ», у которого есть метод «отправить пользователю ссылку на курс». Метод использует электронную почту, но должен ли он работать с почтовым провайдером напрямую? Давайте попробуем:

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
​
class Order:
  def ship(self):
    message = Mail(to_emails=self.customer.email, subject='Вот ваш курс', content='Удачи в прослушивании')
    sendgrid = SendgridAPIClient(settings.SENDGRID_API_KEY)
    sendgrid.send(message)

Получился код, в котором класс «Заказ» намертво зацеплен с классами Mail и SendGridAPIClient из поставки Сендгрида. Если появятся другие классы, которые пишут письма пользователю — к примеру «Догонятель», который через три дня после покупки спрашивает у пользователя, как ему курс, то придётся туда копировать эту логику:

class Chaser:
  def chase(self):
    message = Mail(to_emails=self.customer.email, subject='Как вам курс?', content='Расскажите, что нам улучшить?')
    sendgrid = SendgridAPIClient(settings.SENDGRID_API_KEY)
    sendgrid.send(message)

Такой одинаковый код программисты называют «копипастой» — по имени операции copy‑paste , которая его порождает. Копипаста приводит к большому количеству ненужной работы. Допустим, Сендгрид стал хуже доставлять наши письма и, чтобы спасти ситуацию, мы решили перейти к другому почтовому провайдеру, скажем, к Мейлджету. АПИ Мейлджета несовместим с АПИ Сендгрида, и поэтому нам придётся искать в коде все места, которые зацеплены с реализацией Сендгрида и полностью переделывать их на Мейлджет — отдельно править и «Заказ», и «Догонятель». Если таких мест в коде 5 — придётся править все 5.

Теперь давайте отцепим «Заказ» и «Догонятель» от Сендгрида. Для этого сосредоточим логику отправки в одном месте — специальном классе «Почтальон»:

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
            ​
class Mailman:
  def __init__(self, customer):
    self.customer = customer
​
  def deliver(self, subject, message):
    message = Mail(to_emails=self.customer.email, subject=subject, content=message)
    sendgrid = SendgridAPIClient(settings.SENDGRID_API_KEY)
    sendgrid.send(message)

Теперь ни «Заказ», ни «Догонятель» ничего не знают о том, как мы отправляем почту. Их задача — просто попросить почтальона доставить письмо:

from app.mailmain import Mailman
​
class Order:
  def ship(self):
    mailman = Mailman(customer)
    mailman.deliver(subject='Вот ваш курс', content='Удачи в прослушивании')

«Почтальон» теперь легко может переключаться между почтовыми провайдерами или инкапсулировать другую полезную логику — к примеру, мы могли бы научить его журналировать все письма, которые мы шлём, или вообще писать пользователям в телеграм, а не на почту. И для этого не нужно знать, сколько у нас в системе классов, отправляющих письма, — мы уверены, что все они пользуются «Почтальоном» и любые доработки в нём изменят процесс отправки во всём приложении.

P. S. Это был совет об управлении разработкой. Хотите больше знать о планировании спринтов, управлении продуктом или о настройке инфраструктуры? Присылайте вопросы.
Управление проектомВеб‑разработка
Отправить
Поделиться
Запинить

Рекомендуем другие советы