Коли виправдані геттери та сетери


175

Геттерів та сетерів часто критикують як неналежне ОО. З іншого боку, більшість кодів, що я бачив, мають великі геттери та сетери.

Коли виправдані геттери та сетери? Ви намагаєтесь уникати їх використання? Чи загалом вони зловживають?

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

Джерела критики щодо геттера / сетера (деякі з коментарів, щоб покращити видимість):

Висловити критику просто: Геттери та сетери дозволяють маніпулювати внутрішнім станом об'єктів ззовні об'єкта. Це порушує інкапсуляцію. Тільки сам об’єкт повинен дбати про свій внутрішній стан.

І приклад Процедурна версія коду.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Версія коду мутатора:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

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

Питання раніше обговорювалося в "Переповнення стека":


21
Getters and setters are often criticized as being not proper OO- Цитування, будь ласка.
Роберт Харві


45
@Job, чому ж він має код? Це гарне запитання і пахи священної війни, якщо сприймати його серйозно.
Пітер Тернер

2
@Winston Ewert: "... питання полягає в тому, чи потрібно взагалі отримувати доступ до даних всередині класу.": Ну, ніхто не змушує вас реалізовувати геттери та сетери для всіх змінних членів, ви реалізуєте лише ті, що вам потрібні. Ось чому ви використовуєте getters та setters замість загальнодоступних змінних.
Джорджіо

2
@Winston Ewert: Я не бачу, як відсутність геттерів / сетерів вирішить цю проблему: повинен бути спосіб отримати доступ до кожної частини даних, інакше вона марна; завдання гарного дизайну вирішити, яка частина даних доступна, до якої частини коду. Якщо хтось є поганим дизайнером, він чи вона буде такою з геттерами та сетерами. Всього мої 2 копійки.
Джорджіо

Відповіді:


162

Наявність геттерів та сетерів само по собі не порушує інкапсуляцію. Що означає порушення інкапсуляції, це автоматично додавання геттера та сеттера для кожного члена даних (у кожному полі , в java lingo), не замислюючись про це. Хоча це краще, ніж оприлюднювати всіх учасників даних, це лише невеликий крок.

Сенс інкапсуляції полягає не в тому, що ви не повинні бути в змозі знати або змінити стан об'єкта за межами об'єкта, а в тому, що ви повинні мати розумну політику для цього.

  • Деякі учасники даних можуть бути повністю внутрішніми для об'єкта і не повинні мати ні геттерів, ні сетерів.

  • Деякі учасники даних мають бути лише для читання, тому їм можуть знадобитися геттери, але не сетери.

  • Деякі члени даних, можливо, повинні відповідати один одному. У такому випадку ви б не надавали сетер для кожного, а єдиний метод їх встановлення одночасно, щоб ви могли перевірити значення на узгодженість.

  • Деякі учасники даних можуть потребувати лише певного зміни, таких як збільшення або зменшення на певну кількість. У цьому випадку ви б надали increment()та / або decrement()метод, а не сеттер.

  • Але інших, можливо, потрібно буде читати і писати, і вони матимуть і геттер, і сеттер.

Розглянемо приклад а class Person. Скажімо, у людини є ім’я, номер соціального страхування та вік. Скажімо, ми не дозволяємо людям ніколи змінювати свої імена чи номери соціального страхування. Однак вік людини слід збільшувати на 1 щороку. У цьому випадку ви б надали конструктор, який ініціалізував би ім'я та SSN до заданих значень і який ініціалізував би вік до 0. Ви також надали метод incrementAge(), який збільшить вік на 1. Ви також надали б загони для всіх трьох. У цьому випадку ніяких сетерів не потрібно.

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

Зараз скажімо, у людини теж є зарплата. І люди можуть змінювати роботу за бажанням, а це означає, що їхня зарплата також зміниться. Для моделювання цієї ситуації у нас немає іншого способу, окрім як запропонувати setSalary()метод! Дозволяти змінювати зарплату за бажанням - це цілком розумна політика в цьому випадку.

