Стоит ли когда-нибудь использовать защищенные переменные-члены? В чем преимущества и какие проблемы это может вызвать?
Стоит ли когда-нибудь использовать защищенные переменные-члены? В чем преимущества и какие проблемы это может вызвать?
Ответы:
Стоит ли когда-нибудь использовать защищенные переменные-члены?
Зависит от того, насколько вы придирчивы к скрытию состояния.
Если разработчик придет и создаст подклассы вашего класса, он может испортить его, потому что не понимает его полностью. С закрытыми членами, кроме открытого интерфейса, они не могут видеть детали реализации того, как что-то делается, что дает вам возможность изменить это позже.
В настоящее время принято считать, что они вызывают чрезмерную связь между производными классами и их базами.
У них нет особого преимущества перед защищенными методами / свойствами (когда-то у них могло быть небольшое преимущество в производительности), и они также чаще использовались в эпоху, когда в моде было очень глубокое наследование, чего сейчас нет.
no particular advantage over protected methods/properties
быть no particular advantage over *private* methods/properties
?
super
конструкцию для вызова родительского конструктора; Затем он позаботится об инициализации переменных частного состояния в родительском классе.
Как правило, если что-то намеренно не считается публичным, я делаю это частным.
Если возникает ситуация, когда мне нужен доступ к этой частной переменной или методу из производного класса, я меняю ее с частной на защищенную.
Такого почти никогда не бывает - я вообще-то не фанат наследования, поскольку это не очень хороший способ моделировать большинство ситуаций. В любом случае, не беспокойтесь.
Я бы сказал, что это нормально (и, вероятно, лучший способ сделать это) для большинства разработчиков.
Дело в том , что если через год придет другой разработчик и решит, что ему нужен доступ к вашей частной переменной-члену, он просто отредактирует код, изменит его на защищенный и продолжит свой бизнес.
Единственные реальные исключения из этого - если вы занимаетесь доставкой двоичных dll в виде черного ящика третьим лицам. В основном это Microsoft, поставщики «Custom DataGrid Control» и, возможно, несколько других крупных приложений, которые поставляются с библиотеками расширяемости. Если вы не относитесь к этой категории, не стоит тратить время / силы на беспокойство о подобных вещах.
Ключевая проблема для меня заключается в том, что после того, как вы сделаете переменную защищенной, вы не сможете разрешить никакому методу в вашем классе полагаться на то, что его значение находится в пределах диапазона, потому что подкласс всегда может поместить его вне диапазона.
Например, если у меня есть класс, который определяет ширину и высоту визуализируемого объекта, и я делаю эти переменные защищенными, тогда я не могу делать никаких предположений относительно (например) соотношения сторон.
Что критически важно, я никогда не смогу сделать эти предположения в любой момент с момента выпуска этого кода в виде библиотеки, поскольку даже если я обновлю свои сеттеры для поддержания соотношения сторон, у меня нет гарантии, что переменные устанавливаются через сеттеры или доступны через геттеры в существующем коде.
Ни один из подклассов моего класса не может сделать эту гарантию, поскольку они также не могут обеспечить соблюдение значений переменных, даже если это весь смысл их подкласса .
Например:
Ограничивая переменные частными, я могу затем обеспечить желаемое поведение через сеттеры или геттеры.
В общем, я бы сохранил ваши защищенные переменные-члены в том редком случае, когда вы полностью контролируете код, который их также использует. Если вы создаете публичный API, я бы сказал никогда. Ниже мы будем ссылаться на переменную-член как на «свойство» объекта.
Вот что ваш суперкласс не может делать после того, как переменная-член будет защищена, а не закрыта с аксессорами:
лениво создавайте значение «на лету», когда свойство читается. Если вы добавите защищенный метод получения, вы можете лениво создать значение и передать его обратно.
знать, когда свойство было изменено или удалено. Это может привести к ошибкам, когда суперкласс делает предположения о состоянии этой переменной. Создание защищенного метода установки для переменной сохраняет этот контроль.
Установите точку останова или добавьте вывод отладки при чтении или записи переменной.
Переименуйте эту переменную-член, не просматривая весь код, который может ее использовать.
В общем, я думаю, что это был бы редкий случай, когда я бы рекомендовал создать защищенную переменную-член. Лучше потратить несколько минут на раскрытие свойства через геттеры / сеттеры, чем спустя несколько часов, отслеживая ошибку в каком-либо другом коде, который изменил защищенную переменную. Более того, вы застрахованы от добавления функций в будущем (например, отложенной загрузки) без нарушения зависимого кода. Позже это сделать сложнее, чем сейчас.
На уровне дизайна может быть уместным использовать защищенное свойство, но для реализации я не вижу преимуществ в сопоставлении его с защищенной переменной-членом, а не с методами доступа / мутатора.
Защищенные переменные-члены имеют существенные недостатки, поскольку они эффективно разрешают клиентскому коду (подклассу) доступ к внутреннему состоянию класса базового класса. Это мешает базовому классу эффективно поддерживать свои инварианты.
По той же причине защищенные переменные-члены также значительно затрудняют написание безопасного многопоточного кода, если только не гарантируется постоянство или не ограничивается одним потоком.
Методы аксессора / мутатора предлагают значительно большую стабильность API и гибкость реализации при обслуживании.
Кроме того, если вы сторонник объектно-ориентированного подхода, объекты взаимодействуют / взаимодействуют посредством отправки сообщений, а не чтения / настройки состояния.
Взамен они предлагают очень мало преимуществ. Я бы не стал удалять их из чужого кода, но сам я ими не пользуюсь.
В большинстве случаев использовать protected опасно, потому что вы несколько нарушаете инкапсуляцию вашего класса, которая вполне может быть разбита плохо спроектированным производным классом.
Но у меня есть один хороший пример: допустим, у вас есть некий универсальный контейнер. Он имеет внутреннюю реализацию и внутренние средства доступа. Но вам нужно предложить как минимум 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
. Я хочу доказать свою неправоту. Все, что вам нужно сделать, это написать класс с защищенным членом и запретить мне изменять его. Очевидно, что класс не должен быть окончательным, поскольку весь смысл использования protected - наследование. Либо что-то инкапсулировано, либо нет. Промежуточного состояния нет.
Короче да.
Защищенные переменные-члены позволяют получить доступ к переменной из любых подклассов, а также из любых классов в одном пакете. Это может быть очень полезно, особенно для данных только для чтения. Однако я не верю, что они когда-либо понадобятся, потому что любое использование защищенной переменной-члена может быть реплицировано с помощью частной переменной-члена и пары геттеров и сеттеров.
Для получения подробной информации о модификаторах доступа .Net перейдите сюда
У защищенных переменных-членов нет реальных преимуществ или недостатков, вопрос в том, что вам нужно в вашей конкретной ситуации. Обычно принято объявлять переменные-члены как частные и разрешать внешний доступ через свойства. Кроме того, некоторые инструменты (например, некоторые преобразователи O / R) ожидают, что данные объекта будут представлены свойствами, и не распознают общедоступные или защищенные переменные-члены. Но если вы знаете, что хотите, чтобы ваши подклассы (и ТОЛЬКО ваши подклассы) имели доступ к определенной переменной, нет причин не объявлять ее защищенной.