В Java, C # и многих других строго типизированных, статически проверенных языках мы привыкли писать код следующим образом:
public void m1() { ... }
protected void m2() { ... }
private void m2() { ... }
void m2() { ... }
Некоторые динамически проверяемые языки не предоставляют ключевые слова для выражения уровня «приватности» данного члена класса и вместо этого полагаются на соглашения о кодировании. Например, в Python префикс частных членов ставится с подчеркиванием:
_m(self): pass
Можно утверждать, что предоставление таких ключевых слов на динамически проверяемых языках добавит мало пользы, поскольку проверяется только во время выполнения.
Однако я не могу найти вескую причину для предоставления этих ключевых слов на статически проверенных языках. Я считаю необходимым заполнить мой код довольно многословными ключевыми словами, такими protected
как раздражающие и отвлекающие. До сих пор я не был в ситуации, когда ошибка компилятора, вызванная этими ключевыми словами, спасла бы меня от ошибки. Наоборот, я был в ситуациях, когда ошибочное размещение protected
не позволяло мне использовать библиотеку.
Имея это в виду, мой вопрос:
Является ли информация сокрытием больше, чем соглашение между программистами, используемое для определения того, что является частью официального интерфейса класса?
Может ли оно быть использовано для защиты секретного состояния класса от нападения? Может ли рефлексия перекрыть этот механизм? Что могло бы стоить компилятору применять скрытие информации?
источник
Ответы:
Я учусь на сертификацию Java, и в целом это касается того, как управлять модификаторами доступа. И они имеют смысл, и их следует использовать правильно.
Я работал с python, и во время моего обучения я слышал, что в python существует соглашение, потому что люди, которые работают с ним, должны знать, что это значит и как его применять. Тем не менее, в
_m(self): pass
подчеркивании предупредил бы меня не возиться с этим полем. Но все ли будут следовать этой конвенции? Я работаю с Javascript и должен сказать, что нет . Иногда мне нужно проверить проблему, и причина была в том, что человек делал то, чего не должен был делать ...Прочитайте это обсуждение относительно подчеркивания Python.
Из того, что я сказал, да.
Да, его можно использовать для защиты секретного состояния класса, и его следует использовать не только для этого, но и для того, чтобы пользователи не связывались с состоянием объектов, чтобы изменить свое поведение на то, что, по их мнению, должно быть таким, каким должен быть объект работал Вы, как разработчик, должны планировать и думать об этом и разрабатывать свой класс так, чтобы это имело смысл, чтобы его поведение не было изменено. И компилятор, как хороший друг, поможет вам сохранить код так, как вы его разработали, применяя политики доступа.
Отредактировано Да, отражение может, проверить комментарии
Последний вопрос интересный, и я готов прочитать ответы на него.
источник
«Частный» спецификатор доступа не связан с ошибкой компилятора, он генерируется при первом его отображении. На самом деле речь идет о запрете доступа к чему-то, что все еще может измениться при изменении реализации класса, содержащего закрытый член.
Другими словами, если вы не разрешите использовать его, когда он все еще работает, вы случайно не сможете использовать его, когда он больше не работает.
Как отметил Делнан ниже, префиксная конвенция препятствует случайному использованию членов, которые могут изменяться, если эта конвенция соблюдается и понимается правильно. Для злонамеренного (или неосведомленного) пользователя это не делает ничего, чтобы помешать ему получить доступ к этому члену со всеми возможными последствиями. В языках со встроенной поддержкой спецификаторов доступа это не происходит по незнанию (ошибка компилятора) и выделяется, как больной большой палец при злонамеренности (странные конструкции для доступа к приватному члену).
Спецификатор «защищенного» доступа - это отдельная история - не думайте, что это просто «не совсем публично» или «очень похоже на приват». «Защищенный» означает, что вы, вероятно, захотите использовать эту функциональность, когда вы наследуете класс, содержащий защищенный член. Защищенные члены являются частью «интерфейса расширения», который вы будете использовать для добавления функциональности поверх существующих классов без изменения самих этих существующих классов.
Итак, краткое резюме:
источник
Если вы пишете код, который будет использоваться кем-то другим, то сокрытие информации может обеспечить гораздо более простой интерфейс. «Кто-то другой» может быть другим разработчиком в вашей команде, разработчиками, использующими API, который вы написали на коммерческой основе, или даже вашим будущим пользователем, который «просто не может вспомнить, как работает эта чертова штука». Работать с классом, в котором доступно только 4 метода, намного проще, чем с классом 40.
источник
_member
илиprivate member
в своем классе ничтожно мало, если другой программист понимает, что он должен смотреть только наpublic
членов без префиксов или без них.Я хотел бы предложить, что для справки, вы начинаете с чтения об инвариантах класса .
Короче говоря, инвариант - это предположение о состоянии класса, которое должно оставаться верным в течение всей жизни класса.
Давайте использовать очень простой пример C #:
Что тут происходит?
addresses
инициализируется по классу строительства.readonly
, так что ничто изнутри не может коснуться его после построения (это не всегда правильно / необходимо, но здесь полезно).Send
метод может сделать предположение,addresses
которое никогда не будетnull
. Он не должен выполнять эту проверку, потому что нет возможности изменить значение.Если бы другим классам было разрешено писать в
addresses
поле (т. Е. Если бы это было такpublic
), то это предположение больше не было бы верным. Каждый другой метод в классе, который зависит от этого поля, должен будет начать выполнять явные проверки на ноль или рискнуть сбой программы.Так что да, это намного больше, чем «конвенция»; Все модификаторы доступа для членов класса вместе образуют набор предположений о том, когда и как это состояние может быть изменено. Эти предположения впоследствии включаются в зависимые и взаимозависимые члены и классы, чтобы программистам не приходилось рассуждать обо всем состоянии программы одновременно. Способность делать предположения является критически важным элементом управления сложностью программного обеспечения.
На эти другие вопросы:
Да и нет. Защитный код, как и большая часть кода, будет опираться на определенные инварианты. Модификаторы доступа, безусловно, полезны в качестве указателей доверенным абонентам, которые не должны связываться с ним. Вредоносный код не будет заботиться, но вредоносный код также не должен проходить через компилятор.
Конечно может. Но рефлексия требует, чтобы вызывающий код имел такой уровень привилегий / доверия. Если вы используете вредоносный код с полным доверием и / или правами администратора, значит, вы уже проиграли эту битву.
Компилятор уже делает жизнь это. Так же как и среда выполнения в .NET, Java и других подобных средах - код операции, используемый для вызова метода, не будет успешным, если метод является закрытым. Единственные способы обойти это ограничение требуют наличия доверенного / повышенного кода, а повышенный код всегда может просто записываться непосредственно в память программы. Он применяется настолько, насколько это возможно, без специальной операционной системы.
источник
_
указанием контракта, но без его принудительного исполнения. Это может затруднить невежество в отношении контракта, но не усложнит его нарушение , особенно если сравнивать с утомительными и часто ограничительными методами, такими как Reflection. Конвенция гласит: «Вы не должны нарушать это». Модификатор доступа говорит: «Вы не можете сломать это». Я не пытаюсь сказать, что один подход лучше другого - они оба хороши в зависимости от вашей философии, но, тем не менее, это очень разные подходы.private
/protected
доступа похож на презерватив. Вам не нужно использовать один, но если вы не собираетесь, то вам лучше быть чертовски уверены в вашем времени и ваших партнерах. Это не предотвратит злонамеренный поступок и может даже не сработать каждый раз, но определенно снизит риски, вызванные небрежностью, и сделает это гораздо эффективнее, чем выражение «пожалуйста, будьте осторожны». О, и если у вас есть, но не используйте его, то не ожидайте от меня сочувствия.Сокрытие информации - это больше, чем просто соглашение; попытка обойти это может фактически нарушить функциональность класса во многих случаях. Например, довольно распространенной практикой является сохранение значения в
private
переменной, выставление его с использованием свойстваprotected
илиpublic
того же времени, а в получателе, проверьте наличие нуля и выполните любую необходимую инициализацию (т. Е. Отложенную загрузку). Или сохраните что-нибудь вprivate
переменной, предоставьте ее с помощью свойства и в установщике проверьте, изменилось ли значение и запустили лиPropertyChanging
/PropertyChanged
события. Но не увидев внутреннюю реализацию, вы никогда не узнаете всего, что происходит за кулисами.источник
Сокрытие информации развилось из нисходящей философии дизайна. Python был назван восходящим языком .
Сокрытие информации обеспечивается на уровне классов в Java, C ++ и C #, поэтому на данном уровне это не совсем соглашение. Сделать класс «черным ящиком» очень просто, с открытыми интерфейсами и скрытыми (частными) деталями.
Как вы указали, в Python программисты должны следовать соглашению не использовать то, что предназначено для сокрытия, так как все видно.
Даже с Java, C ++ или C # в какой-то момент сокрытие информации становится соглашением. На высших уровнях абстракции нет элементов управления доступом, используемых в более сложных программных архитектурах. Например, в Java вы можете найти использование «.internal». названия пакетов. Это чисто соглашение об именах, потому что нелегко реализовать такую информацию, скрывающуюся, только за счет доступности пакетов.
Одним из языков, который стремится определить формально доступ, является Eiffel. В этой статье рассказывается о некоторых других слабостях, скрывающих информацию, таких как Java.
Справочная информация : скрытие информации было предложено в 1971 году Дэвидом Парнасом . В этой статье он указывает, что использование информации о других модулях может «катастрофически увеличить связность структуры системы». Согласно этой идее, отсутствие сокрытия информации может привести к тому, что системы будут сложно поддерживать. Он продолжает с:
источник
Ruby и PHP имеют его и применяют во время выполнения.
Точка сокрытия информации - фактически показ информации. «Скрывая» внутренние детали, цель становится очевидной с внешней точки зрения. Есть языки, которые охватывают это. В Java доступ по умолчанию для внутреннего пакета, в haXe для защищенного. Вы явно объявляете их публичными, чтобы разоблачить их.
Смысл этого в том, чтобы сделать ваши классы простыми в использовании, предоставляя только очень понятный интерфейс. Вы хотите, чтобы остальное было защищено, чтобы ни один умный парень не пришел и не испортил ваше внутреннее состояние, чтобы заставить ваш класс делать то, что он хочет.
Кроме того, когда модификаторы доступа применяются во время выполнения, вы можете использовать их для обеспечения определенного уровня безопасности, но я не думаю, что это особенно хорошее решение.
источник
pydoc
удаляет_prefixedMembers
из интерфейса. Беспорядочные интерфейсы не имеют ничего общего с выбором ключевого слова, проверяемого компилятором.Модификаторы Acces определенно могут делать то, чего не может конвенция
Например, в Java вы не можете получить доступ к закрытому члену / полю, если не используете отражение.
Поэтому, если я напишу интерфейс для плагина и правильно откажу в праве на изменение приватных полей с помощью отражения (и на настройку менеджера безопасности :)), я могу отправить некоторый объект функциям, реализованным кем-либо, и знать, что он не может получить доступ к его приватным полям.
Конечно, могут быть некоторые ошибки безопасности, которые позволят ему преодолеть это, но это не философски важно (но на практике это определенно так).
Если пользователь запускает мой интерфейс в своей среде, он имеет контроль и, следовательно, может обойти модификаторы доступа.
источник
Это не столько спасает авторов приложений от ошибок, сколько позволяет авторам библиотеки решать, какие части своей реализации они поддерживают.
Если у меня есть библиотека
Я могу хотеть иметь возможность заменить
foo
реализацию и, возможно,fooHelper
радикально измениться . Если группа людей решила использоватьfooHelper
несмотря на все предупреждения в моей документации, то я не смогу сделать это.private
позволяет авторам библиотеки разбивать библиотеки на методы управляемых размеров (иprivate
вспомогательные классы), не опасаясь, что они будут вынуждены хранить эти внутренние детали в течение многих лет.Напомним, что в Java
private
не применяется компилятор, а проверяется байт-код Java .В Java не только отражение может переопределить этот механизм. Есть два вида
private
в Java. Этоprivate
предотвращает доступ одного внешнего класса кprivate
членам другого внешнего класса, который проверяется верификатором байт-кода, но такжеprivate
и к тем, которые используются внутренним классом через метод синтетического метода доступа с закрытым пакетом, как вПоскольку класс
B
(действительно именованныйC$B
) используетi
, компилятор создает синтетический методB
доступа, который позволяет получить доступC.i
таким образом, чтобы он прошел проверку байт-кода. К сожалению, посколькуClassLoader
позволяет вам создать класс из abyte[]
, довольно просто получить доступ к частным лицам, которыеC
открылись для внутренних классов, создав новый класс вC
пакете внутри , что возможно, еслиC
jar не был запечатан.Правильное
private
применение требует координации между загрузчиками классов, верификатором байт-кода и политикой безопасности, которая может препятствовать отражающему доступу рядовых лиц.Да. «Безопасная декомпозиция» возможна, когда программисты могут сотрудничать, сохраняя при этом свойства безопасности своих модулей - мне не нужно доверять автору другого модуля кода, чтобы не нарушать свойства безопасности моего модуля.
Языки возможностей объектов, такие как Joe-E, используют скрытие информации и другие средства, чтобы сделать возможной безопасную декомпозицию:
Ссылка на эту страницу дает пример того, как
private
правоприменение делает возможной безопасную декомпозицию.источник
Сокрытие информации - одна из главных задач хорошего проектирования программного обеспечения. Проверьте любые из работ Дэйва Парнаса с конца 70-х. По сути, если вы не можете гарантировать, что внутреннее состояние вашего модуля непротиворечиво, вы не можете ничего гарантировать о его поведении. И единственный способ гарантировать его внутреннее состояние - это сохранить его в тайне и изменять его только теми средствами, которые вы предоставляете.
источник
Делая защиту частью языка, вы получаете нечто: разумную уверенность.
Если я сделаю переменную закрытой, у меня будет разумная уверенность, что к ней будут обращаться только код из этого класса или явно объявленные друзья этого класса. Объем кода, который может разумно коснуться этого значения, ограничен и явно определен.
Есть ли способы обойти синтаксическую защиту? Абсолютно; у большинства языков есть они. В C ++ вы всегда можете привести класс к другому типу и ткнуть его битами. В Java и C # вы можете отразить это в себе. И так далее.
Однако делать это сложно . Очевидно, что вы делаете то, что не должны делать. Вы не можете сделать это случайно (вне диких записей в C ++). Вы должны охотно подумать: «Я собираюсь коснуться того, что мне не сказал мой компилятор». Вы должны охотно делать что-то неразумное .
Без синтаксической защиты программист может просто случайно все испортить. Вы должны научить пользователей соглашению, и они должны следовать этому соглашению каждый раз . Если они этого не делают, мир становится очень небезопасным.
Без синтаксической защиты ответственность лежит не на тех людях, которые используют класс. Они должны следовать соглашению, иначе произойдет неуказанное зло. Поцарапайте это: может произойти неуказанная плохость .
Нет ничего хуже, чем API, где, если вы сделаете не то, все может работать в любом случае. Это дает ложное заверение пользователю, что он поступил правильно, что все в порядке и т. Д.
А что нового пользователя на этом языке? Мало того, что они должны изучать и следовать действующему синтаксису (обеспечивается компилятором), они теперь должны следовать этому соглашению (в лучшем случае обеспечивается их коллегами). А если нет, то ничего плохого не может произойти. Что происходит, если программист не понимает, почему существует соглашение? Что происходит, когда он говорит: «Винт это» и просто тыкает в ваших рядовых? И что он думает, если все продолжает работать?
Он считает, что соглашение глупо. И он не будет следовать этому больше никогда. И он скажет всем своим друзьям, чтобы не беспокоить тоже.
источник