До речі, в вашому прикладі, я дав би клас і методи, замість і . Тоді у вас все-таки буде інкапсуляція.FridgeputCheese()takeCheese()get_cheese()set_cheese()


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}

4
Будь ласка, не сприймайте мій приклад холодильника надто серйозно. :) Справжній холодильник повинен бути контейнерним об'єктом, я б очікував, що він вміє тримати предмети, не переживаючи, що саме вони є. IE це було б як ArrayList. Що стосується того, що робить його Холодильником, скажімо, він серіалізує об’єкти на диск, коли вони зберігаються, щоб вони пережили збій системи. Гаразд. Досить серйозно поставитися до мого прикладу холодильника.
Вінстон Еверт

6
Але чи не повинен холодильник знати терміни придатності, щоб він міг сказати вам, що сир пішов погано і його слід викидати? :) Гаразд, ми повинні зупинити це! :)
Діма

10
Досить цікаве обговорення " Холодильної логіки" ви тут
проходили

35
Ха-ха, я хотів би побачити рекламу для цього: "Це абсолютно новий вид холодильника: він кидає на вас речі, намагаючись схопити щось, чого немає! Цей спосіб ви спробуєте це лише один раз! набиваючи холодильник, і нехай холодильник турбується про протиправну поведінку! "
габлін

42
У прикладі все неправильно. Не повинно бути вікового поля, ані методу setAge (). Вік - це функція дати народження людини порівняно з певним моментом часу. Хоча, здавалося б, тривіально, це саме те, що неправильно в сучасній практиці повної змінності об'єктів та інших поганих конструкцій, як це бачать методи get / set для приватних полів, а не ретельно враховуючи, що насправді є змінним, що таке поле, який об'єкт слід знати про його поведінку та про те, які зміни стану фактично дійсні (які методи setX повністю руйнують).
Даррелл Тейг

41

Основна причина отримання та набору ятрів на Java дуже проста:

  • В інтерфейсі можна вказати лише методи , а не поля.

Отже, якщо ви хочете дозволити поле проходити через інтерфейс, вам знадобиться читач і метод запису. Традиційно їх називають getX та setX для поля x.


44
Це обмеження мови. Справжнє питання полягає в тому, чи варто дозволяти іншим об'єктам маніпулювати цим станом.
Вінстон Еверт

3
@ Peter Turner, явно ніщо не заважає мові мати інтерфейси, що містять властивості. Під обкладинками, які цілком можуть бути реалізовані як getter / setter, але було б досить просто додати підтримку визначенням інтерфейсу для властивостей.
Вінстон Еверт

2
@Winston, вам, зрештою, потрібно буде дозволити класам передавати інформацію між собою, щоб насправді виконати роботу. Що б ви запропонували замість цього?

6
Об'єкт повинен забезпечити для нього внутрішній інтерфейс більш високого рівня. Геттери та сетери, як правило, є інтерфейсом низького рівня. Наприклад, припустимо, у вас є клас, який реалізує двійкове дерево. Ви можете мати такі функції, як GetLeft (), GetRight (), GetValue () та GetKey () для навігації по дереву. Але це абсолютно неправильний підхід. Ваш клас бінарного дерева повинен забезпечувати такі операції, як Find, Insert, Delete.
Вінстон Еверт

6
Для іншого прикладу розглянемо шматок тетрису. Шматок тетрису має деякий внутрішній стан, наприклад блок_типу, обертання, положення. У вас можуть бути такі люди, як GetBlockType (), GetRotation (), GetPosition (). Але ти справді не повинен. У вас має бути метод GetSquares (), який повертає список всіх квадратів, зайнятих цим фрагментом. У вас також не повинно бути таких елементів, як SetPosition () або SetRotation (). Натомість у вас повинні бути такі операції, як MoveLeft (), MoveRight (), Rotate (), Fall ().
Вінстон Еверт

20

Від http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

Стиль JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-стиль:

connection.connect("dukie","duke");

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

