Найбільш зручний для людини спосіб замовлення визначень методів класів?


38

У будь-якому даному визначенні класу я бачив визначення методів, упорядковані різними способами: алфавітним, хронологічним, заснованим на найбільш поширеному використанні, алфавітно згрупованим за видимістю, алфавітному з getters та setters, згрупованими разом і т. Д. Коли я починаю писати новий клас, Я схильний просто вводити все, а потім змінювати порядок, коли закінчую писати весь клас. У цій записці у мене є три питання:

  1. Чи має значення замовлення?
  2. Чи є "найкраще" замовлення?
  3. Я здогадуюсь, що немає, тож які плюси і мінуси різних стратегій впорядкування?

1
Ви дійсно не очікуєте, що люди виріжуть / вставляють код, щоб просто змінити методи. Якщо IDE робить це автоматично, то добре. Інакше це важко виконати.
Реакційний

Для уточнення, моє питання стосується читабельності / зручності використання, а не синтаксису.
Джоннтрон

Відповіді:


52

У деяких мовах програмування значення має значення, оскільки ви не можете використовувати речі до того моменту, коли вони будуть оголошені. Але забороняючи це, для більшості мов це не має значення для компілятора. Так що, ви залишаєте це питанням для людей.

Моя улюблена цитата Мартіна Фаулера така: Any fool can write code that a computer can understand. Good programmers write code that humans can understand.Тому я б сказав, що впорядкованість вашого класу повинна залежати від того, що полегшує людині розуміння.

Я особисто віддаю перевагу зниженню лікування, яку Боб Мартін надає у своїй Clean Codeкнизі. Змінні учасника вгорі класу, потім конструктори, потім усі інші методи. І ви наказуєте методам бути близькими разом із тим, як вони використовуються в класі (а не довільно ставити все публічне, а потім приватне, яке захищене). Він називає це мінімізацією "вертикальної відстані" чи чогось подібного (наразі не майте книгу на мені).

Редагувати:

Основна ідея "вертикальної відстані" полягає в тому, що ви хочете уникати примушування людей стрибати навколо вашого вихідного коду, щоб зрозуміти це. Якщо речі пов'язані, вони повинні бути ближче один до одного. Неспоріднені речі можуть бути далі один від одного.

Глава 5 Чистого кодексу (чудова книга, btw) детально описує те, як містер Мартін пропонує замовити код. Він припускає, що читання коду має діяти так, як читати газетну статтю: деталі на високому рівні спочатку (вгорі), і ви отримуєте більше деталей під час читання. Він каже: "Якщо одна функція викликає іншу, вони повинні бути вертикально близькими, а абонент повинен бути вище, якщо це можливо." Крім того, пов'язані поняття повинні бути близькими.

Ось ось надуманий приклад, який багато в чому поганий (поганий дизайн OO; ніколи не використовуйте doubleгроші), але ілюструє ідею:

public class Employee {
  ...
  public String getEmployeeId() { return employeeId; }
  public String getFirstName() { return firstName; }
  public String getLastName() { return lastName; }

  public double calculatePaycheck() {
    double pay = getSalary() / PAY_PERIODS_PER_YEAR;
    if (isEligibleForBonus()) {
      pay += calculateBonus();
    }
    return pay;
  }

  private double getSalary() { ... }

  private boolean isEligibleForBonus() {
    return (isFullTimeEmployee() && didCompleteBonusObjectives());
  }

  public boolean isFullTimeEmployee() { ... }
  private boolean didCompleteBonusObjectives() { ... }
  private double calculateBonus() { ... }
}

Методи впорядковані, щоб вони були близькими до тих, що їх викликають, працюючи вниз зверху. Якби ми поставили всі privateметоди нижче publicтих, то вам доведеться робити більше стрибків, щоб слідкувати за потоком програми.

getFirstNameі getLastNameвони концептуально пов'язані (і, getEmployeeIdмабуть, занадто), тому вони близькі між собою. Ми могли перемістити їх усіх донизу, але ми не хотіли б бачити getFirstNameвгорі і getLastNameвнизу.

Сподіваємось, це дає вам основну думку. Якщо вас цікавить така штука, я настійно рекомендую прочитати Clean Code.


Мені потрібно знати, як потрібно розміщувати сеттери та одержувачі змінних екземплярів. Чи повинен він надходити відразу після конструктора класів або внизу класу?
srinivas

Особисто мені вони подобаються вгорі після конструктора. Але це насправді не має значення; Я рекомендую послідовність у вашому проекті та вашій команді як хороший спосіб прийняти рішення.
Аллан

Не варто calculateBonus()прийти раніше isFullTimeEmployee()і didCompleteBonusObjectives()?
winklerrr

