Почему многие разработчики против использования" защищенного " модификатора в ООП?

мой коллега принимает введение в объектно-ориентированное программирование класс и был задан дискуссионный вопрос его профессором:

почему многие разработчики выступают против использования" защищенного " модифера в / внутри классов?

когда вопрос был поднят за обедом, мои коллеги и я не могли придумать причин, почему кто-то может быть против С помощью protected модификатор на a класс. Установка предпосылки вопроса в сторону (что предполагает, что многие разработчики на самом деле против protected модификатор; они?), мы пытались выяснить, почему.

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

Почему разработчики могут быть против использования protected модификатор в их ООП дизайн?

Примечание: поскольку профессор задает общий вопрос ООП, не специфичный для какого-либо одного языка, я не уверен, что ответы могут быть взвешены по-разному из-за разных реализаций protected в C#, Java и т. д..

12 ответов


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

для разработчиков, которые используя данный класс (не расширяя его), защищенный блокирует доступ к потенциально сочным внутренним деталям реализации. Это то, что они будут скорбеть о.

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

  1. обновленная структура изменяет детали реализации, от которых зависит ваш код, и ваш код ломается
  2. дизайнеры понимают, что они не могут изменить реализацию, не нарушая много кода, поэтому они решили не выполнять полезные функции или исправления ошибок. Это называется кодовым параличом.

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

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

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

инкапсуляция-хорошая вещь, даже если она блокирует самый короткий путь к решению. ;>


потому что взаимодействие объектов превосходит наследование.

Джеймс Гослинг, как сообщается, сказал, что если он снова сделает Java, он обойдется без классов, и пояснил, что под "классами" он имел в виду наследование. protected бессмысленно (или сводится к private) без наследования (хотя и не в Java), но С наследование, он взрывается в слизистый общественные вырождаются, и тем более в Java (Программирование Scala глава 5).

Аллен Голуб пишет (в Holub по шаблонам который является Java-книгой, но велик с общей точки зрения ООП), что protected is public другим именем, особенно в Java, где . а защищенный символ-это тот, которому нельзя доверять, чтобы быть тем, что вы видите в классе, в котором он объявлен. другие классы (потомки) могут поменять его под носом. ты имеешь дело с эффектом йо-йо.

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


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

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

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

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


во-первых,protected не является гарантией конфиденциальности, если только единственный потребляемый класс В вашей цепочке наследования не запечатан (т. е. все базовые классы являются внутренними для сборки). Любой разработчик, который знает что-либо об ООП, может получить ваш класс и выставить все ваши внутренности миру.

используя internal на элементах вместо этого обычно решает эту проблему; только ваш код, расположенный в той же сборке, которую вы разрабатываете, может видеть внутренний элемент. Публичный детей вы выставляете за использование других может быть получено извне сборки, но внутренние компоненты, которые вы хотите скрыть, по-прежнему скрыты. Разработчики, создающие дочерние объекты или другие объекты в одной сборке, должны сообщать вам о том, что они используют, если ваши внутренние компоненты действительно чувствительны с точки зрения времени выполнения. Запомните это protected internal означает защищенный или внутренний, не защищенный и внутренний.

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

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

общее и хорошо принятое использование protected on методы для классов методов шаблона; это абстрактные классы, которые имеют открытый скелетный рабочий процесс, но позволяют или требуют расширения определенных частей их функциональности, чтобы быть полезными. Эти функции, как правило, высоко сплоченный и незначительный. Шаблон будет предоставлять общедоступную, не виртуальную "функцию управления" и несколько дескрипторов "помощникам" в качестве защищенных абстрактных или виртуальных методов. Это позволяет потребителям получить класс и реализовать помощников.

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

protected также является самым простым способом модульного тестирования внутренностей класса. Модульное тестирование-это хорошо, но если вы хотите написать тестовый прибор, который выполняет метод, этот метод должен быть доступен извне класса. Частная попросту нет. Внутренняя обычно представляет аналогичную проблему. Чтобы вообще разрешить тестирование внутренностей, они должны быть по крайней мере защищены, чтобы вы могли получить класс с целью экспонирования внутренностей. В противном случае вам нужна действительно oogly отражение, чтобы извлечь MethodInfo вашего частного метода и вызвать его (наряду с skipverification CAS разрешений).


