Главная

Паттерн проектирования: Null-объект

Избегайте условной сложности, используя этот шаблон

Поделиться:
 

20+

Существует 23 классических шаблона проектирования, которые описаны в книге «Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения» (с англ. Design Patterns: Elements of Reusable Object-Oriented Software). Основная суть шаблонов состоит в унификации решения часто встречающихся проблем.

В этой статье я хочу рассказать о том, что такое шаблон Null object и где его следует применять. Данный шаблон не включен в список классических паттернов. Впервые он был опубликован в книге «Pattern Languages of Program». Сегодня он широко используется во избежание условной сложности кода.

Основная идея

В объектно-ориентированном программировании нулевой объект — это объект без ссылочного значения или с определенным нейтральным («нулевым») поведением. Шаблон проектирования Null object описывает использование нулевых объектов и их поведение (или его отсутствие) — Википедия.

Главной особенностью шаблона является то, что это позволяет нам избежать усложнения кода. В большинстве языков, таких как JavaC# или JavaScript, ссылки могут быть нулевыми(NULL). В зависимости от реализации программы, может возникнуть необходимость в проверке ссылок на то, что они не являются нулевыми. Это нужно для того, чтобы иметь возможность вызывать необходимые методы, многие из которых не могут быть использованы для нулевых ссылок.

Короче, подводя итог, можно сказать, что шаблон Null object позволяет нам избежать усложнения кода, используя объекты, а не примитивные типы. Диаграмма данного шаблона выглядит следующим образом:

Класс AbstractObject — это абстрактный класс, определяющий различные операции, которые должны быть реализованы в RealObject и объекте «null» или «default» (NullObject). RealObject будет выполнять операцию для каждого реального(не Null) объекта, в то время как NullObject ничего не будет делать или же будет выполняться операция по умолчанию.

В каких случаях нужно использовать шаблон Null object?

1. Когда необходимо динамически добавлять функционал к определенным объектам, то есть не затрагивая другие объекты.
2. Когда необходимо добавить функционал, который может быть отключен в любой момент.

Преимущества использования шаблона Null object

У шаблона Null object есть несколько преимуществ, обобщенных в следующих пунктах:
1. Шаблон определяет иерархию классов, состоящих из реальных и нулевых объектов.
2. Возможность использования нулевых объектов.
3. Код становится более простым и читабельным, поскольку исключается условная сложность. Использование реальных и нулевых объектов ничем не отличается и не требует дополнительной обработки.

Пример: Saiyan’s world (Проблема)

Супер Сайян — легендарная трансформация сайянов. PS. Что-то из аниме…

Сейчас я хочу показать, как на практике реализовать этот шаблон. В примере я буду использовать JavaScript/TypeScript. Немного контекста:

Представьте, что есть класс Saiyan, который позволяет моделировать атрибуты и методы класса Saiyan. Этот класс реализует интерфейс ISaiyan, который четко определяет характеристики, которым должен соответствовать каждый объект, чтобы быть подлинным Saiyan. Фабрика(шаблон проектирования) под названием SaiyanFactory используется для создания объектов Saiyan. Этот класс абстрагируется от создания Saiyan и может быть сгенерирован с помощью RAM, запросов в базе данных или сложного алгоритма для создания новых объектов.

Код предоставлен ниже. Проблема, которую мы хотим решить, возникает в классах, которые работают в качестве клиентов и используют фабрику. В клиентской части кода вызывается метод getSaiyan для получения Saiyan. В частности, создается Вегета, Боб, Сон Гоку и Лор. Единственными Сайянами из предыдущего списка являются Вегета и Сон Гоку; и поэтому и Боб, и Лора не могут быть созданы как объекты типа Саян.

Так как мы не можем гарантировать, что фабрика всегда вернет объекты типа Saiyan, поэтому всегда должны делать проверку. В таком случае конечный код усложняется из-за повторяющихся условных выражений if-else.
Да, данный функционал можно абстрагировать с помощью функции, но он все равно будет в коде. В итоге получается следующая диаграмма UML.

В следующей секции статьи мы решим эту проблему с помощью шаблона Null object.
Диаграмма, показывающая связь между ISaiyan и Saiyan представлена выше

Код фабрики:

Ну и наконец, код, связанный с клиентской частью, который усложнен из-за обработки нулевых объектов, полученных из фабрики:

Пример: Saiyan’s world (Решение)

В вышеприведенном случае хорошим решением будет являться использование шаблона NULL object.
Новая диаграмма UML с использованием этого шаблона:

Давайте теперь рассмотрим, как это вообще работает. Мы делаем четыре запроса нашего Saiyan и результат сохраняем в переменные. Это помогает избежать проверок на то, является ли объект нулевым. В нашем примере мы также используем метод toString.

Мы упростили код благодаря небольшим изменениям во внутренней структуре классов. Фабрика вместо использования только класса Saiyan, из которого генерируется новый Saiyan, создаст простое наследование (жесткую композицию) из этого класса Saiyan, в результате чего появляются два новых класса, RealSaiyan и NullSaiyan, которые преобразуют класс Saiyan в абстрактный класс.

Класс Saiyan теперь определяет методы, которые должны реализовывать все производные классы. Логика Saiyan будет реализована в классе RealSaiyan, в то время как логика объектов, которые не найдены(null) будет реализована в классе NullSaiyan.

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

Код фабрики, которая возвращает два вида объектов, выглядит следующим образом:

Код AbstractSaiyan следующий:

Наконец, код каждого конкретного класса выглядит следующим образом:

Я создал несколько npm скриптов, которые запускают примеры кода, проиллюстрированные здесь.

npm run example1-problem
npm run example1-solution-1

Заключение

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

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

Не стоит глупо использовать шаблоны в любой непонятной ситуации. Главное — это уметь распознать проблему, которую может решить конкретный шаблон, и когда вы можете или не можете его использовать.

Дополнительные ссылки

· Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, & Vlissides, Addison Wesley, 1995

· Sourcemaking.com

· The Null-Object Pattern — Wikipedia.

· https://www.geeksforgeeks.org/null-object-design-pattern/

20+