Чому приватні поля є недостатньо захищеними?


265

Чи privateкорисна видимість полів / властивостей / атрибутів класу? В OOP рано чи пізно ви збираєтеся скласти підклас класу, і в цьому випадку добре зрозуміти і мати можливість повністю змінити реалізацію.

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

Моє запитання: чи знаєте ви про важливий випадок використання, коли privateзамість цього protectedє хорошим інструментом, або два варіанти " protected& public" вистачить для мов OOP?



236
Клієнтам: Хоча я також категорично не погоджуюся з приміщенням ОП, я закликаю це питання, оскільки воно цілком узгоджене і варто відповісти. Так, ОП потрібно сказати, чому це неправильно, але спосіб зробити це - написати відповідь (або запропонувати зміни до існуючих відповідей), а не оскаржувати лише тому, що він ще цього не зрозумів.
Ixrec

18
Отримані класи - частина зовнішнього світу.
CodesInChaos

20
Не забувайте, що захищений не завжди означає, що доступ заблокований до ієрархії спадкування. У Java також надається доступ до рівня пакету.
berry120

8
Мій професор казав, що "є речі, які я б не сказав своїм дітям. Це я, мої приватні сфери".
SAT

Відповіді:


225

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

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

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

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


11
Я б кинув тебе більше +1, якби міг, оскільки це вперше privateнасправді має сенс для мене. Перспективу губ потрібно використовувати частіше, інакше кожен, хто любить ("подібний до C") "абсолютний контроль, абсолютна відповідальність", що кодує менталітет, може позначити це як "захищає мене від себе". Зауважте, що я все-таки використовував privateраніше, я просто завжди відчував гарну документацію і умовне позначення імен, як _fooнаголосити, що ви, мабуть, не повинні возитися з ним, було рівнозначно, якщо не краще. Вміння детерміновано сказати, що "нічого не зламається" - це законна, private-особлива особливість.
abluejelly

Я спочатку ігнорував випадок коду публічних бібліотек і фреймворків і більш-менш думав лише про термін «клієнтський код». Оптимізація внутрішньої реалізації - хороший приклад для мого запитання, хоча я запитую себе, чи справді це відбувається насправді (особливо, коли багато людей рекомендують клас не повинен бути довше 1000 рядків коду). Взагалі мені подобається підхід Рубі, де приватне є своєрідною рекомендацією: "Ось дракони, будьте обережні".
Адам Лібуша

9
@ AdamLibuša Хоча це набагато більша угода, якщо ваш код є загальнодоступним, він все ще застосовується, навіть якщо ви автор усіх клієнтів класу. Проблема просто змінюється від певних рефакторів, що неможливо, а певні рефактори втомлюють і схильні до помилок. Оптимізація насправді є найменш поширеною причиною цих рефакторів у моєму досвіді (хоча я в основному роблю Javascript), як правило, це більше нагадує помилку, яка виявила основний недолік реалізації, який вимагає реструктуризації графіка залежності / виклику різних внутрішніх бітів для досягнення справді надійний виправлення.
Іксрек

5
"" рано чи пізно ви збираєтесь складати підклас кожного класу ", майже напевно, це не так." І до речі, ви майже напевно цього не зробите, і ви НЕ БУДЕТЕ, звичайно, ні, замініть кожну функцію в класі і змініть використання кожного елемента даних у класі. Деякі речі логічно повинні бути записані каменем, щоб клас мав якесь значення.
Джей

1
Я б кинув тебе більше, ніж +1, якщо б міг => Це ми називаємо баунті @abluejelly
Thomas Ayoub

256

В OOP рано чи пізно ви збираєтеся скласти підклас класу