злоупотребление protected потенциально может привести к нарушению Принцип Подстановки Лисков (L in SOLID) - PDF link. Принцип гласит, что классы должны быть написаны таким образом, что любой подкласс может быть заменен его базовым классом. У Роберта К. Мартина есть хорошая дискуссия об этом в гибкие принципы, шаблоны и практики в C#. Суть аргумента в том, что a подтипа является только подтипом, когда он заменить для его базового класса, а не просто наличие IS-отношения.

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

Это далеко не черно-белое, и нельзя было бы выпустить общее правило, указывающее, является ли или нет protected следует / не следует использовать. Бывают случаи, когда это действительно необходимо и есть бывают случаи, когда это не так. Необходимо использовать судебное решение по отдельному делу.


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


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

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

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


ресурсы :


protected члены приводят к тому, что ваш класс имеет два интерфейса: один для клиентов, которые выбирают не подкласс, и больший для клиентов, которые выбирают подкласс. protected классы вроде те же - они являются дополнениями к вашему public класс, который предположительно делать что-то, но они только для определенных клиентов.

иногда это действительно то, что вы хотите сделать: вы тщательно разработали оба интерфейса для двух разных вариантов использования. Абстрактный класс может попасть в эту категорию: у вас есть "исполнители", использующие protected+public интерфейс, и "пользователи", используя только public интерфейс. Могут быть некоторые полезные функции, которые вы можете предоставить разработчикам, которые пользователям не нужны, и могут быть конфигурационные материалы, которые пользователи не трогают, но разработчики должны настроить. Выполнение этого с защищенными методами просто избавляет вас от определения отдельного интерфейса для некоторой фабрики, чтобы вернуться к "пользователям". Так что я не думаю, что это вредно, хотя это может считаться немного причудливым, так как это не "нормальный" способ определения двух интерфейсов.

вещь, чтобы быть уверенным, хотя, когда вы используете protected, Это то, что вы на самом деле не нарушаете инкапсуляцию в ситуации, когда код, который вы хотите сломать, просто так происходит, является подклассом. Если что-то "должно" быть private, он protected нарушает инкапсуляцию, которую вы изначально намеревались. Плохой интерфейс по-прежнему плохой интерфейс, независимо от того, является ли он представлен миру или просто подклассам. Если на то пошло, есть вероятность, что любой простолюдин с улицы может подкласса ваш класс. Поэтому даже "просто подклассы" не очень ограничительны - ваш плохой protected интерфейс не далеко от public. Я думаю, что именно поэтому люди по умолчанию подозрительны protected - похоже, что он сметает зло под ковер, ограничивая, кто его видит, но на самом деле это не так.

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

разве этого недостаточно? Делать что-то лучше... лучше ;-)


одна из причин "защищенного" заключается в том, что классу может быть полезно включать в свой контракт вещи, которые истинны для класса, но не должны быть истинны для производных классов. Согласно принципу подстановки Лискова, все, что может быть сделано через открытые интерфейсы с объектом типа Q, также должно быть выполнимо с любым объектом, тип которого наследует Q. таким образом, Q не должен рекламировать любую способность, которая не может присутствовать при некоторых производных Q.

с другой стороны, предположим, что Q имеет некоторую способность, которая была бы полезна в некоторых производных, но не других, таких как клонирование. Базовый тип Q можно клонировать при соблюдении определенных условий, но некоторые подтипы могут не поддерживать клонирование. В этом случае Q должен выставлять как "защищенные" крючки, необходимые для поддержки клонирования. Подтипы, которые могут быть с пользой клонированы, могут использовать эти крючки и предоставлять метод клонирования. Те, которые не могут быть клонированы, не будут выставлять крючки.

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

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


потому что защищен public для своих подклассов, это своего рода API для подклассов. Это похоже на многие публичные методы для клиента.


в Java модификатор protected часто не имеет смысла.

Если вы разрабатываете однопакетное приложение и не ожидаете, что оно будет расширено (это не библиотека), то лучше использовать модификатор по умолчанию - package private.

Если вы создаете мульти-пакет прикладных программ, то вам может понадобиться модификатор protected позволяет бета.Сын, чтобы призвать Альфу.Отец-конструктор суперкласса (но в остальном Альфа.Отец конструктор недоступный.) Я буду спекулировать без доказательств, что разработчики предпочитают однопакетные приложения.

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


есть правило.."Используйте Private, если у вас нет веской причины не" Вы должны подумать, будет ли кто-то использовать ваши пакеты/классы и решить, что действительно нужно. Я никогда не использую protected, если я не собираюсь выпускать lib кому-то другому и только если это необходимо.