Приватне відносно захищеного - Концепція належної практики видимості [закрито]


145

Я шукав і знаю теоретичну різницю.

  • public - будь-який клас / функція може отримати доступ до методу / властивості.
  • захищено - лише цей клас та будь-які підкласи можуть отримати доступ до методу / властивості.
  • приватне - лише цей клас може отримати доступ до методу / властивості. Це навіть не буде успадковано.

Це все добре і добре, питання полягає в тому, яка між ними практична різниця? Коли б ви використовували privateі коли б ви використовували protected? Чи є стандартна чи прийнятна добра практика щодо цієї?

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

(Зауважте, що це питання для мене, але також для подальшого використання, оскільки я не бачив такого питання, як це ТАК).


33
Це важливо? Будь-яка мова з підтримкою OOP викликає це питання. Я буваю програмою на PHP, але я думаю, що це питання стосується будь-якої підтримуючої мови OOP.
Привид

2
Гаразд ярмарок, просто цікаво, чи забули ви теги. Тепер я бачу тег oop.
Пол Беллора

Я повинен встановити видимість (приватну / публічну / захищену) для кожного властивості класу? Або лише деякі з них повинні мати тип видимості? Якщо так, то як визначити, яка власність повинна мати видимість у верхньому класі?
user4271704

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

Є ще один специфікатор доступу internalна мові C #, який обмежує рівень доступу класу або його членів у фізичній збірці. Хоча я не впевнений у його підтримці чи чомусь подібному в інших мовах.
RBT

Відповіді:


128

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

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

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


1
Справжнє питання тут - про privatevs protectedКоли я хочу, щоб майно успадковувалось, а коли не я? Я не можу реально сказати, чи користувач іноді хоче взяти свій клас і продовжити його ...
Ghost Madara

16
Що ж, питання полягає не в тому, що користувач хоче перекрити, а в тому, що ви хочете дозволити переосмислювати. Зазвичай це допомагає перемикати сторони і намагатися думати: Якби я використав цей клас і створив підклас, що б я хотів мати можливість переосмислити? Іноді інші користувачі все одно пропустять речі
Thorsten Dittmar

3
Захищені поля - це погана практика у спадщині! . Будь ласка, будьте про це. Ось чому
RBT

4
Приватні поля погані на практиці у спадщині !. (перебільшуючи) Будь ласка, прочитайте мою відповідь.
Ларрі

6
Типова контрольно-відроджувальна думка.
bruno desthuilliers

58

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

Стара мудрість

"позначати його приватним, якщо у вас немає вагомих причин не"

це мало сенс у дні, коли це було написано, перш ніж відкритий код домінував у бібліотечному просторі розробників та VCS / mgmt залежності. Завдяки Гитубу, Мейвіну та ін. вони стали гіпер спільними зусиллями. Тоді також можна було заробляти гроші, обмежуючи способи використання бібліотеки. Я провів, мабуть, перші 8 чи 9 років своєї кар’єри, чітко дотримуючись цієї «найкращої практики».

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

Ти коли-небудь:

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

Це три найбільші раціоналізації, які я чув для методів маркування за замовчуванням:

Раціоналізація №1: Це небезпечно і немає жодних причин перекривати конкретний метод

Я не можу підрахувати, скільки разів я помилявся щодо того, чи виникне потреба у відміні певного способу, який я написав. Попрацювавши над кількома популярними відкритими джерелами, я навчився важко розуміти справжню вартість маркування речей. Це часто виключає єдине практичне рішення небачених проблем або випадків використання. І навпаки, я ніколи за 16+ років професійного розвитку не шкодував, щоб позначити метод, захищений замість приватного, з причин, пов’язаних із безпекою API. Коли розробник вирішує розширити клас і замінити метод, вони свідомо говорять "Я знаю, що роблю". і заради продуктивності, якої має бути достатньо. періоду. Якщо це небезпечно, відзначте це у класі / методі Javadocs, не просто сліпо заплющуйте двері.

Методи маркування, захищені за замовчуванням, є пом’якшенням для однієї з найважливіших проблем сучасного розвитку ПЗ: провал уяви.

Раціоналізація №2: Він підтримує чистоту публічного API / Javadocs

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

Раціоналізація №3: Моє програмне забезпечення є комерційним, і мені потрібно обмежити його використання.

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

Ніколи не кажи ніколи

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

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


