Також відомий як Introduce Null Object

Рефакторинг Введення Null-об'єкту

Проблема

Через те, що деякі методи повертають `null` замість реальних об'єктів, у вас в коді присутня безліч перевірок на `null`.

Рішення

Замість `null` повертайте Null-об'єкт, який надає поведінку за умовчанням.
До
if (customer == null) {
  plan = BillingPlan.basic();
}
else {
  plan = customer.getPlan();
}
Після
class NullCustomer extends Customer {
  boolean isNull() {
    return true;
  }
  Plan getPlan() {
    return new NullPlan();
  }
  // Some other NULL functionality.
}

// Replace null values with Null-object.
customer = (order.customer != null) ?
  order.customer : new NullCustomer();

// Use Null-object as if it's normal subclass.
plan = customer.getPlan();
До
if (customer == null) 
{
  plan = BillingPlan.Basic();
}
else 
{
  plan = customer.GetPlan();
}
Після
public sealed class NullCustomer: Customer 
{
  public override bool IsNull 
  {
    get { return true; }
  }
  
  public override Plan GetPlan() 
  {
    return new NullPlan();
  }
  // Some other NULL functionality.
}

// Replace null values with Null-object.
customer = order.customer ?? new NullCustomer();

// Use Null-object as if it's normal subclass.
plan = customer.GetPlan();
До
if ($customer == null)
  $plan = BillingPlan::basic();
else
  $plan = $customer->getPlan();
Після
class NullCustomer extends Customer {
  function isNull() {
    return true;
  }
  function getPlan() {
    return new NullPlan();
  }
  // Some other NULL functionality.
}

// Replace null values with Null-object.
$customer = ($order->customer != null) ?
  $order->customer :
  new NullCustomer();

// Use Null-object as if it's normal subclass.
$plan = $customer->getPlan();
До
if customer == None:
    plan = BillingPlan.basic()
else:
    plan = customer.getPlan()
Після
class NullCustomer(Customer):

    def isNull(self):
        return True
    
    def getPlan(self):
        return self.NullPlan()
        
    # Some other NULL functionality.

# Replace null values with Null-object.
customer = order.customer if order.customer != None else NullCustomer()

# Use Null-object as if it's normal subclass.
plan = customer.getPlan()

Причини рефакторингу

Безліч перевірок на null ускладнюють і засмічують код.

Недоліки

  • За відмову від умовних операторів ви розплачуєтесь ще одним новим класом.

Порядок рефакторингу

  1. З потрібного вам класу створіть підклас, який виконуватиме роль Null-об'єкту.

  2. У обох класах створіть метод isNull(), який повертатиме true для Null-об'єкту і false для реального класу.

  3. Знайдіть усі місця, де код може повернути null замість реального об'єкту. Змініть цей код так, щоб він повертав Null-об'єкт.

  4. Знайдіть усі місця, де змінні реального класу порівнюються з null. Замініть такі перевірки викликом методу isNull().

    • Якщо в цих умовних операторах при значенні, не рівному `null`, виконуються методи початкового класу, перевизначить ці методи в Null-класі і вставте туди код з частини умови else. Після цього умовний оператор можна буде взагалі видалити, а різна поведінка здійснюватиметься за рахунок поліморфізму.
    • Якщо не все так просто, і методи перевизначити не виходить, подивіться, чи можна просто виділити операції, які повинні були виконуватися при значенні рівному null в нові методи Null-об'єкту. Викликайте ці методи замість старого коду в else як операції за умовчанням.

Замучились читати?

Збігайте за подушкою, в нас тут контенту приблизно на 7 годин читання.

Або спробуйте наш новий інтерактивний курс з рефакторингу. Він більш інформативний та набагато цікавіший за банальний тест.

Дізнатися більше...

Живий приклад

Перший раз у нас? Нічого страшного!

У нас тут все просто – цей інтерактивний приклад дуже схожий на відео (хоча й виглядає набагато крутіше).

  1. Натискаєте велику кнопку "Почати", а далі слідуєте усім підказкам.
  2. Ви можете перемотувати програвання, використовуючи стрілки зліва.
  3. Окрім того, ви можете подивитися різницю між стартовим та отриманим кодом, натиснувши кнопку ока ().
  4. Кнопка компіляції та тестування () дає змогу перевірити код на наявність помилок.