Должны ли вы когда-нибудь использовать защищенные переменные-члены?

StackOverflow https://stackoverflow.com/questions/37011

  •  09-06-2019
  •  | 
  •  

Вопрос

Должны ли вы когда-нибудь использовать защищенные переменные-члены?Каковы преимущества и какие проблемы это может вызвать?

Это было полезно?

Решение

Должны ли вы когда-нибудь использовать защищенные переменные-члены?

Зависит от того, насколько вы придирчивы к сокрытию состояния.

  • Если вы не хотите никакой утечки внутреннего состояния, то лучше всего объявить все ваши переменные-члены закрытыми.
  • Если вас действительно не волнует, что подклассы могут получать доступ к внутреннему состоянию, то protected достаточно хорош.

Если придет разработчик и подклассирует ваш класс, он может все испортить, потому что не понимает его до конца.С закрытыми участниками, отличными от общедоступного интерфейса, они не могут видеть конкретные детали реализации того, как все делается, что дает вам гибкость при изменении этого позже.

Другие советы

В настоящее время общее ощущение состоит в том, что они вызывают неоправданную связь между производными классами и их базами.

У них нет особого преимущества перед защищенными методами / свойствами (когда-то они могли иметь небольшое преимущество в производительности), и они также использовались чаще в эпоху, когда в моде было очень глубокое наследование, которого нет момент.

Как правило, если что-то не задумано как общедоступное, я делаю это частным.

Если возникает ситуация, когда мне нужен доступ к этой закрытой переменной или методу из производного класса, я меняю его с частного на защищенный.

Этого почти не бывает - я вообще не фанат наследования, так как это не очень хороший способ для моделирования большинства ситуаций. Во всяком случае, продолжайте, не беспокойтесь.

Я бы сказал, что это хорошо (и, вероятно, лучший способ сделать это) для большинства разработчиков.

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

Единственными реальными исключениями из этого являются случаи, когда вы отправляете двоичные DLL в виде черного ящика третьим лицам. Он состоит в основном из Microsoft, поставщиков «Custom DataGrid Control» и, возможно, нескольких других крупных приложений, которые поставляются с библиотеками расширяемости. Если вы не относитесь к этой категории, не стоит тратить время / усилия на то, чтобы беспокоиться о подобных вещах.

В общем, я хотел бы сохранить ваши защищенные переменные-члены в редком случае, когда вы имеете полный контроль над кодом, который их также использует. Если вы создаете публичный API, я бы сказал, никогда. Ниже мы будем ссылаться на переменную-член как " свойство " объекта.

Вот что ваш суперкласс не может сделать после создания защищенной переменной-члена, а не private-with-accessors:

<Ол>
  • лениво создает значение на лету, когда свойство читается. Если вы добавите защищенный метод получения, вы можете лениво создать значение и передать его обратно.

  • знает, когда свойство было изменено или удалено. Это может привести к ошибкам, когда суперкласс делает предположения о состоянии этой переменной. Создание защищенного метода установки для переменной сохраняет этот элемент управления.

  • Установите точку останова или добавьте выходные данные отладки, когда переменная будет считана или записана.

  • Переименуйте эту переменную-член без поиска во всем коде, который может ее использовать.

  • В общем, я думаю, что это был бы редкий случай, когда я бы рекомендовал создать защищенную переменную-член. Вам лучше потратить несколько минут на раскрытие свойства через методы получения / установки, чем несколько часов спустя, чтобы обнаружить ошибку в каком-то другом коде, который изменил защищенную переменную. Не только это, но вы застрахованы от добавления будущих функций (таких как отложенная загрузка) без нарушения зависимого кода. Это сложнее сделать позже, чем сейчас.

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

    Защищенные переменные-члены имеют существенные недостатки, поскольку они эффективно разрешают клиентскому коду (подклассу) доступ к внутреннему состоянию класса базового класса. Это мешает базовому классу эффективно поддерживать свои инварианты.

    По той же причине защищенные переменные-члены также значительно затрудняют написание безопасного многопоточного кода, если не гарантируется постоянная или ограниченная одним потоком.

    Методы доступа / мутатора обеспечивают значительно большую стабильность API и гибкость реализации при обслуживании.

    Кроме того, если вы являетесь пуристом OO, объекты взаимодействуют / обмениваются сообщениями, а не читают / устанавливают состояние.

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

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

    Например, если у меня есть класс, который определяет ширину и высоту визуализируемого объекта, и я защищаю эти переменные, я тогда не могу делать никаких предположений относительно (например) соотношения сторон.

    Критически, я могу никогда делайте эти предположения в любой момент с момента выпуска кода в виде библиотеки, поскольку даже если я обновлю свои настройки для поддержания соотношения сторон, у меня нет гарантии, что переменные устанавливаются через настройки или доступны через геттеры в существующем коде.

    Ни один подкласс моего класса также не может предоставить эту гарантию, поскольку они также не могут принудительно применять значения переменных, даже если в этом весь смысл их подкласса.

    В качестве примера:

    • У меня есть класс rectangle, ширина и высота которого хранятся как защищенные переменные.
    • Очевидным подклассом (в моем контексте) является класс "DisplayedRectangle", где единственное отличие заключается в том, что я ограничиваю ширину и высоту допустимыми значениями для графического отображения.
    • Но сейчас это невозможно, начиная с моего класса DisplayedRectangle не могу действительно ограничивайте эти значения, поскольку любой его подкласс может переопределять значения напрямую, при этом все еще обрабатываясь как DisplayedRectangle .

    Ограничивая переменные закрытыми, я могу затем обеспечить желаемое поведение с помощью установщиков или геттеров.

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

    Но у меня есть один хороший пример: допустим, вы можете использовать какой-то универсальный контейнер. Он имеет внутреннюю реализацию и внутренние средства доступа. Но вам нужно предложить как минимум 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 было бы такой болью, его следует запретить.

    Сводка . Таким образом, вы защитили данные, используемые производным классом. Тем не менее, мы должны принять во внимание тот факт, что базовый класс должен быть абстрактным.

    Короче говоря, да.

    Защищенные переменные-члены обеспечивают доступ к переменной из любых подклассов, а также из любых классов в одном пакете. Это может быть очень полезно, особенно для данных только для чтения. Однако я не верю, что они когда-либо необходимы, поскольку любое использование защищенной переменной-члена может быть реплицировано с использованием закрытой переменной-члена и нескольких методов получения и установки.

    Просто для записи, под пунктом 24 «Исключительного C ++» в одной из сносок Саттер идет " вы бы никогда не написали класс, имеющий открытую или защищенную переменную-член право? (Независимо от плохого примера, поданного некоторыми библиотеками.) & Quot;

    Для получения подробной информации о модификаторах доступа .Net иди сюда

    Нет никаких реальных преимуществ или недостатков для защищенных переменных-членов, это вопрос того, что вам нужно в вашей конкретной ситуации. В целом, принято объявлять переменные-члены как закрытые и разрешать внешний доступ через свойства. Кроме того, некоторые инструменты (например, некоторые средства отображения O / R) ожидают, что данные объекта будут представлены свойствами, и не распознают общедоступные или защищенные переменные-члены. Но если вы знаете, что хотите, чтобы ваши подклассы (и ТОЛЬКО ваши подклассы) обращались к определенной переменной, нет причин не объявлять ее защищенной.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top