Це неправильно. Не кожен клас призначений для підкласів, а деякі статично набрані мови OOP навіть мають функції для його запобігання, наприклад final(Java та C ++) або sealed(C #).

добре розуміти та мати можливість повністю змінити реалізацію.

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

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

Або, якщо говорити про Скотта Майєрса: на приватні частини класу впливає кінцева кількість коду: код самого класу.

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

На захищені частини потенційно впливає кожен існуючий підклас, і кожен підклас ще не буде записаний, що також є нескінченною кількістю коду.

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


34
+1 за теоретичний сценарій "постраждалого від нескінченної кількості коду"
Broken_Window

13
Можливо, варто також зазначити, що дизайнери C # насправді вирішили заборонити переосмислювати успадкований метод, якщо він прямо не позначений як virtual, з причин, викладених у цій відповіді.
Буде Вусден

11
Або простіше кажучи: protectedце для методів, а не для членів даних.
Матьє М.

7
Зараз я не можу його знайти, але я пам’ятаю, що читав добре написану відповідь від @EricLippert про те, чому в MS sealedвеликі частини бібліотеки .net та IIRC, чому він хотів би, щоб решта зачинили. У той момент, коли ви дозволяєте стороннім спадкоємцям почати торкатися внутрішніх значень, вам потрібно додати величезну кількість перевірок перевірки / обґрунтованості до кожного методу, оскільки ви більше не можете довіряти будь-яким інваріантам дизайну щодо внутрішнього стану об'єктів.
Ден Нелі

4
@ ThorbjørnRavnAndersen "під час розвитку, але не після звільнення" - як на землі повинен знати клас? Ви дійсно хочете скласти тестову версію, яка відрізняється від випущеної версії, коли ви навіть не можете протестувати версію версії? Тести все одно не повинні мати доступ до приватних матеріалів.
Себастьян Редл

33

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

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


2
+1 для "їзди на тренері та конях через що-небудь". Чудова фраза, яку б я хотів, щоб почула більше.
Нік Хартлі

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

3
@ AdamLibuša "Не ділитися кодом"? Як тільки ви публікуєте будь-яку DLL взагалі, ви ділитесь кодом - всі структури та методи та все, що може побачити весь світ, особливо з мовами, які підтримують відображення за замовчуванням. Кожен може робити все, що завгодно, "вашим кодом" - privateце лише спосіб сказати "не торкайтеся цих", і застосовувати це в рамках контракту на компілятор. У таких системах, як .NET, це також має важливі наслідки для безпеки - ви можете торкатися приватних осіб лише тоді, коли маєте повне довіру (в основному, еквівалент адміністратора / root доступу).
Луаан

2
@ AdamLibuša Я думаю, що ваша плутанина в основному випливає з різних підходів до ООП різних мов. Корінь OOP (як було визначено спочатку) - це обмін повідомленнями - це означає, що все є приватним, за винятком повідомлень, на які ви відповідаєте. У більшості мов OOPish це піддається "збереженню даних" - спосіб зробити публічний інтерфейс якомога меншим. Єдиний спосіб, коли користувач (будь то підклас чи інший клас) маніпулює вашим класом, це через визначений вами загальнодоступний інтерфейс - аналогічно тому, як ви зазвичай використовуєте кермо та педалі для керування автомобілем :)
Luaan

3
@Luaan +1, коли добре чіпати приватних осіб!
Nigel Touch

12

При спробі формально міркувати про правильність програми, орієнтованої на об'єкти , типово використовувати модульний підхід, що включає об'єктні інваріанти . У такому підході

  1. Методи пов'язані з ними до і після умов (контрактів).
  2. Об'єкти асоціювали з ними інваріанти.

Модульне міркування про об'єкт протікає наступним чином (спочатку приблизно наближенням)

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

Уявіть, що ми перевіряємо, object Aвикористовуючи підхід вище. А тепер хочу перевірити method gз object Bяких вимагає method fвід object A. Модульне міркування дозволяє нам міркувати, method gне переглядаючи реалізацію method f. За умови, що ми можемо встановити інваріантність object Aта передумову method fна сайті виклику, method g,ми можемо сприйняти умову повідомлення method fяк підсумок поведінки виклику методу. Крім того, ми також будемо знати, що після виклику повертається інваріант Aдосі утримується.

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

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

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

Захищені поля

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

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

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


8

privateзмінні в класі кращі, ніж protectedз тієї ж причини, що breakоператор всередині switchблоку краще, ніж goto labelоператор; що полягає в тому, що люди-програмісти схильні до помилок.

protectedзмінні піддаються ненавмисному зловживанню (помилки програміста) так само, як gotoзаява піддається створення коду спагетті.

Чи можливо записати робочий код без помилок за допомогою protectedзмінних класу? Так, звісно! Так само, як можна написати робочий код без помилок, використовуючи goto; але як сказано на кліше: "Просто тому, що ти можеш, не означає, що ти повинен!"

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

Базові класи абсолютно не знають похідних класів. Що стосується базового класу, то protectedвін фактично не дає вам більше захисту public, тому що ніщо не заважає похідному класу створити publicгеттер / сеттер, який веде себе як бекдор.

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

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

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

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


