Null object (шаблон проєктування)
В об'єктно-орієнтованому програмуванні, Null Object або нульовий об'єкт — це об'єкт з визначеною нейтральною (англ. null) поведінкою. Шаблон проєктування Null Object описує використання цих об'єктів та їх поведінки або відсутності таких. Вперше цей шаблон було описано в серії книжок Pattern Languages of Program Design.[1]
Мотивація
У більшості об'єктно-орієнтованих мов програмування, таких як Java або C#, посилання можуть приймати значення null. Це значення говорить про те, що за посиланням не існує реального об'єкту і виклик методів може призвести до численних помилок та краху системи.
Традиційно, методи, що повертають об'єкти (наприклад, методи твірних шаблонів) у випадку невдачі (наприклад, пошук об'єкту в порожній колекції) повинні повертати коректне значення посилання — null
.
Обробка цього результату може бути реалізована простою перевіркою посилання на null
перед викликом методів.
// Приклад на Java class Animal { public void makeSound() { System.out.write("Гав-гав!"); } } ... Animal pet = animalsProvider.getAnimal(); if (pet != null) { pet.makeSound(); } else { /* обробка помилки */ } ...
Проблему обробки null
-посилань і вирішує шаблон Null object.
Треба відзначити, що, наприклад, у мові програмування Objective-C використовується інший підхід до цієї проблеми: всі методи, що викликаються через nil
-посилання (nil
близька за сенсом до null частина Objective-C) повертають також nil
.
Опис
Замість використання null
-посилання для відображення відсутності об'єкту треба використовувати спеціальний об'єкт, який реалізує потрібні інтерфейси, але не має поведінки, тобто має пусті методи. Перевагою цього підходу є те, що реалізація нульового об'єкту завжди передбачувана: вона нічого не робить, а тому не має таких побічних ефектів, які має null
-посилання.
Наприклад, функція повинна прочитати список файлів в директорії та виконати якісь дії з кожним. В випадку, якщо директорія порожня, можна повернути null
або згенерувати виняток. Таким чином, код, що очікує список файлів повинен перевіряти, чи дійсно він отримав список, а це в свою чергу ускладнює структуру програми.
Якщо повертати нульовий об'єкт (наприклад, порожній список), то зникає необхідність перевірок. Викликаючий код може ітерувати список файлів, не зважаючи на можливі помилки і не виконувати ніяких дій при цьому.
Хоча, залишається можливим робити дещо змінену перевірку: «чи дорівнює результат нульовому об'єкту?», та діяти далі за логікою програми.
Також, цей шаблон може використовуватися як заглушка при тестуванні.
Відношення до інших шаблонів
Шаблон може розглядатися як спеціальний випадок шаблонів Стан (англ. State) та Стратегія (англ. Strategy). Він не написаний в книзі GoF, а був запропонований Мартіном Фаулером[2] та Джошуа Керієвскі[3].
В мовах
C++
Мова зі статично типізованими посиланнями на об'єкти показує, як нульовий об'єкт стає складнішим шаблоном.
class animal { public: virtual void make_sound() = 0; }; class dog : public animal { void make_sound() { cout << "woof!" << endl; } }; class null_animal : public animal { void make_sound() { } };
Існують ситуації, коли вказівник або посилання на об'єкт класу animal
необхідний, але за ним немає належного об'єкту. Посилання не може бути null
, коли вказівник animal *
— може: такий вказівник можна використовувати для зберігання об'єкту, але неможливо безпосередньо викликати методи. Код a->make_sound()
викличе помилку undefined behavior (укр. невизначена поведінка), якщо a
буде null
-вказівником.
Шаблон Null object вирішує цю проблему, впроваджуючи спеціальний клас null_animal
, який може бути інстанційований та викорстовуватись з вказівником чи посиланням на animal
.
C#
С# є мовою, в якій шаблон Null object можна реалізувати канонічно. В нижче наведеному прикладі, нульовий об'єкт реалізує очікувану порожню поведінку та попереджає проблеми часу виконання програми, а сам виняток Null Reference Exception, який буде згенеровано, якщо відбудеться використання null
-посилання.
using System; interface Animal { void MakeSound(); } class Dog : Animal { public void MakeSound() { Console.WriteLine("Woof!"); } } class NullAnimal : Animal { public void MakeSound() { } } static class Program { static void Main() { Animal dog = new Dog(); dog.MakeSound(); // Animal unknown = new NullAnimal(); //<< замінює: Animal unknown = null; unknown.MakeSound(); // нічого не відбувається } }
Smalltalk
За принципом Smalltalk, «все є об'єктом», відсутність якогось об'єкту моделюється об'єктом nil
. В GNU Smalltalk, наприклад, nil
є об'єктом класу UndefinedObject
, прямого нащадка Object
.
Будь-яка операція, яка не змогла повернути потрібний об'єкт може повернути nil
замість нього, таким чином, попереджаючи повернення «об'єкту нема». Цей підхід спрощує програму, позбавляючи від null-посилань, null-вказівників.
Ruby
В Ruby нульовий об’єкт це повноцінний об’єкт класу NilClass. Він специфічним чином працює в логічних виразах: він приймає значення false. Ruby дозволяє розширювати цей клас, тому, якщо ви хочете таку ж поведінку, як у в Objective-C, тоді використовуйте щось на кшталт цього:
class NilClass def method_missing(*) return nil end end
Див. також
Посилання
- Jeffrey Walkers' account of the Null Object Pattern [Архівовано 6 червня 2012 у Wayback Machine.]
- Martin Fowlers' description of Special Case, a slightly more general pattern [Архівовано 16 травня 2012 у Wayback Machine.]
- Null Object Pattern Revisited [Архівовано 10 квітня 2010 у Wayback Machine.]
- Introduce Null Object refactoring [Архівовано 30 травня 2012 у Wayback Machine.]
- SourceMaking Tutorial [Архівовано 15 червня 2012 у Wayback Machine.]
Примітки
- ↑ Woolf, Bobby (1998). Null Object. У Martin, Robert; Riehle, Dirk; Buschmann, Frank (ред.). Pattern Languages of Program Design 3. Addison-Wesley.
- ↑ Fowler, Martin (1999). Refactoring. Improving the Design of Existing Code. Addison-Wesley. ISBN 0-201-48567-2.
- ↑ Kerievsky, Joshua (2004). Refactoring To Patterns. Addison-Wesley. ISBN 0-321-21335-1.
- п
- о
- р
конкурентного
програмування
Базові шаблони | Клієнт-серверна архітектура • Front end та back end • Триярусна архітектура • Гексагональна архітектура (Архітектура портів та адаптерів) • Відокремлений інтерфейс (Separated Interface) • Сервісно-орієнтована архітектура • Мікросервіси • Push/Pull модель |
---|---|
Шаблони об'єктного структурування | |
Шаблони представлення | |
Шаблони предметно-орієнтованого проєктування | Rich/Anemic модель • DDD • Інваріант • Entity • Value Object • Aggregate Root • DTO • Repository • Патерн сервісного рівня (Service Layer) • Фабричний метод (Factory Method) • Специфікація |
Шаблони сервісно-орієнтованої архітектури | Сервісно-орієнтована архітектура • Мікросервіси • Шлюз (Gateway) • Публікація-підписка • Інтеграційна шина даних • Event Sourcing • Двофазна транзакція • Saga • Circuit breaker • Асинхронна http модель |
корпоративних
програмних
додатків
Базові шаблони | Об'єкт-значення (Value Object) • Гроші (Money) • Особливий випадок (Special Case) • Супертип рівня (Layer Supertype) • Відокремлений інтерфейс (Separated Interface) • Шлюз (Gateway) • Розподільник (Mapper) • Реєстр (Registry) • Плагін (Plugin) • Набір записів (Record Set) • Заглушка сервісу (Service Stub) |
---|---|
Шаблони логіки домену | Сценарій транзакції (Transaction script) • Модель предметної області (Domain model) • Обробник таблиці (Table Module) • Патерн сервісного рівня (Service Layer) |
Шаблони сховища даних | Активний запис (Active Record) • Шлюз до даних таблиці (Table Data Gateway) • Шлюз до даних запису (Row Data Gateway) • Відображення даних (Data Mapper) |
Шаблони об'єктно-реляційної поведінки | Одиниця роботи (Unit Of Work) • Мапа відповідності (Identity Map) • Ліниве завантажування (Lazy Load) |
Шаблони об'єктно-реляційного структурування | Поле первинного ключа (Identity Field) • Розмітка зовнішніх ключів (Foreign Key Mapping) • Розмітка зв'язків таблиць (Association Table Mapping) • Відображення залежних об'єктів (Dependent Mapping) • Об'єднане значення (Embedded Value) • Серіалізований великий об'єкт (Serialized LOB) • Наслідування з однією таблицею (Single Table Inheritance) • Наслідування з таблицею для кожного класу (Class Table Inheritance) • Наслідування з таблицею для кожного конкретного класу (Concrete Table Inheritance) • Відображення із наслідуванням (Inheritance Mappers) • База даних звітності |
Шаблони обробки об'єктно-реляційних метаданих | Відображення на основі метаданих (Metadata Mapping) • Об'єкт-запит (Query Object) • Сховище (Repository) |
Шаблони вебпредставлення | Модель-вид-контролер (Model View Controller) • Контролер сторінки (Page Controller) • Єдина точка входу (Front controller) • Контролер аплікації (Application Controller) • Шаблонізатор (Template View) • Перетворювач (Transform View) • Двокрокова шаблонізація (Two Step View) |
Шаблони розподіленої обробки даних | Фасад (Remote Facade) • Об'єкт передачі даних (Data Transfer Object) |
Шаблони локального конкурентного програмування | Оптимістичне блокування (Optimistic Offline Lock) • Песимістичне блокування (Pessimistic Offline Lock) • Блокування із низьким рівнем деталізації (Coarse Grained Lock) • Неявне блокування (Implicit Lock) |
Шаблони збереження стану сеансу | Збереження стану сеансу на стороні клієнта (Client Session State) • Збереження стану сеансу на стороні сервера (Server Session State) • Збереження стану сеансу в базі даних (Database Session State) |