Чистый код предлагает избегать защищенных переменных в разделе «Вертикальное расстояние» главы «Форматирование»:
Концепции, которые тесно связаны, должны быть расположены вертикально близко друг к другу. Очевидно, что это правило не работает для концепций, которые принадлежат отдельным файлам. Но тогда тесно связанные понятия не должны быть разделены на разные файлы, если у вас нет веских причин. Действительно, это одна из причин, по которой следует избегать защищенных переменных .
В чем причина?
code-quality
clean-code
variables
Matsemann
источник
источник
Ответы:
Защищенных переменных следует избегать, потому что:
Но, как видите, все они «склонны». Иногда защищенный элемент является наиболее элегантным решением. И защищенные функции имеют тенденцию иметь меньше этих проблем. Но есть ряд вещей, которые заставляют их относиться с осторожностью. Со всем, что требует такого рода заботы, люди будут совершать ошибки, а в мире программирования это означает ошибки и проблемы проектирования.
источник
Это действительно та же самая причина, по которой вы избегаете глобалов, только в меньшем масштабе. Это потому, что трудно найти везде, где переменная читается, или, что еще хуже, записывается, и трудно согласовать использование. Если использование ограничено, как, например, запись в одном очевидном месте в производном классе и чтение в одном очевидном месте в базовом классе, тогда защищенные переменные могут сделать код более понятным и лаконичным. Если вы испытываете желание читать и записывать переменную в разных файлах, лучше ее инкапсулировать.
источник
Я не читал книгу, но могу понять, что имел в виду дядя Боб.
Если вы надеваете
protected
что-то, это означает, что класс может наследовать это. Но переменные-члены должны принадлежать классу, в котором они содержатся; это часть базовой инкапсуляции. Установкаprotected
переменной-члена нарушает инкапсуляцию, поскольку теперь производный класс имеет доступ к деталям реализации базового класса. Это та же проблема, которая возникает, когда вы создаете переменнуюpublic
в обычном классе.Чтобы исправить проблему, вы можете инкапсулировать переменную в защищенное свойство, например так:
Это позволяет
name
безопасно устанавливать его из производного класса, используя аргумент конструктора, не раскрывая подробности реализации базового класса.источник
protected
иpublic
членами. ЕслиBaseType
выставляется открытый член, это означает, что все производные типы должны иметь один и тот же элемент, работающий одинаково, посколькуDerivedType
экземпляр может быть передан в код, который получаетBaseType
ссылку и ожидает использовать этот элемент. Напротив, еслиBaseType
выставляетprotected
участника, тоDerivedType
может ожидать доступа к этому членуbase
, ноbase
не может быть ничем иным, кромеBaseType
.Аргумент дяди Боба - это, в первую очередь, аргумент расстояния: если у вас есть концепция, важная для класса, объедините эту концепцию вместе с классом в этом файле. Не разделены между двумя файлами на диске.
Защищенные переменные-члены разбросаны в двух местах, и это выглядит как волшебство. Вы ссылки на эту переменную, но она не определена здесь ... где это она определяется? И так начинается охота. Лучше
protected
вообще избежать его аргументации.Теперь я не верю, что правилу нужно постоянно подчиняться букве (например: не пользуйся
protected
). Посмотрите на дух того, к чему он стремится ... объедините связанные вещи в один файл - используйте для этого методы программирования и функции. Я бы порекомендовал вам не переоценивать и не вдаваться в детали этого.источник
Некоторые очень хорошие ответы уже здесь. Но одна вещь, которую нужно добавить:
В большинстве современных ОО-языков хорошая привычка (в Java AFAIK это необходимо) помещать каждый класс в отдельный файл (пожалуйста, не думайте о C ++, где у вас часто есть два файла).
Таким образом, практически невозможно сохранить функции в производном классе, обращающиеся к защищенной переменной базового класса, «вертикально расположенной» в исходном коде к определению переменной. Но в идеале они должны храниться там, поскольку защищенные переменные предназначены для внутреннего использования, что делает функции, обращающиеся к ним, часто «тесно связанной концепцией».
источник
Основная идея заключается в том, что «поле» (переменная уровня экземпляра), которое объявлено как защищенное, вероятно, более заметно, чем должно быть, и менее «защищено», чем вам может показаться. В C / C ++ / Java / C # отсутствует модификатор доступа, который эквивалентен «доступным только дочерним классам в одной сборке», что дает вам возможность определять свои собственные дочерние элементы, которые могут получить доступ к полю в вашей сборке, но не разрешать детям, созданным в других сборках, одинаковый доступ; C # имеет внутренние и защищенные модификаторы, но их объединение делает доступ «внутренним или защищенным», а не «внутренним и защищенным». Таким образом, защищенное поле доступно любому ребенку, независимо от того, написали ли вы этот ребенок или кто-то другой. Защищен, таким образом, открытая дверь для хакера.
Кроме того, поля по определению практически не имеют проверки, присущей их изменению. в C # вы можете сделать один readonly, что делает типы значений фактически постоянными и ссылочные типы не могут быть повторно инициализированы (но все еще очень изменчивы), но это все. Как таковые, даже защищенные, ваши дети (которым вы не можете доверять) имеют доступ к этому полю и могут установить для него что-то недопустимое, что делает состояние объекта несовместимым (что-то, чего следует избегать).
Принятый способ работы с полями - сделать их приватными и получить к ним доступ с помощью свойства и / или метода получения и установки. Если все потребители класса нуждаются в значении, сделайте получатель (по крайней мере) публичным. Если это нужно только детям, защитите добытчик.
Другой подход, который отвечает на вопрос, это спросить себя; почему код в дочернем методе должен иметь возможность напрямую изменять данные моего состояния? Что это говорит об этом коде? Это аргумент "вертикального расстояния" на его лице. Если в дочернем элементе есть код, который должен напрямую изменять родительское состояние, то, возможно, этот код должен в первую очередь принадлежать родителю?
источник
Это интересная дискуссия.
Честно говоря, хорошие инструменты могут смягчить многие из этих «вертикальных» проблем. На самом деле, на мой взгляд, понятие «файл» на самом деле препятствует разработке программного обеспечения - над чем работает проект Light Table (http://www.chris-granger.com/2012/04/12/light- таблица --- а-новый-ида-концепция /).
Уберите файлы с картинки, и защищенная область станет намного привлекательнее. Защищенная концепция становится наиболее понятной в таких языках, как SmallTalk, где любое наследование просто расширяет исходную концепцию класса. Он должен делать все и иметь все, что делал родительский класс.
По моему мнению, иерархии классов должны быть как можно более мелкими, с большинством расширений, происходящих из композиции. В таких моделях я не вижу причин, по которым частные переменные не должны быть защищены. Если расширенный класс должен представлять расширение, включающее в себя все поведение родительского класса (что, я считаю, должно и, следовательно, полагает, что закрытые методы по своей сути плохие), почему расширенный класс не должен также представлять хранение таких данных?
Иными словами, в любом случае, когда я считаю, что закрытая переменная подойдет лучше защищенной, наследование не является решением проблемы в первую очередь.
источник
Как там говорится, это только одна из причин, то есть логически связанный код должен быть помещен в физически связанные объекты (файлы, пакеты и другие). В меньшем масштабе это то же самое, что и причина, по которой вы не помещаете класс, который делает запросы к БД, внутри пакета с классами, отображающими пользовательский интерфейс.
Однако главная причина того, что защищенные переменные не рекомендуются, заключается в том, что они имеют тенденцию нарушать инкапсуляцию. Переменные и методы должны иметь как можно более ограниченную видимость; для дальнейшей ссылки см. « Эффективная Java» Джошуа Блоха , пункт 13 - «Минимизируйте доступность классов и членов».
Тем не менее, вы не должны воспринимать все эти рекламные объявления только как линию гильдии; если защищенные переменные очень плохи, они не были бы помещены в язык в первую очередь. Разумное место для защищенных полей imho находится внутри базового класса из среды тестирования (классы интеграционного тестирования расширяют ее).
источник
Я считаю, что использование защищенных переменных для тестов JUnit может быть полезным. Если вы сделаете их приватными, вы не сможете получить к ним доступ во время тестирования без размышлений. Это может быть полезно, если вы тестируете сложный процесс с помощью множества изменений состояния.
Вы можете сделать их закрытыми, используя защищенные методы получения, но если переменные будут использоваться только во время теста JUnit, я не уверен, что это лучшее решение.
источник
Да, я согласен, что это несколько странно, учитывая, что (насколько я помню) в книге также обсуждаются все не частные переменные как нечто, чего следует избегать.
Я думаю, что проблема с защищенным модификатором заключается в том, что член не только подвергается воздействию подклассов, но также становится видимым для всего пакета. Я думаю, что это двойной аспект, который дядя Боб использует здесь. Конечно, не всегда можно обойтись, если есть защищенные методы и, следовательно, квалификация защищенной переменной.
Я думаю, что более интересный подход, чтобы сделать все общественности, что будет заставлять вас есть маленькие классы, которые, в свою очередь, оказывает весь вертикальное расстояние метрическую несколько спорным.
источник