private
Полезна ли видимость полей / свойств / атрибутов класса? В ООП рано или поздно вы создадите подкласс класса, и в этом случае полезно понять и иметь возможность полностью изменить реализацию.
Одна из первых вещей, которые я делаю, когда делаю подкласс класса, - это изменение набора private
методов на protected
. Тем не менее, важно скрывать детали от внешнего мира - поэтому нам нужно, protected
а не только public
.
Мой вопрос: знаете ли вы о важном случае использования, где private
вместо protected
хорошего инструмента, или двух вариантов « protected
& public
» будет достаточно для языков ООП?
object-oriented
encapsulation
access-modifiers
Адам Либуша
источник
источник
Ответы:
Потому что, как вы говорите, по-
protected
прежнему оставляет вам возможность «полностью изменить реализацию». Он не защищает ничего в классе.Почему мы заботимся о «подлинной защите» вещей внутри класса? Потому что иначе было бы невозможно изменить детали реализации, не нарушая клиентский код . Иными словами, люди, которые пишут подклассы, также являются «внешним миром» для человека, который написал оригинальный базовый класс.
На практике
protected
члены - это, по сути, «открытый API для подклассов» класса, и они должны оставаться стабильными и обратно совместимыми так же, как иpublic
члены. Если бы у нас не было возможности создавать настоящихprivate
участников, то ничего в реализации никогда не было бы безопасно изменить, потому что вы не смогли бы исключить вероятность того, что (не злонамеренный) клиентский код каким-то образом зависел от Это.Между прочим, хотя «в ООП рано или поздно вы создадите подкласс класса» технически верно, ваш аргумент, похоже, делает гораздо более сильное предположение, что «рано или поздно вы сделаете подкласс каждый класс ", что почти наверняка не так.
источник
private
когда-либо на самом деле имел смысл для меня. Перспективу lib нужно использовать чаще, иначе любой, кому нравится (C-like) менталитет кодирования «абсолютный контроль, абсолютная ответственность», может пометить его как «защищающий меня от себя». Обратите внимание, что я все еще использовалprivate
ранее, я просто всегда чувствовал хорошую документацию и соглашение об именах, вроде того,_foo
чтобы указать, что вы, вероятно, не должны связываться с ним, было эквивалентно, если не лучше. Возможность детерминистически сказать «ничего не сломается» - законная,private
единственная особенность.Это не верно. Не каждый класс должен быть разделен на подклассы, и некоторые статически типизированные языки ООП даже имеют функции, предотвращающие это, например,
final
(Java и C ++) илиsealed
(C #).Нет, это не так. Для класса хорошо иметь возможность четко определять свой открытый интерфейс и сохранять свои инварианты, даже если он унаследован от.
В общем, контроль доступа - это разделение. Вы хотите, чтобы отдельная часть кода была понята без детального понимания того, как она взаимодействует с остальной частью кода. Частный доступ позволяет это. Если все хотя бы защищено, вы должны понимать, что делает каждый подкласс, чтобы понять, как работает базовый класс.
Или, говоря словами Скотта Мейерса: на частные части класса влияет ограниченное количество кода: код самого класса.
На открытые части потенциально влияет каждый бит кода, который существует, и каждый бит кода, который еще предстоит написать, что представляет собой бесконечное количество кода.
Защищенные части потенциально затрагиваются каждым существующим подклассом, и каждый подкласс еще не написан, что также является бесконечным количеством кода.
Вывод заключается в том, что защищенный дает вам немного больше, чем публичный, тогда как закрытый дает вам реальное улучшение. Это наличие спецификатора защищенного доступа, который является сомнительным, а не частным.
источник
virtual
, по причинам, изложенным в этом ответе.protected
для методов, а не для членов данных.sealed
большие части библиотеки .net и IIRC, почему он хотел бы запереть все остальное. В тот момент, когда вы позволяете сторонним наследникам начать трогать внутренние значения, вам нужно добавить огромное количество проверок / проверок работоспособности для каждого метода, потому что вы больше не можете доверять никаким инвариантам проекта относительно внутреннего состояния объектов.Да, частные поля абсолютно необходимы. Только на этой неделе мне нужно было написать собственную реализацию словаря, в которой я контролировал то, что было помещено в словарь. Если бы поле словаря стало защищенным или общедоступным, то элементы управления, которые я так тщательно написал, можно было бы легко обойти.
Закрытые поля, как правило, обеспечивают гарантии того, что данные соответствуют исходному кодеру. Сделайте все защищенным / публичным, и вы будете ездить на тренере и лошадях через эти процедуры и проверку.
источник
private
это просто способ сказать «не трогайте их», и он должен применяться в контракте компилятора. В таких системах, как .NET, это также имеет важные последствия для безопасности - вы можете трогать права других только тогда, когда у вас есть полное доверие (в основном, эквивалент доступа администратора / root).При попытке формально рассуждать о правильности программы объектно - ориентированным это типичное использовать модульный подход , включающий объектные инварианты . В этом подходе
Модульное рассуждение об объекте происходит следующим образом (по крайней мере, в первом приближении)
Представьте, что мы проверяем,
object A
используя подход выше. А теперь хочу проверитьmethod g
изobject B
которых требуетmethod f
отobject A
. Модульные рассуждения позволяют рассуждатьmethod g
без пересмотра реализацииmethod f
. При условии, что мы можем установить инвариантobject A
и предварительное условиеmethod f
на сайте вызова,method g,
мы можем принять условие postmethod f
как сводку поведения вызова метода. Кроме того, мы также узнаем, что после возврата вызова инвариантA
все еще сохраняется.Эта модульность рассуждений позволяет нам формально думать о больших программах. Мы можем рассуждать о каждом из методов в отдельности, а затем составлять результаты этого рассуждения, в свою очередь, рассуждать о больших частях программы.
Частные поля очень полезны в этом процессе. Чтобы знать, что инвариант объекта продолжает удерживаться между двумя вызовами метода для этого объекта, мы обычно полагаемся на тот факт, что объект не изменился за прошедший период.
Чтобы модульные рассуждения работали в контексте, где объекты не имеют закрытых полей, нам нужно было бы каким-то образом убедиться, что независимо от того, какое поле было установлено другим объектом, инвариант всегда был восстановлен (после поля установлен). Трудно представить объектный инвариант, который будет иметь значение независимо от того, какое значение имеют поля объекта, а также полезен для обоснования правильности программы. Нам, вероятно, пришлось бы придумать какое-то сложное соглашение о доступе к полю. И, вероятно, также потерять некоторые (в худшем случае даже все) нашу способность мыслить модульно.
Охраняемые поля
Защищенные поля восстанавливают нашу способность мыслить модульно. В зависимости от языка
protected
может быть ограничена возможность установки поля для всех подклассов или всех подклассов и классов одного пакета. Часто у нас нет доступа ко всем подклассам, когда мы рассуждаем о правильности объекта, который пишем. Например, вы можете писать компонент или библиотеку, которая впоследствии будет использоваться в более крупной программе (или нескольких более крупных программах), некоторые из которых, возможно, еще даже не были написаны. Как правило, вы не будете знать, если и каким образом это может быть подкласса.Однако подкласс обычно обязан поддерживать объектный инвариант класса, который он расширяет. Таким образом, в языке, где protect означает только «подкласс», и где мы дисциплинированы, чтобы гарантировать, что подклассы всегда поддерживают инварианты их суперкласса, вы можете утверждать, что выбор использования защищенного вместо частного теряет только минимальную модульность ,
Хотя я говорил о формальных рассуждениях, часто думают, что когда программисты неформально рассуждают о правильности своего кода, они также иногда полагаются на подобные типы аргументов.
источник
private
переменные в классе лучше, чемprotected
по той же причине, по которойbreak
оператор внутриswitch
блока лучше, чемgoto label
оператор; что люди-программисты подвержены ошибкамprotected
Переменные поддаются непреднамеренному злоупотреблению (ошибкам программиста), так же какgoto
утверждение поддается созданию кода для спагетти.Можно ли написать рабочий код без ошибок, используя
protected
переменные класса? Да, конечно! Так же, как можно написать рабочий код без ошибок, используяgoto
; но, как гласит клише: «Только потому, что ты можешь, не значит, что ты должен!»Классы и даже парадигма ОО существуют для защиты от несчастных, склонных к ошибкам программистов-людей, совершающих ошибки. Защита от человеческих ошибок так же хороша, как защитные меры, встроенные в класс. Выполнение вашего класса
protected
- это эквивалент огромной дыры в стенах крепости.Базовые классы абсолютно не знают производных классов. Что касается базового класса, на
protected
самом деле он не дает вам большей защиты, чемpublic
, потому что ничто не мешает производному классу создаватьpublic
метод получения / установки, который ведет себя как бэкдор.Если базовый класс разрешает беспрепятственный доступ к своим внутренним деталям реализации, то для самого класса становится невозможным защищаться от ошибок. Базовые классы не имеют абсолютно никаких знаний о своих производных классах и, следовательно, не имеют возможности защититься от ошибок, допущенных в этих производных классах.
Лучшее, что может сделать базовый класс, - это спрятать как можно больше своей реализации
private
и наложить достаточное количество ограничений, чтобы не допустить прерывания изменений из производных классов или чего-либо еще вне класса.В конечном счете, языки высокого уровня существуют, чтобы минимизировать человеческие ошибки. Хорошие методы программирования (такие как принципы SOLID ) также существуют, чтобы минимизировать человеческие ошибки.
Разработчики программного обеспечения, которые игнорируют хорошие методы программирования, имеют гораздо больше шансов на неудачу и с большей вероятностью будут создавать неработоспособные не поддерживаемые решения. Те, кто следуют передовым практикам, имеют гораздо меньше шансов на неудачу, и с большей вероятностью могут предложить работоспособные и поддерживаемые решения.
источник
public
деталей реализации, потому что он поддается неуправляемому коду. Компетентный программист будет избегать этого,goto
потому что он поддается недопустимому коду. Однако реальный мир таков, что иногда вы теряетесь в ужасном беспорядке унаследованного кода и не имеете другого выбора, кроме как использовать егоgoto
, и по этой же причине у вас иногда нет другого выбора, кроме как использовать открытые / защищенные детали реализации.public
свойства / интерфейс, но не с кодом, который использует толькоgoto
.goto
или это только потому, что вы читали такие статьи? Я понимаю, почемуgoto
это зло, потому что я провел 2 года, работая над древним кодом, который его использует; и я потратил еще больше времени на работу с кодом, где у «классов» есть утечка деталей реализации,public
аfriend
спецификаторы используются как быстрые / простые / грязные хаки. Я не принимаю аргумент, что «публичное раскрытие деталей реализации» не может вызвать переплетение спагетти на том же уровне серьезности, что иgoto
потому, что это абсолютно возможно.У наследуемых классов есть два контракта - один с держателями ссылок на объекты и с производными классами. Открытые члены связаны договором с держателями ссылок, а защищенные участники связаны договором с производными классами.
Создание членов
protected
делает его более универсальным базовым классом, но часто ограничивает способы изменения будущих версий класса. Создание членовprivate
позволяет автору класса более гибко изменять внутреннюю работу класса, но ограничивает виды классов, которые могут быть из него полезны.Например,
List<T>
в .NET резервное хранилище становится приватным; если бы он был защищен, производные типы могли бы делать некоторые полезные вещи, которые иначе были бы невозможны, но будущие версииList<T>
всегда должны были бы использовать свое неуклюжее монолитное резервное хранилище даже для списков, содержащих миллионы элементов. Создание частного хранилища резервных копий позволит будущим версиямList<T>
использовать более эффективное хранилище резервных копий, не нарушая производные классы.источник
Я думаю, что в вашем аргументе есть ключевое предположение, что когда кто-то пишет класс, он не знает, кто может расширить этот класс в будущем и по какой причине . Учитывая это предположение, ваш аргумент будет иметь смысл, потому что каждая переменная, которую вы делаете приватной, потенциально может отрезать путь развития в будущем. Однако я бы отверг это предположение.
Если это предположение отклонено, то есть только два случая для рассмотрения.
В этом случае автор знает, что кто-то будет расширять класс и почему, и поэтому точно будет знать, что делать защищенным, а что - частным. Они используют различие между частным и защищенным для передачи своего рода интерфейса пользователю, создающему подкласс.
Этот случай должен быть редким (можно утверждать, что он не является законным), и не предпочтительнее, чем просто изменить исходный класс в исходной кодовой базе. Это также может быть признаком плохого дизайна. В этих случаях я бы предпочел, чтобы человек, взломавший поведение, просто использовал другие хаки, такие как друзья (C / C ++) и
setAccessible(true)
(Java).Я думаю, что это безопасно отрицать это предположение.
Это обычно возвращается к идее композиции, а не наследования . Наследование часто преподается как идеальный способ сократить повторное использование кода, однако оно редко должно быть первым выбором для повторного использования кода. Я не простой бросовым аргумент , и он может быть довольно сложным и спорным понять. Однако в своем опыте моделирования предметной области я обнаружил, что редко использую наследование без четкого понимания того, кто будет наследовать мой класс и почему.
источник
Все три уровня доступа имеют свой сценарий использования, ООП будет неполным без какого-либо из них. Обычно вы делаете
И вы отклоняетесь от этой общей схемы, только если есть веская причина ™ . Остерегайтесь «это облегчит мою жизнь, когда я смогу свободно получить к ней доступ извне» (и снаружи здесь также есть подклассы). Когда я реализую иерархии классов, я часто начинаю с классов, которые не имеют защищенных членов, пока не подхожу к их подклассам / расширению / специализации, становясь базовыми классами фреймворка / инструментария и иногда перемещая часть их оригинальной функциональности на один уровень вверх.
источник
Возможно, более интересный вопрос: зачем нужен любой другой тип поля, кроме частного? Когда подкласс должен взаимодействовать с данными суперкласса, это напрямую создает прямую связь между ними, в то время как использование методов для обеспечения взаимодействия между ними обеспечивает уровень косвенности, который позволяет вносить изменения в суперкласс, который в противном случае был бы очень сложным.
Ряд языков (например, Ruby и Smalltalk) не предоставляют общедоступные поля, поэтому разработчикам не рекомендуется разрешать прямую связь с реализациями их классов, но почему бы не пойти дальше и иметь только закрытые поля? Не было бы никакой потери общности (потому что суперкласс всегда может обеспечить защищенные средства доступа для подкласса), но это обеспечило бы, чтобы классы всегда имели хотя бы небольшую степень изоляции от своих подклассов. Почему это не более распространенный дизайн?
источник
Здесь много хороших ответов, но я все равно добавлю два цента. :-)
Приватность хороша по той же причине, что и глобальные данные плохо.
Если класс объявляет данные закрытыми, то вы абсолютно точно знаете, что единственным кодом, который связывается с этими данными, является код в классе. Когда есть ошибка, вам не нужно искать во всем творении, чтобы найти все места, которые могут изменить эти данные. Вы знаете, что это в классе. Когда вы вносите изменения в код и что-то меняете в том, как используется это поле, вам не нужно отслеживать все потенциальные места, которые могут использовать это поле, и изучать, не нарушит ли их запланированное изменение. Вы знаете, что единственные места в классе.
У меня было много, много раз, когда мне приходилось вносить изменения в классы, находящиеся в библиотеке и используемые несколькими приложениями, и мне приходилось очень осторожно действовать, чтобы не сломать какое-либо приложение, о котором я ничего не знаю. Чем больше открытых и защищенных данных, тем больше вероятность проблем.
источник
Я думаю, что стоит упомянуть некоторые особые мнения.
Теоретически, хорошо контролировать уровень доступа по всем причинам, указанным в других ответах.
На практике слишком часто при просмотре кода я вижу людей (которые любят использовать приватный), меняющих уровень доступа с частного -> защищенный и не слишком часто с защищенного -> публичный. Почти всегда изменение свойств класса включает в себя изменение сеттеров / геттеров. Они потратили большую часть моего времени (проверка кода) и их (изменение кода).
Меня также раздражает, что это означает, что их классы не закрыты для модификации.
Это было с внутренним кодом, где вы всегда можете изменить его, если вам нужно. Ситуация хуже со сторонним кодом, когда изменить код не так просто.
Так сколько программистов думают, что это хлопотно? Ну, как многие используют языки программирования, которые не имеют приватных? Конечно, люди не просто используют эти языки, потому что у них нет личных спецификаторов, но это помогает упростить языки, и важна простота.
Имо, это очень похоже на динамическую / статическую типизацию. Теоретически статическая типизация очень хороша. На практике, это только мешает , как 2% ошибок неразумной эффективности динамической типизации ... . Использование частного, вероятно, предотвращает ошибку меньше.
Я думаю, что принципы SOLID хороши, я хочу, чтобы люди заботились о них больше, чем о создании класса с публичным, защищенным и приватным.
источник
Я также хотел бы добавить еще один практический пример того, почему
protected
этого недостаточно. В моем университете первые годы предпринимают проект, в рамках которого им приходится разрабатывать настольную версию настольной игры (для которой позже разрабатывается ИИ, и он подключается к другим игрокам по сети). Некоторый частичный код предоставляется им для начала работы, включая среду тестирования. Некоторые свойства основного игрового класса представленыprotected
так, чтобы к ним имели доступ тестовые классы, расширяющие этот класс. Но эти поля не являются конфиденциальной информацией.В ТОМ для блока я часто вижу студенты просто сделать весь свой добавленный код
protected
илиpublic
(возможно , потому , что они видели другиеprotected
иpublic
вещи , и предположили , что они должны последовать этот примеру). Я спрашиваю их, почему их уровень защиты неуместен, и многие не знают почему. Ответ заключается в том, что конфиденциальная информация, которую они выставляют подклассам, означает, что другой игрок может обманывать, просто расширяя этот класс и получая доступ к высокочувствительной информации для игры (по сути, скрытое местоположение противников, я думаю, похоже, как было бы, если бы вы мог видеть фигуры ваших противников на доске линкора, расширяя класс). Это делает их код очень опасным в контексте игры.Помимо этого, есть много других причин, чтобы сохранить что-то частное даже для ваших подклассов. Это может быть скрыть детали реализации, которые могут испортить правильную работу класса, если они изменены кем-то, кто не обязательно знает, что они делают (в основном думая о других людях, использующих здесь ваш код).
источник
Частные методы / переменные, как правило, будут скрыты от подкласса. Это может быть хорошо.
Закрытый метод может делать предположения о параметрах и оставлять проверку работоспособности вызывающей стороне.
Защищенный метод должен проверять правильность входных данных.
источник
«Закрытый» означает: не предназначен для изменения или доступа к кому-либо, кроме самого класса. Не предназначен для изменения или доступа подклассов. Подклассы? Какие подклассы? Вы не должны подкласс этого!
«защищенный» означает: предназначен только для изменения или доступа классов или подклассов. Вероятно, вычет, что вы должны подкласс, иначе почему "защищен", а не "частный"?
Здесь есть четкая разница. Если я сделаю что-то личное, ты должен держать свои грязные пальцы подальше от этого. Даже если вы подкласс.
источник
Некоторые рассуждения о
private
противprotected
методов :private
методы предотвращают повторное использование кода. Подкласс не может использовать код в закрытом методе и может нуждаться в его повторной реализации или повторной реализации метода (ов), которые изначально зависят от закрытого метода & c.С другой стороны, любой метод, который не
private
может рассматриваться как API, предоставляемый классом «внешнему миру», в том смысле, что сторонние подклассы также рассматриваются как «внешний мир», как кто-то другой предложил в своем ответе. уже.Это плохо? - Я так не думаю.
Конечно, (псевдо) публичный API блокирует первоначального программиста и препятствует рефакторингу этих интерфейсов. Но с другой стороны, почему программист не должен разрабатывать свои собственные «детали реализации» таким чистым и стабильным способом, как его общедоступный API? Должен ли он использовать
private
так, чтобы он мог быть небрежным в структурировании своего "частного" кода? Думая, может быть, он мог бы почистить это позже, потому что никто не заметит? - нетПрограммист должен также немного подумать о своем «частном» коде, чтобы структурировать его таким образом, чтобы это позволяло или даже способствовало повторному использованию как можно большего количества кода. Тогда не приватные части могут не стать таким бременем в будущем, как некоторые опасаются.
Много кода (фреймворка), который я вижу, принимает непоследовательное использование
private
:,protected
обычно встречаются не финальные методы, которые делают только что-то большее, чем делегирование частному методу.protected
Неокончательные методы, чей контракт может быть выполнен только через прямой доступ к приватным полям.Эти методы не могут быть логически переопределены / улучшены, хотя технически нет ничего, что могло бы сделать это (компилятором) очевидным.
Хотите расширяемость и наследование? Не делай свои методы
private
.Не хотите, чтобы определенное поведение вашего класса изменилось? Сделай свои методы
final
.Неужели ваш метод не может быть вызван вне определенного, четко определенного контекста? Сделайте свой метод
private
и / или подумайте, как сделать требуемый четко определенный контекст доступным для повторного использования через другойprotected
метод-обертку.Вот почему я рекомендую использовать
private
экономно. И не путатьprivate
сfinal
. - Если реализация метода жизненно важна для общего контракта класса и, следовательно, не должна быть заменена / переопределена, сделайте этоfinal
!Для полей
private
не очень плохо. До тех пор, пока поле (я) может быть разумно «использовано» с помощью соответствующих методов (это не такgetXX()
илиsetXX()
!).источник
private
противprotected
первоначального вопроса, дает ошибочный ( «использоватьprivate
экономно»?) И идет вразрез с общим консенсусом по дизайну OO. Использование неprivate
имеет ничего общего с сокрытием неаккуратного кода.private
помогает поддерживать API в чистоте.Приватный : когда у вас есть что-то, что никогда не будет полезным для какого-либо подкласса для вызова или переопределения.
Защищено : когда у вас есть что-то, что имеет специфическую для подкласса реализацию / константу.
Пример:
источник
Мне было трудно понять этот вопрос, поэтому я хотел бы поделиться частью своего опыта:
$classInstance->field
. И хитрость в том, что это "это оно". Дети вашего класса получат к нему полный доступ, потому что это их законная внутренняя часть.ОБНОВЛЕНИЕ: практический пример с реальной задачей, которую я решил. Вот оно: у вас есть токен, например USB или LPT (это был мой случай), и у вас есть промежуточное ПО. Токен запрашивает у вас пин-код, открывается, если он правильный, и вы можете отправить зашифрованную часть и номер ключа для расшифровки. Ключи хранятся в токене, вы не можете их прочитать, используйте только их. И были временные ключи для сеанса, подписанные ключом в токене, но хранящиеся в самом промежуточном программном обеспечении. Ключ temp не должен был просачиваться где-либо снаружи, просто существовать на уровне драйвера. И я использовал приватные поля для хранения этого временного ключа и некоторых данных, связанных с аппаратным соединением. Поэтому никакие производные не могли использовать не только общедоступный интерфейс, но и некоторые защищенные«удобные» подпрограммы, которые я сделал для задачи, но не смогли открыть сейф с ключами и HW-взаимодействием. Имеет смысл?
источник