Для речей - що можна змінити / покращити у цій відповіді?
Бен Коттрелл

Я не проголосував, але порівнював рівень доступу з перервою / переходом?
име96

@ imela96 Ні, порівнюючи причину, чому їх уникають, і відволікає "Краща практика" (особливо для написання нового коду). тобто грамотний програміст уникатиме publicдеталей реалізації, оскільки він піддається нездійсненному коду. Грамотний програміст уникне, gotoоскільки він піддається нездійсненному коду. Однак реальний світ такий, що іноді ви втрачаєте себе в жахливому безладі застарілого коду, і не маєте іншого вибору, як використовувати goto, і з цієї ж причини вам іноді не залишається іншого вибору, крім використання загальнодоступних та захищених деталей реалізації.
Бен Коттрелл

Тяжкість різниться за величиною, вона незрівнянна. Я міг би жити з кодом, який має лише publicвластивості / інтерфейс, але не з кодом, який використовує тільки goto.
име96

2
@ imela96 Ви коли-небудь насправді працювали в коді, який використовує, gotoчи це просто тому, що ви читали такі статті? Я розумію, чому goto зло, тому що я провів 2 роки, працюючи над стародавнім кодом, який його використовує; і я витратив ще довше, працюючи в коді, де "класи" деталі своєї реалізації просочилися скрізь, publicа friendспецифікатори використовуються як швидкі / легкі / брудні хаки. Я не погоджуюся з аргументом того, що "публічне викриття деталей щодо впровадження" не може спричинити заплутаність спагетті на тому ж рівні суворості, як gotoце абсолютно можливо.
Бен Коттрелл

4

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

Створення членів protectedробить його більш універсальним базовим класом, але часто обмежує способи зміни майбутніх версій класу. Створення членів privateдозволяє автору класу більшої універсальності змінювати внутрішню роботу класу, але обмежує види класів, які можуть бути корисно виведені з нього.

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


4

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

Якщо це припущення буде відхилено, то слід розглянути лише два випадки.

  1. У автора оригінального класу були дуже чіткі ідеї, чому він може бути розширений (наприклад, це BaseFoo і буде кілька конкретних реалізацій Foo вниз).

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

  1. Автор дочірнього класу намагається втрутитися в певну поведінку до батьківського класу.

Цей випадок повинен бути рідкісним (ви можете стверджувати, що це не є законним), і не вважається за краще просто змінювати оригінальний клас у вихідній базі коду. Це також може бути симптомом поганого дизайну. У таких випадках я вважаю за краще, щоб людина, що хакує в поведінці, просто використовувала інші хаки, як друзі (C / C ++) та setAccessible(true)(Java).

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

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


2

Усі три рівні доступу мають свій приклад використання, OOP був би неповним, не маючи жодного з них. Зазвичай ви робите

  • зробити всі змінні / члени даних приватними . Ви не хочете, щоб хтось із зовнішніх возився з вашими внутрішніми даними. Також методи, що надають допоміжний функціонал (обчислення думок на основі декількох змінних членів) для вашого загальнодоступного або захищеного інтерфейсу - це лише для внутрішнього використання, і ви, можливо, захочете його змінити / вдосконалити в майбутньому.
  • зробити загальний інтерфейс вашого класу загальнодоступним . З цим повинні працювати користувачі вашого оригінального класу, і як ви думаєте, як повинні виглядати отримані класи. Для забезпечення належної інкапсуляції це, як правило, лише методи (і допоміжні класи / структури, enums, typedefs, що б користувачеві потрібно було працювати з вашими методами), а не змінні.
  • оголосити захищені методи, які можуть бути корисними для тих, хто хоче розширити / спеціалізувати функціональність вашого класу, але не повинен бути частиною публічного інтерфейсу - адже ви зазвичай піднімаєте приватних членів до захищених, коли це необхідно. Якщо ви сумніваєтесь, ви цього не зробите, поки не знаєте про це
    1. ваш клас може / може / буде підкласом,
    2. і мати чітке уявлення про те, якими можуть бути випадки використання підкласингу.

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


1

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

Ряд мов (наприклад, Ruby та Smalltalk) не надають загальнодоступні поля, щоб розробники не відмовлялися від можливості прямого зв'язку з їх реалізацією класу, але чому б не піти далі і мати лише приватні поля? Не було б втрати загальності (оскільки надклас завжди може забезпечити захищені аксесуари для підкласу), але це забезпечило б, щоб класи завжди мали принаймні невелику ступінь ізоляції від своїх підкласів. Чому це не більш поширений дизайн?