@winklerrr Я бачу аргумент для цього. Я помістив їх туди , де я зробив , тому isFullTimeEmployeeі didCompleteBonusObjectivesвикористовуються isEligibleForBonusтаким чином , вони повинні бути вертикально близько до нього. Але ви потенційно можете рухатись calculateBonusвгору, щоб поставити його ближче до того, де його називають. На жаль, якщо у вас є функції виклику функцій, врешті-решт у вас виникають проблеми (наприклад, одні спільні функції, викликані багатьма іншими), де немає ідеального впорядкування. Тоді це залишається за вашим найкращим рішенням. Я рекомендую зберігати заняття та функції невеликими, щоб зменшити ці проблеми.
Аллан

2

Я зазвичай замовляю свої методи за відношенням та порядком використання.

Візьміть мережевий клас, я збираюся згрупувати всі методи TCP, потім всі методи UDP разом. Всередині TCP я мав би метод настройки як перший, можливо, надішліть задане повідомлення другим і закрийте tcp socket як третій.

Очевидно, що не всі класи будуть відповідати цій схемі, але це мій загальний робочий процес.

Я роблю це таким чином для налагодження більше, ніж будь-що інше, коли у мене є проблема, і я хочу перейти до методу, я не думаю, як це написано, я думаю, за що це відповідає, і переходжу до цього розділу.

Цей спосіб, зокрема, має сенс для перегляду / використання вашого коду стороною стороною, який буде згруповано, і буде дотримуватися порядку його використання, тому код, який вони будуть записувати разом із вашим класом, буде відповідати тій же структурі, що і клас.

Що стосується цього?

для читабельності, безумовно.

окрім цього не справді, лише у тих випадках, коли певними мовами ви не можете викликати метод, якщо не визначено вище, де його називають і т.д.


0

В ідеалі ваші заняття такі малі, що це не має значення. Якщо у вас є лише десяток методів, і ваш редактор або IDE підтримує складання, у вас не виникає проблем, оскільки весь список методів вміщується в 12 рядків.

Якщо цього не встановити, то відмінність вищого рівня повинна бути публічною та приватною. Спочатку перерахуйте загальнодоступні методи: це те, що люди будуть шукати найбільше, оскільки вони визначають спосіб інтерфейсу вашого класу з іншою базою кодів.

Потім всередині кожного з них має сенс групувати методи за функціональністю: конструктори та деструктори в одному блоці, геттери / сеттери в іншому, оператори перевантажують, статичні методи та іншу частину групи. У мові C ++ я хотів би operator=наближатися до конструкторів, тому що це тісно пов'язане з конструктором копіювання, а також тому, що я хочу мати можливість швидко визначити, чи всі (чи ні) ctor за замовчуванням, ctor копії, operator = і dtor існують.


-1

тл; д-р

Тільки якщо мова, якою ви працюєте, вимагає конкретного замовлення. Крім цього, замовлення залежить від вас. Виберіть систему, яка є послідовною та має сенс, і намагайтеся дотримуватися її якомога більше.


1. Чи має значення замовлення?

Тільки якщо мова, якою ви працюєте, повинна мати функцію, визначену раніше у файлі, ніж там, де вона викликана, як, наприклад, у цьому прикладі:

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

ви отримаєте помилку, тому що телефонуєте funcB() перш ніж її використовувати. Я думаю, це проблема в PL / SQL і, можливо, також і в C, але ви можете мати попередні декларації, такі як:

void funcB();

void funcA()
{
   funcB();
}

void funcB()
{
   //do something interesting...
}

Це єдина ситуація, про яку я можу подумати, де, якщо замовлення "неправильне", ви навіть не зможете скласти.

В іншому випадку ви завжди можете їх замовити як завгодно. Напевно, ви навіть можете написати інструмент для цього (якщо ви не можете його знайти там).

2. Чи є "найкраще" замовлення?

Якщо мова / середовище не має вимог до замовлення, то "найкращий" порядок - це той, який найкраще підходить для вас . Мені подобається, що всі геттери / сетери разом, зазвичай на початку класу (але після конструкторів / статичних ініціалізаторів), а потім приватними методами, потім захищеними, потім загальнодоступними. У кожній групі на основі сфери зазвичай немає впорядкування, хоча я перевантажений методом, який я намагаюся зберігати разом, за кількістю параметрів. Я також намагаюся утримувати методи з відповідними функціональними можливостями разом, хоча іноді мені доводиться порушувати своє замовлення на основі сфери; а іноді намагаються підтримувати впорядкування, засноване на масштабах, розбиває групову функціональність. І мій IDE може надати мені алфавітний контур, так що це теж добре. ;)

Деякі мови, як-от C #, мають можливість групувати код у "регіонах" , які не впливають на компіляцію, але можуть полегшити збереження пов'язаних функцій разом, а потім приховати / відобразити їх за допомогою IDE. Як зазначав MainMa , є деякі, хто вважає це поганою практикою. Я бачив добрі та погані приклади регіонів, які використовуються таким чином, тому якщо ви збираєтеся йти цим шляхом, будьте обережні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.