Чи повинні ви коли-небудь використовувати захищені змінні-члени? Які переваги та які проблеми це може спричинити?
Чи повинні ви коли-небудь використовувати захищені змінні-члени? Які переваги та які проблеми це може спричинити?
Відповіді:
Чи повинні ви коли-небудь використовувати захищені змінні-члени?
Залежить від того, наскільки ви вимогливі до приховування стану.
Якщо розробник приходить і підкласує ваш клас, вони можуть зіпсувати його, оскільки вони цього не розуміють повністю. З приватними членами, крім загальнодоступного інтерфейсу, вони не можуть бачити конкретні деталі реалізації того, як все робиться, що надає вам гнучкість його подальшої зміни.
Загальне відчуття сьогодні полягає в тому, що вони спричиняють надмірне зв’язування між похідними класами та їх базами.
Вони не мають особливих переваг над захищеними методами / властивостями (колись вони могли мати незначну перевагу в продуктивності), і вони також використовувались частіше в епоху, коли в моді було дуже глибоке успадкування, чого на даний момент немає.
no particular advantage over protected methods/properties
бути no particular advantage over *private* methods/properties
?
super
конструкцію для виклику батьківського конструктора; тоді він подбає про ініціалізацію змінних приватного стану в батьківському класі.
Як правило, якщо щось свідомо не сприймається як публічне, я роблю це приватним.
Якщо виникає ситуація, коли мені потрібен доступ до цієї приватної змінної або методу з похідного класу, я змінюю їх з приватного на захищений.
Це майже ніколи не трапляється - я насправді не шанувальник спадщини, оскільки це не особливо хороший спосіб моделювати більшість ситуацій. У будь-якому випадку, продовжуйте, не хвилюйтеся.
Я б сказав, що це нормально (і, мабуть, найкращий спосіб це зробити) для більшості розробників.
Справа в тому , що якщо через рік з’явиться якийсь інший розробник і вирішить, що їм потрібен доступ до вашої змінної приватного учасника, вони просто відредагують код, змінять його на захищений і продовжать свою справу.
Єдині реальні винятки з цього полягають у тому, що ви займаєтеся доставкою двійкових dll-файлів у формі чорної скриньки третім сторонам. Це, в основному, Microsoft, ті постачальники "Custom DataGrid Control" і, можливо, кілька інших великих програм, що постачаються з бібліотеками розширення. Якщо ви не входите до цієї категорії, не варто витрачати час / зусилля, щоб турбуватися про подібні речі.
Ключове питання для мене полягає в тому, що коли ви робите захищену змінну, ви не можете дозволити жодному методу у своєму класі покладатися на те, що його значення знаходиться в межах діапазону, оскільки підклас завжди може помістити його поза діапазоном.
Наприклад, якщо у мене є клас, який визначає ширину та висоту об'єкта, що відображається, і я роблю ці змінні захищеними, тоді я не можу робити жодних припущень щодо (наприклад) співвідношення сторін.
Критично важливо, що я ніколи не можу зробити ці припущення в будь-який момент з моменту випуску коду як бібліотеки, оскільки, навіть якщо я оновлю мої сетери, щоб підтримувати співвідношення сторін, я не маю гарантії того, що змінні встановлюються через сеттери або доступ до них через геттери в існуючому коді.
Також жоден підклас мого класу не може зробити цю гарантію, оскільки вони також не можуть застосовувати значення змінних, навіть якщо це вся суть їх підкласу .
Як приклад:
Обмежуючи змінні як приватні, я можу тоді застосувати поведінку, яку я хочу, через сетери або геттери.
Взагалі, я б зберіг ваші змінні захищеного члена до того рідкісного випадку, коли ви повністю контролюєте код, який їх також використовує. Якщо ви створюєте загальнодоступний API, я б сказав ніколи. Нижче ми будемо називати змінну-член як "властивість" об'єкта.
Ось що ваш суперклас не може зробити, зробивши змінну-член захищеною, а не приватною-з-accessors:
ліниво створюйте значення на льоту, коли властивість читається. Якщо ви додаєте захищений метод отримання, ви можете ліниво створити значення і передати його назад.
знати, коли властивість було змінено або видалено. Це може спричинити помилки, коли суперклас робить припущення про стан цієї змінної. Створення захищеного методу встановлення для змінної зберігає цей контроль.
Встановіть точку зупинки або додайте вивід налагодження, коли змінна читається або в неї записується.
Перейменуйте цю змінну-учасника, не переглядаючи весь код, який може її використовувати.
Загалом, я думаю, що це був би рідкісний випадок, коли я рекомендував би зробити змінну захищеного члена. Вам краще витратити кілька хвилин на викриття властивості за допомогою геттерів / сетерів, ніж через години, відстежуючи помилку в якомусь іншому коді, який змінив захищену змінну. Мало того, але ви застраховані від додавання майбутніх функціональних можливостей (таких як ледаче завантаження), не порушуючи залежний код. Це важче зробити пізніше, ніж зараз.
На рівні проектування може бути доречним використовувати захищене властивість, але для реалізації я не бачу переваг у відображенні цього на захищену змінну члена, а не на методи доступу / мутатора.
Захищені змінні-члени мають суттєві недоліки, оскільки вони ефективно дозволяють клієнтському коду (підкласу) отримувати доступ до внутрішнього стану класу базового класу. Це заважає базовому класу ефективно підтримувати свої інваріанти.
З тієї ж причини захищені змінні-члени також значно ускладнюють написання безпечного багатопотокового коду, якщо не гарантують константу або обмежуються одним потоком.
Методи доступу / мутатора пропонують значно більшу стабільність API та гнучкість реалізації при технічному обслуговуванні.
Крім того, якщо ви пурист ОО, об'єкти співпрацюють / спілкуються, надсилаючи повідомлення, а не читаючи / встановлюючи стан.
Натомість вони пропонують дуже мало переваг. Я б не обов'язково видаляв їх із чужого коду, але сам я їх не використовую.
Здебільшого небезпечно використовувати захищений, оскільки ви дещо порушуєте інкапсуляцію вашого класу, який цілком може бути розбитий погано розробленим похідним класом.
Але у мене є один хороший приклад: скажімо, ви можете зробити якийсь загальний контейнер. Він має внутрішню реалізацію та внутрішні аксесуари. Але вам потрібно запропонувати принаймні 3 загальнодоступних доступу до його даних: map, hash_map, vector-like. Тоді у вас є щось на зразок:
template <typename T, typename TContainer>
class Base
{
// etc.
protected
TContainer container ;
}
template <typename Key, typename T>
class DerivedMap : public Base<T, std::map<Key, T> > { /* etc. */ }
template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }
template <typename T>
class DerivedVector : public Base<T, std::vector<T> > { /* etc. */ }
Я використовував цей тип коду менше місяця тому (тому код з пам'яті). Поміркувавши, я вважаю, що хоча загальний контейнер Base повинен бути абстрактним класом, навіть якщо він може жити досить добре, оскільки використання безпосередньо Base було б такою болем, це слід забороняти.
Підсумок Таким чином, ви захистили дані, що використовуються похідним класом. Тим не менше, ми повинні враховувати той факт, що базовий клас повинен бути абстрактним.
protected
не більше інкапсульований, ніж public
. Я готовий, щоб довели, що я неправий. Все, що вам потрібно зробити, це написати клас із захищеним членом і заборонити мені змінювати його. Очевидно, що клас повинен бути не остаточним, оскільки вся суть використання захищеного - для успадкування. Або щось інкапсульовано, або ні. Не існує проміжного стану.
Коротше, так.
Захищені змінні-члени дозволяють доступ до змінної з будь-яких підкласів, а також будь-яких класів того самого пакета. Це може бути дуже корисним, особливо для даних лише для читання. Однак я не вірю, що вони коли-небудь необхідні, тому що будь-яке використання захищеної змінної члена може бути відтворено за допомогою приватної змінної члена та декількох геттерів та сетерів.
Для отримання детальної інформації про модифікатори доступу .Net перейдіть сюди
У захищених змінних-членів немає реальних переваг чи недоліків, це питання того, що вам потрібно у вашій конкретній ситуації. Загалом прийнято практику оголошувати змінні-члени приватними та вмикати зовнішній доступ через властивості. Крім того, деякі інструменти (наприклад, деякі O / R карти) очікують, що дані об'єкта будуть представлені властивостями і не розпізнають загальнодоступні або захищені змінні члена. Але якщо ви знаєте, що хочете, щоб ваші підкласи (і ТІЛЬКИ ваші підкласи) отримували доступ до певної змінної, немає причин не оголошувати її захищеною.