Null object (шаблон проєктування)

Матеріал з Вікіпедії — вільної енциклопедії.
Версія від 18:43, 22 вересня 2023, створена A.sav (обговорення | внесок) (clean up, replaced: воий → овий за допомогою AWB)
(різн.) ← Попередня версія | Поточна версія (різн.) | Новіша версія → (різн.)
Перейти до навігації Перейти до пошуку

В об'єктно-орієнтованому програмуванні, 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].

В мовах

[ред. | ред. код]

Мова зі статично типізованими посиланнями на об'єкти показує, як нульовий об'єкт стає складнішим шаблоном.

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.

С# є мовою, в якій шаблон 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 нульовий об’єкт це повноцінний об’єкт класу NilClass. Він специфічним чином працює в логічних виразах: він приймає значення false. Ruby дозволяє розширювати цей клас, тому, якщо ви хочете таку ж поведінку, як у в Objective-C, тоді використовуйте щось на кшталт цього:

  class NilClass
    def method_missing(*)
      return nil
    end
  end

Див. також

[ред. | ред. код]

Посилання

[ред. | ред. код]

Примітки

[ред. | ред. код]
  1. Woolf, Bobby (1998). Null Object. У Martin, Robert; Riehle, Dirk; Buschmann, Frank (ред.). Pattern Languages of Program Design 3. Addison-Wesley.
  2. Fowler, Martin (1999). Refactoring. Improving the Design of Existing Code. Addison-Wesley. ISBN 0-201-48567-2.
  3. Kerievsky, Joshua (2004). Refactoring To Patterns. Addison-Wesley. ISBN 0-321-21335-1.