Рубрики
Без рубрики

Слишком много знаю

Принцип одной ответственности заявляет, что данная программа, процедура или функция должны быть … Теги с Python, программированием, кодовой коробкой.

Принцип одинарной ответственности заявляет, что данная программа, процедура или функция должны нести ответственность за одну вещь. Следуя этим правилам, означает также, что мы должны думать о том, сколько наша часть программы знает, а сколько достаточно для выполнения заданной операции.

Дополнительные аргументы

Дополнительные аргументы являются значениями, которые могут быть переданы для заданной функции или нет, необязательный аргумент также может означать, что значение может быть Нет или нулевой или любое другое пустое значение (зависит от языка). Давайте посмотрим на пример первого кода, он будет Python с некоторыми аннотациями типа Mypy, но не волнуйтесь, если вы не знаете Python, примеры должны быть легкими для кодера любого языка, так как я пытался сохранить сложность на очень низком уровне.

def buy_product(user: User = None, product: Product = None):
  if user and product:
    user.products.add(product)

Функция buy_product Имеет два дополнительных аргумента, и это означает, что его можно назвать без ничего, или с Нет значения. Ниже некоторых примеров такого «странного» вызова функции:

buy_product() # yyy, but what product and who buys it?
buy_product(user=None, product=None) # buying None by None :)
buy_product(user=my_user, product=None) # ah yes user buys nothing
buy_product(user=None, product=my_product) # ah yes None buys smth

Я надеюсь, когда вы прочитали выше, вы чувствуете себя некомфортно, по крайней мере, я делаю. Здесь что-то не так, как мы можем купить продукт, не предоставив, кто покупает это ни то, что покупается.

Проблема с buy_product Это действительно не отвечает на ситуацию, когда один из аргументов не установлен, эта функция для Нет В любом из аргументов изменяется функцией NOOP (без операций), так что ничего не делает.

Намерение создателя такой функции состоит в том, чтобы вызвать его в каких-либо обстоятельствах и быть уверенным, что оно будет работать только для действительных данных, а также быть NOOP в других ситуациях. Это также позволяет избежать ветвления логики, мы всегда называем это. Но мы действительно ничего избегаем? И что, если мы пройдем дополнительное значение еще больше?

def do_payment(user: User = None, product: Product = None):
  if user and product:
    call_some_payment_service(...)

def add_product_db(user: User = None, product: Product = None):
  if user and product:
    append_to_db(...)

def log(user: User = None, product: Product = None):
  if user and product:
    log_service_call(...)

def buy_product(user: User = None, product: Product = None):
  do_payment(user, product)
  add_product_db(user, product)
  log(user, product)

Теперь наша ситуация меняется еще больше. Мы все еще можем просто позвонить нашу функцию buy_product () Но это означает, что он проходит Нет вниз и все функции имеют дело с этим.

Проблема со всем этим состоит в том, что все функции могут работать только с существующими значениями, не готовы работать с Нет , подобно Нет не поддерживается значением доменов этих функций. Эти функции слишком много знают, они знают, что аргумент, который для них требуется для работы, не может быть установлен. Делая это, мы расширяем все обязанности функций для обработки топости. Мы не хотим этого, поверьте мне.

Решение этой проблемы является – вызовите эти функции только тогда, когда у нас достаточно данных для выполнения операции.

# definition without optionality in arguments
def buy_product(user: User, product: Product):
  # below functions also do not consider working with None
  do_payment(user, product)
  add_product_db(user, product)
  log(user, product)


# calling it
if user and product:
  buy_product(user, product)

В результате все данные передаются ниже buy_product безопасен, как всегда настроен. Не нужно больше справляться с необходимостью.

Отправить немного электронной почты

Наша кодовая база имеет такую функцию для отправки электронной почты.

def send_email(user, email_template, data):
  if user.active and user.email_notifications:
    smpt_conf = {# setting some email conf #}
    content = compile_template(email_template, data)
    my_email_service(
      smpt_conf, 
      user.email, 
      content, 
      title = data['title']
    )

Теперь приходит новые требования, и нам нужно отправить электронное письмо не пользователю, а кому-то еще. У нас есть электронная почта, название и даже контент, который составляется HTML. Но … мы явно не можем использовать наш send_email Функция, как она подготовлена только для пользователей и только для некоторых конкретных шаблонов.

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

def send_email(email, title, content):
  smpt_conf = {# setting some email conf #}
  my_email_service(smpt_conf, email, content, title)

Новая версия упрощена, имеет только знания о сообщениях электронной почты. Знает, что и к какому электронному письму следует отправить. Теперь его можно использовать в корпусе нового использования:

send_email(my_email, my_title, my_html)

А также Теперь использую с пользователем

def send_email_to_user(user, email_template, data):
  if user.active and user.email_notifications:
    send_email(
      user.email, 
      data['title'], 
      compile_template(email_template, data)
    )

Сделано, мы сделали send_email Ответственный только для отправки электронных писем и send_email_to_user Является ли функция связанной области, которая имеет дело с отправкой электронных писем для пользователей.

И теперь небольшое дополнение, если мы посмотрим на send_email_to_user Также NOOP, когда наш пользователь не активен или не имеет уведомлений по электронной почте, почему тогда эта функция должна знать ее? Ну, в этой конкретной ситуации может быть в порядке, чтобы иметь функцию, которая отвечает за отправку электронной почты пользователей, и отправка его только тогда, когда это возможно, у нас есть эта дополнительная безопасность, которую электронная почта не будет отправлена, если не должно быть. Имея функцию, которая иногда ничего не делает, – это инструмент, который мы должны использовать, но не чрезвычайно.

Я вошел в систему?

log_movement(request)
add_points(request)
send_sms(request)

Определения вышеуказанных функций:

def log_movement(request: Request):
  if request.session:
    log("movement", request.session.user.name)
  else:
    log("noop",'Anonymous user')

def add_points(request: Request):
  if request.session:
    db_add_points(request.session.user)

def send_sms(request: Request):
  if request.session and request.session.user.sms_notifications:
    sms_service("movement", request.session.user)

Теперь, что не так? Посмотрите, что все эти функции имеют одинаковую проблему и одинаковое состояние, они проверяют, имеет ли пользователь сеанс и выполнять работу. Объект запроса содержит много других информаций, которые действительно не важны для этих функций, и если бы мы хотели проверить их, нам нужно будет издеваться над всеми этими деталями, издевательства не использовали вещи не специально радостным опытом. Опять же, эти функции слишком много знают, давайте исправим это.

# definitions with less knowledge
def log_movement(user_name: string):
  log("movement", user_name)

# below function even not need to exists 
# as it just calls another
def add_points(user: User):
  db_add_points(user)

def send_sms(user: User):
  if user.sms_notifications:
    sms_service("movement", user)

# calling them
if request.session:
  user = request.session.user
  log_movement(user)
  add_points(user)
  send_sms(user)
else:
  log("noop",'Anonymous user')

Вместо того, чтобы переместить ответственность перед всеми функциями, мы просто имеем дело с вопросом один раз на вершине, другие функции имеют только то, что они хотели, больше ничего не хотелось.

Таким образом, если мы сможем сужать домен и знание функций, мы должны сделать это, вместо того, чтобы передавать проблему вниз, справиться с ним, как только он может быть решен. Если функция не работает для Нет Не используйте его с ним, если многие функции имеют дело с той же проблемой, возможно, может быть решена проблема, прежде чем они будут называться? Конечно, это зависит, и каждый выбор, который вы делаете в коде, должны быть тщательно рассмотрены, так как нет серебряных пуль.

Оригинал: “https://dev.to/macsikora/know-too-much-14md”