Чому погана ідея створити загальний сетер та геттер з відображенням?


49

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

Тож чи може хтось словесно пояснити, чому саме з огляду на те, що слід уникати роздумів, зокрема у випадку загального геттера та сеттера?


12
Якщо ви просто хочете зменшити котельну плиту, і Lombok, і Groovy створюватимуть геттери та сетери безшумно, але безпечно.
chrylis -на страйк-

2
Як би ви навіть споживали ці геттери та сетери статичною мовою, як java? Мені це здається нестандартним.
Есбен Сков Педерсен

@EsbenSkovPedersen Ви споживаєте їх так само, як Map, getі putце досить незграбний спосіб робити речі.
HAEM

2
IIRC, Lombok синтезує гетерів / сеттерів під час компіляції, як це продиктовано відповідною анотацією, тому вони: не несуть покарання для відображення продуктивності, можуть бути статично проаналізовані / вбудовані, можуть бути відновлені (IntelliJ має плагін для Lombok)
Олександр

Відповіді:


117

Недоліки рефлексії взагалі

Відображення важче зрозуміти, ніж прямолінійний код.

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

Код відбиття недоступний для статичного аналізу

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

Але якщо місце , яке використовує поглинач щось на зразок callGetter("Foo")і callGetterробить getClass().getMethod("get"+name).invoke(this), то описаний вище метод не знайде його, і компілятор буде скаржитися. Тільки коли код буде фактично виконаний, ви отримаєте NoSuchMethodException. І уявіть, який біль ви відчуваєте, якщо цей виняток (який відслідковується) проковтнуто, callGetterоскільки "він використовується лише з жорстко зашифрованими рядками, насправді це не може відбутися". (Ніхто не зробив би цього, хтось може сперечатися? За винятком того, що ОП зробив саме так у своїй відповіді "ТА". Якщо поле буде перейменовано, користувачі загального сетера ніколи не помітять, за винятком надзвичайно незрозумілої помилки сеттера, яка мовчки нічого не робить. Користувачі Getter, якщо їм пощастить, помітять консольний вихід ігнорованого винятку.)

Код відбиття не перевіряється типом компілятором

Це в основному великий підпункт вищезазначеного. Код відображення - це все Object. Типи перевіряються під час виконання. Помилки виявляються одиничними тестами, але лише якщо у вас є покриття. ("Це просто геттер, мені не потрібно це перевіряти.") В основному ви втрачаєте перевагу, використовуючи Java над Python, ви здобули вас в першу чергу.

Код відображення недоступний для оптимізації

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

Код відображення в цілому просто повільний

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

Недоліком загального геттера / сеттера

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


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

2
Варто також зазначити, що геттери / сетери можуть легко створюватися ідентифікатором.
stiemannkj1

26
+1 Логіка відображення налагодження у проекті розміру виробництва повинна бути визнана ООН тортурами та незаконна.
errantlinguist

4
"важче зрозуміти цих програмістів." - для цих програмістів це важко зрозуміти, але важче зрозуміти для всіх, як би не був досвідчений.
BartoszKP

4
"Код відбиття недоступний для статичного аналізу" - Це. Так важливо для включення рефакторингу. Оскільки інструменти статичного аналізу стають більш потужними (наприклад, пошук коду в останньому сервері Team Foundation), код письма, який дозволяє їм, стає ще більш цінним.
Dan J

11

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

І тому, що це менш ефективно. Принаймні, на C #, геттер / сеттер буде вкладений прямо в одну інструкцію CIL. У своїй відповіді ви замінюєте це 3-ма викликами функцій, які, у свою чергу, повинні завантажувати метадані, щоб відображатись. Я, як правило, використовував 10 разів як велике правило для витрат на роздуми, але коли я шукав дані, одна з перших відповідей включала цю відповідь, яка визначила його ближче до 1000x.

(І це робить ваш код важче налагоджувати, і це шкодить юзабіліті, оскільки існує більше способів неправомірного використання, і це шкодить ремонтопридатності, оскільки ви зараз використовуєте магічні рядки, а не належні ідентифікатори ...)


2
Зауважте, що питання позначене Java, яка має таке чудове лайно, як концепція Beans. Боба не має відкритих полів, але це може привести поле через getX()та setX()методи , які можуть бути знайдені з допомогою відображення. Не обов’язково квасоля повинна інкапсулювати свої значення, вони можуть бути просто DTO. Тож на Яві гетери часто є необхідним болем. С # властивості є набагато, набагато приємнішими в будь-якому відношенні.
амон

11
Хоча C # властивості - це одягнені загони / сетери. Але так, концепція Beans - це катастрофа.
Себастьян Редл

6
@amon Правильно, C # придумав жахливу ідею, і це було легше здійснити.
JimmyJames

5
@JimmyJames Насправді це не страшна ідея; ви можете підтримувати сумісність ABI, незважаючи на зміни коду. Думаю, у багатьох проблем немає.
Кейсі

3
@Casey Обсяг цього виходить за рамки коментаря, але побудова інтерфейсу із внутрішніх деталей реалізації є зворотним. Це призводить до процесуального оформлення. Бувають випадки, коли використання методу getX є правильною відповіддю, але це досить рідко в добре розробленому коді. Проблема з геттерами та сеттерами в Java не в кодовій коді навколо них, а в тому, що вони є частиною жахливого дизайнерського підходу. Зробити це простіше - це як зробити простіше вдарити себе в обличчя.
JimmyJames

8

Окрім вже наведених аргументів.

Він не забезпечує очікуваного інтерфейсу.

Користувачі розраховують зателефонувати foo.getBar () і foo.setBar (значення), а не foo.get ("bar") і foo.set ("bar", value)

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


Мені здається, що ця причина важливіша за всі інші в інших відповідях.
Есбен Сков Педерсен

-4

Звичайно, це не погана ідея, це просто, ніж у звичайному світі Java - погана ідея (коли ти хочеш лише геттера чи сетера). Наприклад, деякі фреймворки (spring mvc) або бібліотеки вже використовують його (jstl) таким чином, деякі json o xml парсери використовують його, якщо ви хочете використовувати DSL, ви збираєтесь його використовувати. Тоді це залежить від вашого сценарію використання.

Нічого не є правильним чи неправильним як факт.

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