Ваше питання: коли виправданий геттер / сетер виправданий? Можливо, коли потрібна зміна режиму, або вам потрібно допитати об’єкт для отримання деякої інформації.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Думаючи про це, коли я вперше почав кодувати в C #, я написав багато коду в стилі Javabeans, проілюстрованому вище. Але коли я набув досвіду роботи з мовою, я почав робити більше налаштування членів у конструкторі та використовував методи, що більше нагадували вищевказаний стиль OO.


6
Чому стиль OO "навести всі аргументи як параметри"?

5
Хоча ваш "OO-стиль" чудово підходить для 2 або 3 варіантів, я б вважав за краще стиль JavaBean, коли ви перейдете вище цього. Маючи 20 перевантажених методів для кожної можливої ​​комбінації параметрів, просто виглядає жахливо
TheLQ

1
@TheLQ: C # 4.0 полегшує це, дозволяючи необов'язкові та названі параметри у викликах конструктора та методу.
Роберт Харві

7
@TheLQ Для цього потрібен Builder з вільним синтаксисом, див., Наприклад, Guava MapMaker .
maaartinus

5
@ Thorbjørn Ravn Andersen, я б сказав, "надати всі аргументи як параметри" краще в OO-стилі, ніж "викликати JavaBean setters", тому що сеттер означає, що встановлюється базовий атрибут, який за визначенням розкриває внутрішні деталі. Використання методу connect (..) у прикладі не передбачає такого припущення; можливо, ви встановили атрибути користувача та пароля, можливо, ви цього не зробите. Важливо те, що ви орієнтуєтесь на правильну концепцію ОО: повідомлення "підключити".
Андрес Ф.

13

Коли виправдані геттери та сетери?

Коли поведінка "отримати" та "встановити" насправді відповідає поведінці у вашій моделі, чого практично ніколи .

Будь-яке інше використання - це лише обман, оскільки поведінка ділової сфери не зрозуміла.

Редагувати

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

Насправді це наслідок процесу проектування. Вам не потрібно потрапляти в інтерфейси, інкапсуляцію та шаблони, сперечаючись, чи не порушує ці парадигми, і що таке, чи не добре програмування OO. Єдине, що в кінцевому рахунку має значення, це те, що якщо у вашому домені немає нічого такого, що працює так, додаючи їх у вас, ви не моделюєте свій домен.

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

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

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

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


це, здається, не пропонує нічого суттєвого щодо питань, викладених та пояснених у попередніх 17 відповідях
gnat

1
Інші відповіді говорять про парадигми об'єктів, інтерфейсів та інших мов. Що добре, але це не центральне питання. Геттери і сетери - це не просто погано, оскільки вони порушують деякі умови кодування, а насамперед тому, що навряд чи ви стикаєтеся з такою поведінкою в реальній моделі, яку представляєте. Цей пункт, як правило, втрачається в дискусії про найкращі практики кодування.
Cormac Mulhall

Що ви думаєте, що це інтерфейс для нарахування заробітної плати, якщо це не set/getзарплата?
Вінстон Еверт

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

3
Або кажучи інакше, існує велика різниця між set_salary (new_salary, служитель_id) та санкціонованим_salary_update (службовий_контракт, авторизуючий_манеджер). Цей процес повинен моделювати внутрішній бізнес-процес
Cormac Mulhall

11

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

Очевидно, є винятки. У Java вам може знадобитися використовувати інтерфейси. Стандартна бібліотека Java має зворотні вимоги сумісності, настільки екстремальні, що переважають нормальні показники якості коду. Можливо навіть, що ви насправді маєте справу з легендарним, але рідкісним випадком, коли є хороший шанс, що ви можете пізніше замінити збережене поле на розрахунок на льоту, не інакше порушивши інтерфейс. Але це винятки. Геттери та сетери - це антидіаграма, яка потребує спеціального обґрунтування.


6
Геттер і сетер означають, що атрибут, а не поле, є частиною інтерфейсу. Припустимо, getBirthDate () та SetBirthDate () беруть "yyyy / mm / dd", але поле, що зберігається, - кількість днів з 01.01.1000. Ви можете змінити це на 01.01.0001, і геттер і сетер збережуть інтерфейс однаковим. Тим більше, що "загальнодоступний" означає публічно доступний, змінні ніколи не повинні бути публічними; завжди використовуйте сеттер для перевірки вхідного значення, оновлення будь-яких пов’язаних полів, перетворення за потребою тощо. Спробуйте використовувати геттер у випадку зміни внутрішнього сховища.
Енді Кенфілд

@AndyCanfield, що трохи твердо сказати, що громадськість означає загальнодоступний доступ. Це лише так, якщо ви програмуєте це погано. Якщо у вас є сеттер, і на нього надсилається неправильне значення, тоді ви можете кинути виняток, який ви можете викликати руйнування програми, але він виходить з помилкою. Значення, то ви можете мати це на увазі і перевірити його. У цьому можу сказати "ах, але мені потрібно зробити лише 1 тест, якщо я використовую сеттер", але, можливо, ур власний код дає змінній неправильне значення ... тому сетер може не зменшити свої тести.n за допомогою тестів, ур прог не "
пошкоджений

3

чи поле доступне безпосередньо чи методом, не дуже важливо.

Важливі інваріанти класу (корисні). І щоб їх зберегти, нам іноді потрібно не мати змоги щось змінити ззовні. Напр. якщо у нас є клас Square з роздільною шириною і висотою, зміна одного з них робить його чимось іншим, ніж квадратним. Тому нам потрібен метод changeSide Якби це був прямокутник, ми могли б мати сетери / публічне поле. Але сетертер ніж би перевіряв, чи краще його більший за нуль.

А конкретними мовами (наприклад, java) є причини, чому нам потрібні ці методи (інтерфейси). І ще одна причина методу - сумісність (вихідна і двійкова). Тож простіше їх додати, а потім подумайте, чи вистачить публічного поля.

btw. Мені подобається використовувати прості класи з незмінною цінністю з загальнодоступними полями.


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

2

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


1
Я здивований, що ця відповідь поки що вниз. Геттери і сетери дозволяють вам зробити більше в майбутньому (наприклад, запустити подію, зробити більше перевірки вводу, зробити внутрішню бухгалтерію), не порушуючи існуючий API. Навіть з непублічним кодом, тим менше API, які вам доведеться змінити, тим менше залежностей для оновлення та меншою кількістю введених помилок при здійсненні цих оновлень.
AmadeusDrZaius

0

Мій підхід такий -

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

Якщо це структура POD, я залишаю доступні слоти.

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


0

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

Ось код із другого прикладу, написаного в Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Зауважте, це схоже на перший приклад на Java? Коли геттери та сетери ставляться до громадян першого класу, вони не є клопотом використовувати, і додаткова гнучкість може бути часом справжньою користю - наприклад, ми можемо вирішити повернути для нового сиру холодильник за замовчуванням:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

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


Це прекрасно сказано. Функції будівельника були додані, оскільки ми хочемо створювати непорушні об'єкти, поступово створюючи рецепт: можливо, якщо ви додасте штучний підсолоджувач, вам не потрібно додавати цукор. Використовуючи названі параметри та навіть із перевантаженням, дуже важко будувати один метод, який виконує всі. І тоді, звичайно, Java не має названих параметрів, отже, ще одна причина, по якій люди використовують шаблон Builder.
YoYo

0

Розглянемо Sizeклас, який інкапсулює ширину та висоту. Я можу усунути задачі за допомогою конструктора, але як це допомагає мені намалювати прямокутник Size? Ширина та висота не є внутрішніми даними класу; вони є спільними даними, які повинні бути доступними споживачам програми "Size".

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

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

Жоден параметр методу ніколи не повинен використовуватися без перевірки. Тому лінь писати:

setMyField(int myField){
    this.myField = myField;
}

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

Геттери, сетери, властивості, мутатори, називайте їх, що вам захочеться, але вони необхідні.