1

Тут багато хороших відповідей, але я все одно кину два мої центи. :-)

Приватне добре з тієї ж причини, що і глобальні дані погані.

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

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


1

Думаю, варто згадати кілька суперечливих думок.

Теоретично добре мати контрольований рівень доступу з усіх причин, зазначених в інших відповідях.

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

Також мене дратує, що це означає, що їхні класи не закриті для модифікації.

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

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

Imo, це дуже схоже на динамічне / статичне введення тексту. Теоретично статичне введення тексту дуже добре. На практиці, це тільки заважає , як 2% помилок нерозумної ефективності динамічної типізації ... . Використання приватного, ймовірно, запобігає помилки менше, ніж це.

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


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

0

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

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

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


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

@sam Як правило, код слід писати з припущенням, що той, хто його змінить далі, не повинен знати всю кодову базу зсередини. Це може бути навіть оригінальний автор часом (час проходить, недосипання ...). Обман може надати студентам коду скелета, щоб тісно вийти, і змінити логіку, яку вони не повинні чіпати.
Філ Лелло

0

Приватні методи / змінні, як правило, приховані від підкласу. Це може бути хорошою справою.

Приватний метод може робити припущення щодо параметрів і залишати перевірку здоровості на абонента.

Захищений метод повинен перевіряти вхідність.


-1

"приватне" означає: не призначений для зміни чи доступу до них будь-яким, крім самого класу. Не призначені для зміни та доступу до підкласів. Підкласи? Які підкласи? Ви не повинні це підклас!

"захищений" означає: призначений лише для зміни або доступу до класів або підкласів. Ймовірно, вирахування, що ви повинні підкласи, інакше чому "захищений", а не "приватний"?

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


-1

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

Деякі міркування щодо методівprivate проти :protected

privateметоди запобігання повторного використання коду. Підклас не може використовувати код у приватному методі і, можливо, доведеться його знову реалізувати - або повторно реалізувати метод (и), які спочатку залежать від приватного методу & c.

З іншого боку, будь-який метод, який не private може розглядатися як API, наданий класом "зовнішньому світу", в тому сенсі, що сторонні підкласи також вважаються "зовнішнім світом", як хтось ще запропонував у своїй відповіді вже.

Це погано? - Я не думаю, що так.

Звичайно, публічний API (псевдо-) блокує оригінальний програміст і перешкоджає рефакторингу цих інтерфейсів. Але, дивлячись навпаки, чому програміст не повинен розробляти власні "деталі реалізації" таким чином, настільки ж чистим і стабільним, як його публічний API? Чи варто його використовувати, privateщоб він недбало ставився до структуризації свого "приватного" коду? Думаючи, може, пізніше він міг би це прибрати, бо ніхто цього не помітить? - Немає.

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

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

Ці методи логічно не можна перекрити / покращити, хоча технічно немає нічого, щоб зробити це (компілятор) очевидним.

Хочете розширення та успадкування? Не робіть своїх методів private.

Не хочете, щоб певна поведінка вашого класу була змінена? Складіть свої методи final.

Невже не може ваш метод викликатись поза певним, чітко визначеним контекстом? Створіть свій метод privateта / або подумайте, як можна зробити необхідний чітко визначений контекст доступним для повторного використання за допомогою іншого protectedметоду обгортки.

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

Для полів privateце не дуже погано. Поки поле (s) може бути досить «використовується» з допомогою відповідних методів (це НЕ getXX() або setXX()!).


2
-1 Це не вирішує privateпроти protectedпервісного питання, дає хибний ( «використовувати privateекономно»?) І йде врозріз із загальним консенсусом по дизайну OO. Використання не privateмає нічого спільного з приховуванням неохайного коду.
Андрес Ф.

3
Ви згадуєте, що у класу є API, але, схоже, не встановлюється зв’язок, що користувач API не хоче бути обтяженим деталями, які їм не потрібно знати. Ідеальна ситуація для користувача класу полягає в тому, щоб інтерфейс містив лише необхідні їм методи, і більше нічого. Створення методів privateдопомагає підтримувати чистий API.
Бен Коттрелл

