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.