Але принаймні у вас є послідовний публічний інтерфейс, до якого ви можете повернутися та ввести правила перевірки, не порушуючи код своїх клієнтів. Це не правда. Додавання правил перевірки далі вниз може дуже "порушити код ваших клієнтів". intНаприклад, візьміть змінну примірника і уявіть, що ви вирішили додати правило перевірки, щоб прийняти лише негативні значення. Проблема полягає в тому, що код вашого клієнта вже може покладатися на можливість встановити його на від'ємне значення ...
jub0bs

0

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

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

Я щойно закінчив писати програму, яка генерує Mazes на Java, і у мене є клас, який представляє "Maze Squares". У мене є дані цього класу, які представляють координати, стіни та булеві і т.д.

У мене повинен бути якийсь спосіб змінити / маніпулювати / отримати доступ до цих даних! Що мені робити без геттерів та сетерів? Java не використовує властивості, а встановлення всіх моїх даних, які є локальними для цього класу, для загальнодоступних - ВИЗНАЧЕНО порушенням інкапсуляції та OO.


6
Ось питання: Якщо ви опублікували всі ці поля загальнодоступними та перестали використовувати геттери та сетери, чим би відрізнявся ваш код?
Вінстон Еверт

Теоретично це не так. Навіщо навіть робити клас у цей момент? Такий же аргумент можна зробити з чим завгодно. Але посада нижче моєї, здається, сказала це більш елегантно. (Геттери та сетери - це спосіб безпечно передати значення об’єкту або отримати значення з цього об’єкта.) Повідомлення продовжується ..
Брайан Харрінгтон

3
Я зазначу: цей плакат врешті погодився зі мною і опублікував ще одну відповідь на це. По суті, використання геттерів та сетерів - це те саме, що безпосередньо маніпулювати даними. Ви дійсно не отримуєте OOness, роблячи це. Для того, щоб бути ОО, вам слід надати дані більш високого рівня.
Вінстон Еверт

4
Я думаю, що найкращим способом його опису було б сказати, що ви повинні проектувати свої інтерфейси зовні, а не зсередини. Інтерфейс, який надається вашим об'єктом, повинен диктуватися тим, як об’єкт використовується, а не як він реалізований. Тому не пишіть гетерів і не дозволяйте зовнішньому об'єкту інтерпретувати дані. Дізнайтеся, на яке запитання їм потрібно відповісти, і напишіть метод, який відповідає на це питання. Не дозволяйте зовнішнім об'єктам змінювати стан сетером, з’ясовуйте, які маніпуляції їм потрібно виконувати, і записуйте методи, які це роблять.
Вінстон Еверт

2
У деяких випадках Геттери і сетери - найкращий метод. Іноді вони є найкращим інтерфейсом, який ви можете надати. Однак у багатьох випадках їх немає. У багатьох випадках ви просто просочуєте деталі реалізації в інші класи.
Вінстон Еверт

-1

По-перше, є в основному два типи об'єктів, які я буду коментувати, значення та типи послуг.

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

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


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

-1

Геттер / сетер виправданий так само, як і будь-який інший публічний метод. Виправдання в основному тому, що ми хочемо надати інтерфейс із зовнішнім світом, який визначає, як взаємодіє наш клас з іншими класами. Ми робимо це головним чином тому, що хочемо зменшити зв’язок між окремими сутностями. Зменшити з'єднання - це корисна річ з багатьох причин:

  • Я можу використовувати зі свого поточного коду той самий інтерфейс класу, хоча тим часом цей клас додав нові методи (але зберіг старий інтерфейс)
  • Може змінити внутрішнє представлення класу, не впливаючи на споживачів
  • Зменшіть ймовірність помилок: якщо ви зробите свої внутрішні дані приватними, ви можете точно знати, що зовнішній код не змішиться з вашими внутрішніми даними

-1

Так, getters та setters є анти-шаблоном в об'єктно-орієнтованому програмуванні і його ніколи не слід використовувати: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . У двох словах, вони не вписуються в об'єктно-орієнтовану парадигму, оскільки вони спонукають вас ставитися до об'єкта, як до структури даних. Яка основна помилка.


2
це , здається, не пропонує нічого істотного над точкою зроблена і пояснена в попередньому рівні 18 відповідей
комар

