Зарегистрируйтесь, чтобы продолжить обучение

Null Object Pattern Python: Полиморфизм

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

Проблема проверки существования объекта

Представим сайт, на котором есть возможность аутентификации. Внутри такой системы всегда присутствует понятие «текущий пользователь». Это тот пользователь, который аутентифицировался при помощи логина и пароля.

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

# Где-то в воображаемом коде

if is_authenticated and current_user.has_articles():
    for article in current_user.get_articles():
        # Здесь выводим статьи

Тут мы сталкиваемся с проверкой аутентификации пользователя. Если ее не сделать, то код упадет с ошибкой, потому что вызывается метод has_articles() у None, так как пользователь отсутствует, если он не залогинился.

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

Чтобы управлять подобными ситуациями, существует несколько подходов, одним из которых является паттерн Null Object.

Использование Null Object Pattern

Null Object Pattern — это шаблон проектирования, который используется для обработки нулевых или отсутствующих значений. Вместо того чтобы возвращать null или None и затем проверять его на каждом шаге, мы создаем объект, который представляет отсутствующее значение и имеет тот же интерфейс, что и остальные объекты.

Это упрощает код, поскольку нам не нужно постоянно проверять, является ли объект null или None. Мы можем просто вызывать методы, как если бы это был обычный объект.

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

class User:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    def has_articles(self):
        return True

    def get_articles(self):
        # воображаемый код, делающий запрос к базе и получающий все статьи
        return db.get_articles(self)


class Guest:
    def get_name(self):
        return "Guest"

    def has_articles(self):
        return False

    def get_articles(self):
        return []

Здесь User — это класс для аутентифицированного пользователя, а Guest — это класс для неаутентифицированного пользователя. Guest представляет собой «нулевой объект», у которого тот же интерфейс, что и User, но возвращает значения по умолчанию.

Большинство методов Null Object возвращает False либо пустые списки, так как у этого пользователя ничего нет.

Теперь напишем функцию, которая возвращает текущего пользователя:

def get_current_user(authenticated):
    if authenticated:
        return User("Alice")
    else:
        return Guest()

Эта функция возвращает либо аутентифицированного пользователя, либо нулевой объект. Мы можем использовать ее, чтобы выводить информацию о текущем пользователе без проверки, является ли пользователь null или None:

current_user = get_current_user(authenticated=True)
print(current_user.get_name())  # => Alice
print(current_user.get_articles())  # => ['article1', 'article2', 'article3']

current_user = get_current_user(authenticated=False)
print(current_user.get_name())  # => Guest
print(current_user.get_articles())  # => []

Здесь мы вызываем функцию get_current_user() с разными значениями параметра authenticated, который указывает, является ли пользователь аутентифицированным. В зависимости от значения этого параметра функция возвращает либо объект класса User с определенными данными, либо объект класса Guest.

Мы видим, что мы без проблем вызываем методы get_name() и get_articles(). При этом во втором случае мы работаем с экземпляром класса Guest. Именно в этом и заключается основное преимущество паттерна Null Object: нам не нужно проверять, является ли объект null или None, перед тем как вызвать его методы.

# где-то дальше в коде
# нам не нужно беспокоиться о том зарегистрированный это пользователь или нет
# потому что метод has_arctiles есть как у User, так и у Guest
if current_user.has_articles():
    for article in current_user.get_articles():
        # Здесь выводим статьи

Выводы

Паттерн Null Object помогает упростить код и избежать постоянных проверок на null или None. Он особенно полезен в языках программирования, где null или None может вызвать ошибку при попытке вызвать методы.

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

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff