Відокрéмлення клáсу (англ. Extract class) — прийом рефакторингу, що полягає в розділенні класу та виділенні полів і методів, що підтримують окремий функціонал, у новостворений клас з метою полегшення роботи з даними.

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

ред.

У ході написання програми класи можуть отримати масу додаткових обов'язків.

Переваги здійснення відокремлення класу

ред.
  1. Функція такого рефакторингу — сприяння дотримання принципу єдиного обов'язку класу. В результаті код класів стає більш чистим і зрозумілим.
  2. Класи з єдиним обов'язком більш стійкі до змін. Наприклад, якщо є клас, який відповідає за 10 різних речей, і до нього потрібно внести певні зміни, то змінюючи один елемент є ризик пошкодити інші.

Недоліки

ред.

Якщо часто проводити такий рефакторинг, потрібно буде вдаватися до антирефакторингу — вбудовування класу.

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

ред.
  1. Створити новий клас, який міститиме виділену функціональність.
  2. Створити зв'язок між старим і новим класом. Найкраще, якщо цей зв'язок буде одностороннім; при цьому другий клас можна буде без проблем використати повторно. З іншого боку, за необхідністю, завжди можна створити двосторонній зв'язок.
  3. Використати переміщення поля і переміщення методу для кожного поля і методу, які потрібно перенести в новий клас. Для методів слід розпочинати з приватних, таким чином знижуючи імовірність допустити масу помилок. Задля полегшення процесу виправлення помилок потрібно проводити тестування після кожного переміщення, аби не отримати багато помилок в кінці роботи.
  4. Після переміщення потрібно подивитись на отримані класи. Можливо, класи потрібно буде перейменувати, зважаючи на їх нові обов'язки. Також варто перевірити, чи можна позбавитися від двостороннього зв'язку між класами, якщо він з'явився.
  5. Важливим моментом є доступність класу ззовні: можна повністю сховати клас, зробивши приватним, і в той же час управляти його полями із старого класу, або зробити публічним, надавши клієнтові можливість безпосередньо міняти значення. Рішення залежить від того, наскільки безпечні для поведінки старого класу будуть несподівані прямі зміни значень в новому класі.

Приклад

ред.

Початковий клас
C#:

class Person
{
      public string getName() 
      {
          return _name;
      }
      public string GetTelephoneNumber() 
      {
          return ("(" + _officeAreaCode + ") " + _officeNumber);
      }
      string getOfficeAreaCode() 
      {
          return _officeAreaCode;
      }
      void GetOfficeAreaCode(string arg) 
      {
          _officeAreaCode = arg;
      }
      string getOfficeNumber() 
      {
          return _officeNumber;
      }
      void SetOfficeNumber(string arg) 
      {
          _officeNumber = arg;
      }
 
      private string _name;
      private string _officeAreaCode;
      private string _officeNumber;

У такому випадку власника телефонного номера можна виділити в окремий клас
C#:

class TelephoneNumber 
{
}

Потім потрібно зробити посилання з персони до телефонного номера
C#:

class Person
{
    ...
    private TelephoneNumber _officeTelephone = new TelephoneNumber();
    ...
}

Тепер потрібно виконати переміщення поля
C#:

class TelephoneNumber 
{
    string getAreaCode() 
    {
        return _areaCode;
    }
    void setAreaCode(string arg) 
    {
        _areaCode = arg;
    }
    private string _areaCode;
}

class Person
{
    public string GetTelephoneNumber() 
    {
        return ("(" + getOfficeAreaCode() + ") " + _officeNumber);
    }
    string getOfficeAreaCode() 
    {
        return _officeTelephone.getAreaCode();
    }
    void setOfficeAreaCode(string arg) 
    {
        _officeTelephone.setAreaCode(arg);
    }
}

Та переміщення методу
C#:

class Person
{
      public string getName() 
      {
          return _name;
      }
      public string getTelephoneNumber()
      {
          return _officeTelephone.getTelephoneNumber();
      }
      TelephoneNumber getOfficeTelephone() 
      {
          return _officeTelephone;
      }
 
      private String _name;
      private TelephoneNumber _officeTelephone = new TelephoneNumber();
}
    class TelephoneNumber
{
      public string GetTelephoneNumber()
      {
          return ("(" + _areaCode + ") " + _number);
      }
      string getAreaCode() 
      {
          return _areaCode;
      }
      void SetAreaCode(string arg) 
      {
          _areaCode = arg;
      }
      string getNumber() 
      {
          return _number;
      }
      void SetNumber(string arg) 
      {
          _number = arg;
      }
      private string _number;
      private string _areaCode;
}

Антирефакторинг

ред.
  • Вбудовування класу

Схожі рефакторинги

ред.
  • Витягання підкласу
  • Заміна простого поля об'єктом

Бореться з запахом

ред.

Посилання

ред.