4
Це також трохи сильно сказати ніколи .
Вінстон Еверт

-4

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

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

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

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


4
і що саме я отримав, додаючи багато коду для виклику setFoo () та getFoo () кожного разу, коли я маю справу зі змінною?
Вінстон Еверт

7
Ось річ, я не думаю, що ви отримуєте інкапсуляцію. Ви все ще маніпулюєте станом об’єкта з інших місць. Ви просто робите це більш багатослівно. Щоб отримати інкапсуляцію, вам потрібно централізувати маніпулювання значеннями в класі, якому належить це значення. Все інше - лише процедурний код в одязі ОО. (Нічого не обов'язково поганого в цьому, але це є).
Вінстон Еверт

2
Перевага інкапсуляції полягає в тому, що вся логіка, пов’язана з певними значеннями, знаходиться в одному місці (більш-менш). Я не розумію цього з геттерами та сетерами. Отже, я не думаю, що я отримую для них багато користі.
Вінстон Еверт

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

3
Я, звичайно, не шанувальник багатослів’я. Але моє заперечення - це справді люди, які роблять свої змінні приватними, а потім називають одержувачів та сеттерів на них вільно, закінчуючи майже без різниці. Якщо ви хочете використовувати геттерів / сетерів у своєму класі, це добре. Питання - чому? Основна перевага, про яку я знаю, полягає в тому, що ви можете зробити певну перевірку даних, щоб виявити помилки раніше. Це менш корисно, коли весь код, що маніпулює даними, знаходиться в одному місці. Але це все одно може бути корисним. В ідеалі у вас є мова, яка підтримує властивості і дозволяє уникнути багатослівності.
Вінстон Еверт

-4

Я думав, у нас тут будуть більш звичні відповіді?

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

Хоча вам потрібно пам’ятати, щоб не перевантажувати інженера, ніколи не робіть геттера чи сетера, які не потрібні. Будь-яка інформація, яку ви не збираєтеся використовувати іншим класом, не повинна мати жодного геттера чи сетера.

Хоча причина, чому ви не оприлюднюєте поля та замість цього отримуєте геттери та сетери, здебільшого:

  • Неможливо зробити належні тести, використовуючи поля. Геттери / сетери значно кращі в цьому плані.

  • Правильно використовувати поля в режимі програмування в режимі реального часу неможливо. Синхронізовані геттери / сетери - це шлях.

  • Тема інтерфейсу (вже пояснено, тому я не буду).

  • Це легше використовувати при правильному використанні системи.

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

Тому єдиний раз, коли ви використовуєте загальнодоступне поле замість геттера / сеттера, це коли ви не робите офіційний проект, а натомість просто робите це для розваги і не можете турбуватися з геттером / сеттером.


2
+1 для перевірки. Шокований тим, що раніше ніхто не згадував про це. Ще одна корисна особливість властивостей - це їх налагодження. (набагато простіше вставити в код точку розриву, ніж вимагати апаратних засобів / точок зупинки пам'яті для полів).
Марк Н

9
Заперечення геттерам і сетерам полягає в тому, що вони часто використовуються для дотримання букви закону про ООП, ігноруючи дух і наміри. OOP каже, що ви не повинні дозволяти іншим об'єктам спілкуватися з вашими внутрішніми. Якщо ви визначите геттерів і сетерів, які дозволяють вам це робити в будь-якому разі, ви в кінцевому підсумку порушите OOP.
Вінстон Еверт

6
Я погоджуся, що у простих громадських областей є багато переваг геттерів та сетерів. Однак мені не зрозуміло, наскільки краще тестування з ними. Чи можете ви пояснити?
Вінстон Еверт

4
@Skarion: Я не згоден з вашою відповіддю. Здається, ви представляєте дві альтернативи: або використовуючи геттери / сетери, або оприлюднюючи поля. Але є й третій варіант: не використовуючи ні getters / setters, ні експонуючи поля! Навіщо вам потрібно перевірити стан внутрішнього поля? Чи не слід ви тестувати загальнодоступний API вашого об'єкта?
Андрес Ф.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.