Напишіть своє Rationalization # 1- Коли я позначаю щось захищене, я вирішу це робити явно, оскільки вважаю, що така поведінка не є остаточним і може бути випадком, коли мої дитячі заняття хотіли б це змінити. Якщо я не такий впевнений, тоді я хотів би зробити його приватним за замовчуванням. Спеціально на такій мові, як C #, яка зберігає метод невіртуальний за замовчуванням, додавати тільки захищений специфікатор доступу не має сенсу. Вам потрібно додати virtualі protectedключові слова, щоб зробити ваш намір зрозумілим. Також люди віддають перевагу асоціації над спадщиною, тому protectedдефолт важко сприймається
RBT

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

3
Я не пам'ятаю, скільки разів мені доводилося «клонувати» основні класи лише тому, що я хотів переосмислити 1 або 2 або методи. І як те, що ви сказали, із соціальною тенденцією, якою ми ділимося більше коду, ніж будь-коли раніше, за замовчуванням є набагато більше сенсу працювати із захищеними, ніж приватними. тобто бути більш відкритими до власної логіки.
shinkou

1
Погодьтеся. Я просто хотів налаштувати Java BufferedReader, щоб обробляти власні розмежувачі ліній. Завдяки приватним полям я повинен клонувати весь клас, а не просто розширювати його та переосмислювати readLine ().
Рон Данн

21

Перестаньте зловживати приватними полями !!!

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

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

Є два випадки, коли ви хочете розширити клас:

  • Ви хочете додати додатковий функціонал до базового класу
  • Ви хочете змінити існуючий клас, який знаходиться поза поточним пакетом (можливо, в деяких бібліотеках)

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

Розглянемо просту бібліотеку, яка моделює машини:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

Автор бібліотеки подумав: немає жодної причини, щоб користувачі моєї бібліотеки не мали доступу до деталей реалізації, assembleCar()правда? Позначимо гвинт як приватний.

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

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

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

Я дуже підтримую відповідь Ніка.


2
Використання приватних полів здебільшого говорить про те, що успадкування - це неправильна абстракція для зміни певної поведінки класу / об'єкта (якщо використовується свідомо). Зміна зазвичай мовчки порушує LSP, оскільки поведінка змінюється без зміни API. Чудова відповідь, +1.
Привид

Проповідуйте! Приватне - це сенс мого існування, коли я намагаюся писати модники Minecraft. Нічого не дратує, ніж використовувати Reflection або ASM, щоб виправити це.
RecursiveExceptionException

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

16

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

  1. Зробіть усі класи остаточними, якщо вам не потрібно відразу підкласифікувати їх.
  2. Зробіть усі методи остаточними, якщо вам не потрібно одразу підкласифікувати їх і замінити.
  3. Зробіть усі параметри методу остаточними, якщо вам не потрібно їх змінювати в тілі методу, який у будь-якому разі є незручним у більшості випадків.

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

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

Відмова: Я не дуже програмую на Java :)


2
Для уточнення: A final classв Java - це клас, від якого не можна успадкувати. A final methodневіртуальний, тобто не може бути замінений підкласом (методи Java за замовчуванням є віртуальними). A final field/parameter/variable- незмінна посилання на змінну (в тому сенсі, що вона не може бути змінена після її ініціалізації).
М.Страмм

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

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

7

Коли б ви використовували privateі коли б ви використовували protected?

Приватне успадкування можна вважати реалізованим з точки зору відносин, а не відносин IS-A . Простіше кажучи, зовнішній інтерфейс класу успадкування не має (видимого) відношення до спадкового класу. Він використовує privateспадщину лише для реалізації подібної функціональності, яку надає клас Base.

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


2
Це "Здійснено з точки зору відносин" - це відносини HAS-A. Якщо всі атрибути приватні, єдиний спосіб отримати доступ до них - це методи. Це те саме, що, якби підклас містив об'єкт надкласу. Видалення всіх захищених атрибутів з ваших конкретних класів означає також усунути всі спадщини від них.
shawnhcorey

2
Цікавий мислительний процес - приватне успадкування . @shawnhcorey дякує за те, що він чітко пояснює, що він вказує на зв'язок HAS-A, який називається асоціацією (яка може бути агрегацією чи складом). Я вважаю, що цю публікацію слід також оновити, щоб уточнити те саме.
RBT

1

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

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