Прагматичный взгляд на частное против публичного - PullRequest
6 голосов
/ 28 марта 2010

Я всегда задавался вопросом о свойствах public, protected и private. Моя память легко может вспомнить времена, когда у меня было для взлома чьего-то кода, а взломанные переменные класса, объявленные как private, всегда расстраивали.

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

Так в чем же смысл этих ограничений?

Ответы [ 9 ]

7 голосов
/ 28 марта 2010

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

Снаружи (публично) - ваш контракт с остальным миром. Вы должны постараться сделать его простым, последовательным, очевидным, надежным и, что очень важно, стабильным.

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

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

Небольшой пример, предположим, что у нас есть:

class MyDate
{ 
    public int y, m, d;
    public void AdvanceDays(int n) { ... } // complicated month/year overflow
    // other utility methods
};

Вы не можете запретить пользователю класса игнорировать AdvanceDays () и просто выполнить:

date.d = date.d + 1; // next day

Но если вы сделаете y, m, d закрытым и протестируете все методы MyDate, вы сможете гарантировать, что в системе будут только действительные даты.

4 голосов
/ 28 марта 2010

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

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

2 голосов
/ 28 марта 2010

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

0 голосов
/ 28 марта 2010

Прежде всего, «свойства» могут относиться к разным вещам на разных языках. Например, в Java вы имеете в виду переменные экземпляра, тогда как в C # есть различие между ними.

Я собираюсь предположить, что вы имеете в виду переменные экземпляра, поскольку вы упоминаете геттеры / сеттеры.

Причина, о которой упоминали другие, - Инкапсуляция. А что нас покупает Encapsulation?

Гибкость

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

Например, мы можем принять решение о внесении изменений, например:

int getFoo()
{
  return foo;
}

int getFoo()
{
  return bar + baz;
}

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

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

void setFoo(int val)
{
  if(foo < 0)
    throw MyException(); // or silently ignore

  foo = val;
}

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

Если бы наша собственность была публичной, то мы бы не смогли ничего сделать!

0 голосов
/ 28 марта 2010

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

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

0 голосов
/ 28 марта 2010

Некоторые языки, например Smalltalk, вообще не имеют модификаторов видимости.

В случае Smalltalk все переменные экземпляра всегда являются закрытыми, а все методы всегда являются открытыми. Разработчик указывает, что метод «приватный» - что-то, что может измениться, или вспомогательный метод, который сам по себе не имеет особого смысла - помещая метод в «приватный» протокол.

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

(Примечание: «свойства» в Smalltalk - это просто методы получения и установки.)

0 голосов
/ 28 марта 2010

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

0 голосов
/ 28 марта 2010

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

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

0 голосов
/ 28 марта 2010

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

Другой пример - вспомогательные функции. Вы можете разбить часть своей большой логики на более мелкие функции, но это не значит, что вы хотите, чтобы все видели и использовали эти вспомогательные функции, вы просто хотите, чтобы они имели доступ к вашим основным функциям API.

Другими словами, вы хотите скрыть некоторые внутренние компоненты вашего кода от интерфейса .

Посмотрите некоторые видео об API, например, этот разговор Google .

...