1
@HannoBinder Я погоджуюся, що там є багато класів, які страждають від жорсткості, проте гнучкість та повторне використання коду набагато менш важливі, ніж інкапсуляція та довгострокова ремонтопридатність. Розглянемо свій випадок, коли всі класи мають захищені члени, а похідні класи можуть поспішати з будь-якими деталями реалізації, які вони хочуть як ви надійно одиниці тестування цих класів, знаючи, що що-небудь у будь-якій частині ще неписаного коду може призвести до відмови цього коду в будь-який час? скільки таких "хакків" міг би взяти код, перш ніж він перероджується у велику кульку грязі?
Бен Коттрелл

2
О, ви сказали "члени". Те, що я говорю, стосується лише методів . Змінні (поля) учасників - це інша історія, де конфіденційність набагато виправданіша.
JimmyB

2
Номенклатура в дискусії є повсюдною, оскільки вона не характерна для конкретної мови. "Член" в C ++ може бути даними, функціями, типом або шаблоном.
JDługosz

-1

Чи знаєте ви про важливий випадок використання, коли приватний замість захищеного є хорошим інструментом, або два варіанти "захищений та громадський" вистачить для мов OOP?

Приватний : коли у вас є щось, що ніколи не буде корисним для будь-якого підкласу, щоб зателефонувати чи переоформити

Захищений : коли у вас є щось, що має специфічну для підкласу реалізацію / константу.

Приклад:

public abstract Class MercedesBenz() extends Car {
  //Might be useful for subclasses to know about their customers
  protected Customer customer; 

  /* Each specific model has its own horn. 
     Therefore: protected, so that each subclass might implement it as they wish
  */
  protected abstract void honk();

  /* Taken from the car class. */
  @Override
  public void getTechSupport(){
     showMercedesBenzHQContactDetails(customer);
     automaticallyNotifyLocalDealer(customer);
  }

  /* 
     This isn't specific for any subclass.
     It is also not useful to call this from inside a subclass,
     because local dealers only want to be notified when a 
     customer wants tech support. 
   */
  private void automaticallyNotifyLocalDealer(){
    ...
  }
}

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

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

-2

Мені було важко зрозуміти цю справу, тому я хотів би поділитися частиною свого досвіду:

  • Що таке заповідне поле? Ні більше нічого , ніж поле, яке не може бути доступні поза класом, тобто публічно , як це: $classInstance->field. І хитрість, що це "це воно". Діти вашого класу матимуть повний доступ до нього, оскільки це їх законна внутрішня частина.
  • Що таке приватне поле? Це " справжня приватна " для вашого власного класу та вашої власної реалізації цього класу. "Зберігайте в недоступному для дітей місці", як на пляшці з ліками. Ви будете мати гарантію, що його неможливо відновити похідними класу, а ваші методи - коли вони будуть викликані - матимуть точне, що ви заявили

ОНОВЛЕННЯ: практичний приклад за допомогою реальної задачі, яку я вирішив. Ось це: у вас є маркер, як USB або LPT (це був мій випадок), і у вас є середнє програмне забезпечення. Маркер запитує у вас пінкод, відкриється, якщо він правильний, і ви можете надіслати зашифровану частину та ряд ключів для розшифровки. Клавіші зберігаються в маркері, їх не можна читати, лише користуватися ними. І були тимчасові ключі для сеансу, підписані ключем у маркері, але збережені в самому проміжному програмному забезпеченні. Температурний ключ не повинен був просочуватися навкруги, а лише існувати на рівні водія. І я використовував приватні поля, щоб зберігати цей тимчасовий ключ та деякі дані, пов’язані з обладнанням. Тож жодні похідні не змогли використовувати не лише публічний інтерфейс, а й деякі захищені"зручні" підпрограми, які я створив для завдання, але не зміг відкрити сильну скриньку із взаємодією ключів та HW. Має сенс?


вибачте, хлопці! просто зіпсував їх - оновивши мою відповідь =) ДЯКУЮ! Я поспішав і помилявся =)
Олексій Веснін

2
Я думаю, що ОП знає про відмінності між двома рівнями захисту, але цікавиться, чому один слід використовувати над іншим.
Сем

@Sam, будь ласка, глибше прочитайте мою відповідь. Вони абсолютно різні типи поля! тільки , що вони мають в загальному, що вони обидва не можуть посилатися публічно
Олексій Веснін

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

@Sam Я думаю, що я зрозумів, що я додав практичний випадок із своєї реальної практики / досвіду. Це те, чого не вистачає у вашій точці?
Олексій Веснін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.