Зачем нам нужны закрытые переменные в классах?
В каждой книге по программированию, которую я прочитал, говорится, что это частная переменная, именно так вы ее определяете, но на этом остановимся.
Формулировка этих объяснений всегда казалась мне такой, будто у нас действительно кризис доверия к нашей профессии. Объяснения всегда звучали так, как будто другие программисты испортили наш код. Тем не менее, есть много языков программирования, которые не имеют частных переменных.
Что помогают предотвратить частные переменные?
Как вы решаете, должна ли конкретная собственность быть частной или нет? Если по умолчанию каждое поле ДОЛЖНО быть приватным, то почему в классе есть открытые члены данных?
При каких обстоятельствах переменная должна быть опубликована?
источник
Ответы:
Это не столько вопрос доверия, сколько вопрос управления сложностью.
К общедоступному члену можно получить доступ извне класса, что по практическим соображениям означает «потенциально где угодно». Если с открытым полем что-то идет не так, виновник может быть где угодно, и поэтому, чтобы отследить ошибку, вам, возможно, придется взглянуть на довольно много кода.
Доступ к закрытому члену, напротив, можно получить только из одного и того же класса, поэтому, если с этим что-то не так, обычно есть только один исходный файл для просмотра. Если в вашем проекте есть миллион строк кода, но ваши классы невелики, это может сократить ваши усилия по отслеживанию ошибок в 1000 раз.
Другое преимущество связано с понятием «сцепление». Открытый член
m
класса,A
который используется другим классом,B
вводит зависимость: если вы изменяетеm
inA
, вы также должны проверить использованиеm
inB
. Хуже того, ничто в классе неA
говорит вам, гдеm
используется, поэтому вам снова придется искать по всей базе кода; если вы пишете библиотеку, вам даже нужно убедиться, что код за пределамиваш проект не ломается из-за ваших изменений. На практике библиотеки склонны придерживаться своих оригинальных сигнатур методов как можно дольше, независимо от того, насколько они болезненны, а затем вводить блок критических изменений при обновлении основной версии. В отличие от закрытых членов, вы можете сразу исключить зависимости - к ним нельзя получить доступ извне, поэтому все зависимости содержатся внутри класса.В этом контексте «другие программисты» включают ваше будущее и прошлое. Скорее всего , вы знаете , в настоящее время , что вы не должны делать эту вещь X с переменной Y, но вы обязаны забыть три месяца вниз по дороге , когда клиент срочно нуждается в вас , чтобы реализовать некоторые функции, и вы удивляетесь , почему делают X - брейки У неясных способов.
Итак, что касается того, когда вы должны сделать вещи частными: я бы сказал, сделайте все частным по умолчанию, а затем выставьте только те части, которые абсолютно должны быть публичными. Чем больше вы можете сделать частным, тем лучше.
источник
Закрытые переменные помогают предотвратить зависимость людей от определенных частей вашего кода. Например, скажем, вы хотите реализовать некоторую структуру данных. Вы хотите, чтобы пользователи вашей структуры данных не заботились о том, как вы ее реализовали, а просто использовали реализацию через ваш четко определенный интерфейс. Причина в том, что если никто не зависит от вашей реализации, вы можете изменить ее, когда захотите. Например, вы можете изменить внутреннюю реализацию для повышения производительности. Любые другие разработчики, которые зависели от ваших реализаций, сломаются, в то время как интерфейс пользователей будет в порядке. Гибкость в изменении реализаций без влияния на пользователей класса - это огромное преимущество, которое дает использование закрытых переменных (и, в более широком смысле, инкапсуляция ).
Кроме того, это не совсем «кризис доверия». Если вы сделаете часть данных общедоступной, вы не сможете гарантировать, что никто не будет зависеть от нее. Часто очень удобно полагаться на какую-то переменную, специфичную для реализации, вместо того, чтобы проходить через открытый интерфейс, особенно в самый разгар срока. Кроме того, разработчики не всегда понимают, что они зависят от того, что может измениться.
Так что я надеюсь, что они ответят на ваши другие вопросы. Все детали вашей реализации должны быть частными, а открытая часть должна быть небольшим, кратким, четко определенным интерфейсом для использования вашего класса.
источник
private
меньше, чем «не предназначен в первую очередь для безопасности», он не обеспечивает никакой «безопасности», о которой можно говорить. Это просто сильный намек (доставляется через компилятор), чтобы не получить к нему доступ.Ключевое слово здесь - Инкапсуляция . В ООП вы хотите использовать закрытые переменные для обеспечения правильной инкапсуляции ваших объектов / классов.
Хотя другие программисты не хотят вас достать, они взаимодействуют с вашим кодом. Если вы не сделаете переменную закрытой, они вполне могут ссылаться на нее в своем собственном коде, не желая причинять какой-либо вред. Однако, если вам нужно вернуться в свой класс и что-то изменить, вы больше не знаете, кто и какую переменную использует и где. Цель инкапсуляции - сделать внешний интерфейс класса явным, чтобы вы знали, что только эти (как правило) методы могут быть использованы другими.
Следовательно, частные переменные гарантируют, что соответствующая переменная останется только в определяющем классе. Если вам нужно изменить это изменение, оно будет локальным для самого класса.
В традиционных языках, таких как C ++ или Java, вы обычно делаете все приватным и доступным только для соответствующих методов получения и установки. Не нужно много решать на самом деле.
Иногда, например в структуре C ++ класс нужен только как способ группировки нескольких вещей. Например, рассмотрим класс Vector , который имеет
x
и сy
только атрибут. В этих случаях вы можете разрешить прямой доступ к этим атрибутам, объявив их открытыми. В частности, вам не нужно заботиться о том, что какой-то код снаружи изменяет объект вашего класса, напрямую записывая новые значения вx
илиy
.В качестве дополнительного замечания, обратите внимание, что этот вопрос несколько отличается в других языках. Например, языки, которые прочно укоренились в функциональном программировании, подчеркивают неизменность данных, то есть значения данных не могут быть изменены вообще. В таких случаях вам не нужно заботиться о том, что другие программисты (или, скорее, их код) делают с вашими данными. Они просто не могут делать ничего, что могло бы повлиять на ваш код, потому что все данные неизменны.
Таким образом, в этих языках вы получаете нечто, называемое принципом унифицированного доступа , когда сознательно не различают методы, такие как методы получения и установки, но обеспечивают прямой доступ к переменной / функции. Таким образом, вы можете сказать, что частные объявления намного более популярны в объектно-ориентированных сценариях.
Это также показывает, как расширение ваших знаний для включения в другие области заставляет вас взглянуть на существующие концепции совершенно по-новому.
источник
Закрытые переменные гарантируют, что последующие ссылки вне области объекта или функции не будут непреднамеренно влиять на другие переменные. Для больших программных проектов это может помочь избежать многих интересных проблем (которые обычно не улавливаются компилятором).
Например, прототипные языки, такие как Javascript, могут позволять пользователям накладывать переменные так, как они считают нужным:
Если бы эта переменная была закрытой, ее нельзя было бы изменить извне:
Тем не менее, не все переменные должны быть закрытыми, а чрезмерное использование частных переменных может на самом деле сделать код более громоздким. Тривиальные поля, которые не оказывают серьезного влияния на объект, не нуждаются
get()
и вset()
методах.Закрытые переменные также облегчают расширение кода в будущем, не полагаясь на тяжелую документацию о том, что делают многие открытые переменные. Существует меньше угрозы случайного нарушения функциональности, если меньшее количество переменных может быть перезаписано.
Открытые переменные следует использовать, когда объект / функция будет обращаться к другим объектам / функциям, поскольку, как правило, закрытые переменные не могут этого делать. У вас может быть от нескольких до нескольких открытых переменных, и их использование не является плохим, если они используются правильно.
источник
Есть несколько хороших ответов со ссылкой на преимущества для будущих сопровождающих и пользователей класса. Тем не менее, есть также преимущества для оригинального дизайна. Он обеспечивает четкое разграничение между тем, когда ваша цель состоит в том, чтобы облегчить себе жизнь, и тем, когда ваша цель состоит в том, чтобы облегчить жизнь классному пользователю. Когда вы выбираете между общедоступным и частным доступом, он сигнализирует вашему мозгу о переключении на тот или иной режим, и в итоге вы получаете лучший API.
Дело не в том, что языки без конфиденциальности делают такой дизайн невозможным, просто менее вероятным. Вместо того, чтобы предлагать что-то наподобие
foo.getBar()
, люди склонны думать, что геттер не нужен, потому что его легче написатьfoo.bar
, но тогда это решение не пересматривается, когда вы в итоге получаете более длинные чудовища, подобныеobj.container[baz].foo.bar
. Программисты, которые никогда не работали на более строгом языке, могут даже не видеть, что с этим не так.Вот почему в языках без конфиденциальности люди часто принимают стандарты именования, которые имитируют это, например, добавление подчеркивания всем «частным» членам. Это полезный сигнал, даже если язык не применяет его.
источник
Все переменные должны быть закрытыми, если они абсолютно не должны быть открытыми (что почти никогда не требуется, вы должны использовать свойства / методы получения и установки).
Переменные в основном обеспечивают состояние объекта, а закрытые переменные не позволяют другим входить и изменять состояние объекта.
источник
Речь идет о том, чтобы прояснить, какие свойства и методы являются интерфейсом, а какие на самом деле являются основными функциями. Открытые методы / свойства относятся к другому коду, часто к коду другого разработчика, использующего ваши объекты. Я никогда не работал над командами из 100+ над одним и тем же проектом, но, по моему опыту, команды из 3-5 человек и до 20 других разработчиков используют вещи, которые я написал, другие проблемы кажутся глупыми.
Примечание: я в первую очередь разработчик JavaScript. Я обычно не беспокоюсь о том, что другие разработчики просматривают мой код, и я полностью осознаю, что они могут просто переопределить мои материалы в любое время. Я предполагаю, что они достаточно компетентны, чтобы знать, что они делают в первую очередь. Если нет, то вряд ли я буду работать в команде, которая не использует систему контроля версий.
Раньше я думал, что глупо помещать методы получения и установки в частные свойства, когда вы на самом деле не выполняли какую-либо проверку или другую специальную обработку свойства, которое нужно установить. Я до сих пор не думаю, что это всегда необходимо, но когда вы имеете дело с крупномасштабной, высокой сложностью, но, самое главное, с множеством других разработчиков, полагающихся на то, что ваши объекты ведут себя согласованно, может быть полезно сделать что-то в последовательный и единый способ. Когда люди видят вызванные методы
getThat
иsetThat
Они точно знают, каковы намерения, и могут написать свои объекты, чтобы взаимодействовать с вашими, ожидая, что они всегда будут получать помидоры, а не томахто. Если они этого не делают, они знают, что ваш объект дает им то, чего он, вероятно, не должен, что означает, что он позволяет другому объекту делать что-то с этими данными, чего он, вероятно, не должен. Вы можете контролировать это по мере необходимости.Другим большим преимуществом является то, что ваши объекты легче передавать или интерпретировать значения иначе, чем получатель или установщик, основанный на некотором различном контексте состояния. Поскольку они обращаются к вашим материалам с помощью метода, гораздо проще изменить работу вашего объекта в дальнейшем, не нарушая другой код, который от него зависит. Важным принципом является то, что только ваш объект на самом деле изменяет данные, за которые вы сделали его ответственным. Это особенно важно для обеспечения слабой связи вашего кода, что является огромным преимуществом с точки зрения переносимости и простоты модификации.
С первоклассными функциями (вы можете передавать функции в качестве аргументов), я не могу думать о множестве веских причин, кроме того, что в этом нет особой необходимости делать это в небольших проектах. Без этого, я полагаю, у вас могут быть члены, которые регулярно и интенсивно обрабатываются другими объектами до такой степени, что кажется постоянным вызовом get get и устанавливает объект, который на самом деле не касается самих этих данных, но в само по себе заставило бы меня задуматься, почему объект отвечает за эти данные. Вообще говоря, я бы хотел пересмотреть мою архитектуру на предмет недостатков, прежде чем решить, что необходимо публичное свойство данных. ИМО, нет ничего плохого в том, что язык позволяет вам делать то, что обычно является плохой идеей. Некоторые языки не согласны.
источник
int[]
,double[]
,Object[]
и т.д. Следует, однако, быть очень осторожным о том , как один выставляют экземпляры такого класса. Смыслvoid CopyLocationTo(Point p);
[принятия aPoint
от вызывающего абонента] более понятенPoint location()
, так как неясно, какое влияниеPoint pt = foo.location(); pt.x ++;
окажет местоположениеfoo
.Смотрите этот пост на мой связанный вопрос о SO .
Суть в том, что область видимости переменной позволяет вам показать потребителям вашего кода, с чем им следует и не следует связываться. Закрытая переменная может содержать данные, которые были «проверены» с использованием установщика свойств или метода обработки, чтобы гарантировать, что весь объект находится в согласованном состоянии. Непосредственное изменение значения закрытой переменной может привести к тому, что объект станет несовместимым. Делая это частным, кто-то должен по-настоящему усердно работать и иметь действительно высокие разрешения времени выполнения, чтобы изменить его.
Таким образом, правильное определение членов является ключевым в самодокументирующемся коде.
источник
Я собираюсь поговорить о значении инкапсуляции с другой точки зрения на реальном примере. Некоторое время назад (в начале 80-х) для Radio Shack Color Computer была написана игра под названием Dungeons of Daggorath. Несколько лет назад Ричард Ханерлах портировал его из списка сборщика бумаги на C / C ++ ( http://mspencer.net/daggorath/dodpcp.html ). Некоторое время спустя я получил код и начал перефакторинг, чтобы улучшить инкапсуляцию ( http://dbp-consulting.com/stuff/ ).
Код уже был разложен на различные объекты для управления расписанием, видео, пользовательским вводом, созданием подземелий, монстрами и т. Д., Но вы точно могли сказать, что он был перенесен с ассемблера. Моей самой большой задачей было увеличить инкапсуляцию. Под этим я подразумеваю получить разные части кода, чтобы вывести свои носы из бизнеса друг друга. Например, было много мест, в которых переменные видео были бы изменены напрямую, чтобы иметь некоторый эффект. Некоторые делали это с проверкой ошибок, некоторые нет, у некоторых были немного разные представления о том, что означает изменение этой вещи. Было много дублирования кода. Вместо этого у раздела видео должен был быть интерфейс для выполнения желаемых задач, и код для этого мог быть все в одном месте, одна идея, одно место для отладки. Такие вещи были безудержными.
Когда код начал дразнить, ошибки, которые даже не были диагностированы, исчезли. Код стал более качественным. Каждое задание стало обязанностью одного места в коде, и было только одно место, чтобы понять его правильно. (Я все еще нахожу больше каждый раз, когда делаю еще один проход по коду.)
Все, что видно в вашем коде, это ваш интерфейс. Независимо от того, хотите ли вы, чтобы это было или нет. Если вы максимально ограничите видимость, то у вас не будет соблазна поместить код в неправильное место позже. Это делает очевидным, какая часть кода отвечает за какие данные. Это делает дизайн лучше, чище, проще, элегантнее.
Если вы возьмете на себя ответственность за свои собственные данные и предоставите интерфейсы всем остальным, кому нужно что-то случиться, ваш код станет намного проще. Это просто выпадает. У вас есть небольшие простые процедуры, которые делают только одно. Меньше сложности == меньше ошибок.
источник
Это не имеет ничего общего с доверием или страхом перед атаками, оно исключительно связано с инкапсуляцией - не навязывая ненужную информацию пользователю класса.
Рассмотрим частные константы - они не должны содержать секретных значений (они должны храниться в другом месте), их нельзя изменить, их не нужно передавать в ваш класс (в противном случае они должны быть открытыми). Единственное возможное использование для них - в качестве констант в ДРУГИХ классах. Но если вы сделаете это, эти классы теперь зависят от вашего класса, чтобы выполнять работу, не связанную с вашим классом. Если вы измените константу, другие классы могут сломаться. Это плохо с обеих сторон - как писатель вашего класса, вы хотите, чтобы свобода изменилась как можно больше, и не хотите беспокоиться о вещах вне вашего контроля. Потребитель вашего класса хочет иметь возможность зависеть от раскрытых деталей вашего класса, не беспокоясь о том, что вы измените его и нарушите его код.
Потребитель вашего класса хочет знать все, что необходимо для ВЗАИМОДЕЙСТВИЯ с вашим классом, и не хочет знать что-либо о вашем классе, что не меняет его поведения: это бесполезные мелочи. Если вы использовали язык с отражением, как часто вы использовали его, чтобы узнать не о том, как какой-то класс делает что-то или где он неожиданно выдает исключение, а просто о названии приватных полей и методов? Я держу пари никогда. Потому что вы не пользуетесь этими знаниями.
источник
Концепция ООП имеет наследование имеет одну из своих функций (Java или C ++). Так что, если мы собираемся наследовать (то есть мы собираемся получить доступ к переменным унаследованного класса), есть возможность повлиять на эти переменные. Таким образом, мы должны решить, могут ли переменные быть изменены или нет.
Только для этой цели мы используем модификаторы доступа в ООП. Один из модификаторов является закрытым, то есть доступ к нему может получить только этот класс. Любой другой класс не может влиять на эти переменные.
Как мы знаем, защищенный означает, что к нему может получить доступ класс, который собирается наследовать.
Почему в ООП существует концепция модификатора, по этим причинам (любой класс может получить доступ к другим переменным класса, используя концепцию ООП). Если нет понятия модификатора, это означает, что было бы трудно при наследовании классов или использовании других концепций ООП.
источник
Как утверждают другие, закрытые переменные хороши для того, чтобы избежать неправильного использования, приводящего объект в противоречивое состояние, и трудно отслеживать ошибки и непредвиденные исключения.
Но с другой стороны, то, что в основном игнорируется другими, касается защищенных полей.
Расширенный подкласс будет иметь полный доступ к защищенным полям, делая объект таким хрупким, как если бы такие поля были общедоступными, но эта хрупкость ограничивается самим расширяющим классом (если только он не раскрывает такие поля еще больше).
Таким образом, публичные поля трудно считать хорошими, и на сегодняшний день единственная причина их использования - для классов, используемых в качестве параметра конфигурации (очень простой класс со многими полями и без логики, так что класс передается как один параметр в какой-то метод).
Но, с другой стороны, приватные поля снижают гибкость вашего кода для других пользователей.
Гибкость против неприятностей, плюсы и минусы:
Объекты, созданные вашим кодом в классе vanilla с защищенными полями, являются безопасными и являются вашей исключительной ответственностью.
С другой стороны, объекты, расширяющие ваш класс защищенными полями, созданными пользователями вашего кода, являются их ответственностью, а не вашей.
Таким образом, плохо документированные защищенные поля / методы, или если пользователи не совсем понимают, как такие поля и методы следует использовать, имеют хорошие шансы создать ненужные проблемы для себя и для вас.
С другой стороны, закрытие большинства вещей снизит гибкость пользователей и может даже увести их в сторону поиска альтернативных вариантов, поскольку они могут не захотеть создавать и поддерживать развилки только для того, чтобы все происходило по-своему.
Таким образом, хороший баланс между частным, защищенным и публичным - вот что действительно имеет значение.
Теперь решить между частным и защищенным является реальной проблемой.
Когда использовать защищенный?
Каждый раз, когда вы понимаете, что поле может быть очень гибким, оно должно быть закодировано как защищенное. Эта гибкость заключается в следующем: от обнуления (когда ноль всегда проверяется и распознается как допустимое состояние без исключений) до наличия ограничений перед использованием вашим классом ex. > = 0, <100 и т. Д. И автоматически исправляется для значений превышения / недостаточности потока, выдавая самое большее предупреждающее сообщение.
Таким образом, для такого защищенного поля вы можете создать геттер и использовать его только (вместо непосредственного использования переменной поля), в то время как другие пользователи могут не использовать его, если они хотят большей гибкости для своего конкретного кода, в моем примере это может быть : если они хотят, чтобы отрицательные значения работали нормально в их расширенном классе.
источник
imo @tdammers не прав и на самом деле вводит в заблуждение.
Частные переменные существуют, чтобы скрыть детали реализации. Ваш класс
A
может использоватьarray
для хранения результатов. Завтра вы можете использоватьtree
илиpriority queue
вместо. Все пользователи вашего класса нуждаются в способе ввода баллов и именaddScore()
и способе определить, кто входит в десятку лучшихgetTop(n)
.Им не нужен доступ к базовой структуре данных. Звучит здорово? Ну, есть несколько предостережений.
Вы не должны хранить много состояний в любом случае, большая часть этого не нужна. Сохранение состояния усложнит ваш код, даже если он «выделен» для определенного класса. Подумайте об этом, эта частная переменная, вероятно, изменилась, потому что другой объект вызвал метод этого класса. Вам все еще нужно выяснить, когда и где был вызван этот метод, и этот публичный метод может быть вызван в любом месте теоретически.
Лучшее, что вы можете сделать, это ограничить количество состояний в вашей программе и использовать чистые функции, когда это возможно.
источник
источник
Сначала вы должны понять концепцию объектно-ориентированного программирования. Имеет абстракцию, инкапсуляцию и т. Д.
Абстракция - Вы получаете представление о логике без необходимости подчеркивать детали реализации.
Инкапсуляция - Вы не можете видеть подчеркивание реализации объекта. Только вы можете видеть открытый интерфейс объекта.
Теперь в конкретной реализации с одной из объектно-ориентированных программ, таких как C #, Java, C ++, вы можете реализовать эти концепции.
private - реализация, которая должна быть скрыта от внешнего мира. Так что вы можете изменить это, и пользователь вашего класса не будет затронут.
public - это интерфейс, с помощью которого можно использовать ваш объект.
источник