Публичные члены данных против геттеров, сеттеров - PullRequest
68 голосов
/ 04 июня 2010

В настоящее время я работаю в Qt и, следовательно, C ++. У меня есть классы, которые имеют частные члены данных и публичные функции-члены. У меня есть публичные методы получения и установки для членов данных, доступных в классе.

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

Или вместо можем ли мы сделать все переменные общедоступными, чтобы вообще не нуждаться в методах получения и установки? Это хорошая практика? Я знаю, что частные члены гарантируют абстракцию данных, но наличие методов получения и установки фактически позволяет довольно легко получить доступ к этим переменным. Любые указания по этому поводу приветствуются.

Ответы [ 15 ]

71 голосов
/ 04 июня 2010

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

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

РЕДАКТИРОВАТЬ: После комментария я сделал на другой ответ.

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

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

Вместо этого изучите, что делает объект и что это значит для того, чтобы быть этим объектом. Затем создайте методы, которые обеспечивают естественный интерфейс с этим объектом. Естественный интерфейс предполагает выставление некоторых внутренних свойств с использованием геттеров и сеттеров. Но важная часть заключается в том, что вы думали об этом заранее и создали геттеры / сеттеры по оправданной причине.

34 голосов
/ 04 июня 2010

Нет, это даже не одно и то же.

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


1. Публичный член данных:

  • обеспечивает как чтение, так и запись (если не постоянный) доступ к элементу данных
  • раскрывает тот факт, что объект данных физически существует и физически является членом этого класса (позволяет создавать указатели типа указатель на член на этот элемент данных)
  • обеспечивает lvalue доступ к элементу данных (позволяет создавать обычные указатели на элемент)


2. Метод, который возвращает ссылку на часть данных (возможно, на частный элемент данных):

  • обеспечивает как чтение, так и запись (если не постоянный) доступ к данным
  • раскрывает тот факт, что объект данных физически существует, но не раскрывает , что он физически является членом этого класса (не позволяет создавать указатели типа указатель на член на данные)
  • предоставляет доступ к данным lvalue (позволяет создавать обычные указатели на них)


3. Методы получения и / или установки (возможно, доступ к частному элементу данных):

  • предоставляет для чтения и / или записи доступ к свойству
  • не раскрывает тот факт, что объект данных физически существует, не говоря уже о физическом представлении в этом классе (не позволяет создавать указатели типа указатель на член для этих данных или любой другой вид указатели по этому вопросу)
  • не предоставляет lvalue-доступ к данным (не позволяет создавать обычные указатели на них)

Подход метода получения / установки даже не раскрывает тот факт, что свойство реализуется физическим объектом. То есть за парой получатель / установщик может отсутствовать физический элемент данных.

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

Конечно, есть варианты каждого подхода. Например, метод получения может вернуть константную ссылку на данные, что поместит их где-то между (2) и (3).

23 голосов
/ 04 июня 2010

Если у вас есть геттеры и сеттеры для каждого из ваших элементов данных, нет смысла делать данные приватными. Вот почему иметь геттеры и сеттеры для каждого элемента данных - плохая идея. Рассмотрим класс std :: string - он (вероятно) имеет ОДИН геттер, функцию size () и вообще не имеет сеттеров.

Или рассмотрим объект BankAccount - должен ли у нас быть установщик SetBalance() для изменения текущего баланса? Нет, большинство банков не поблагодарит вас за такую ​​вещь. Вместо этого мы хотим что-то вроде ApplyTransaction( Transaction & tx ).

10 голосов
/ 04 июня 2010

Сделайте данные общедоступными. В (довольно маловероятном) случае, когда вам когда-нибудь понадобится логика в «getter» или «setter», вы можете изменить тип данных на прокси-класс, который перегружает operator= и / или operator T (где T = любой тип вы используете сейчас) для реализации необходимой логики.

Редактировать: идея о том, что управление доступом к данным представляет собой инкапсуляцию, в основном ложна. Инкапсуляция - это скрытие деталей реализации (в общем!) , а не , контролирующих доступ к данным.

Инкапсуляция дополняет абстракцию: абстракция имеет дело с внешне видимым поведением объекта, в то время как инкапсуляция имеет дело со скрытием деталей того, как это поведение реализовано.

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

Конечно, это нужно держать в контексте: для некоторых классов «данные» вообще не являются хорошей абстракцией. Вообще говоря, если вы можете обеспечить более высокий уровень операций вместо данных, это предпочтительнее. Тем не менее, существуют классы, для которых наиболее используемая абстракция - это чтение и запись данных - и в этом случае (абстрагированные) данные должны быть видны как любые другие данные. Тот факт, что получение или установка значения может включать не просто копирование битов, является деталью реализации, которая должна быть скрыта от пользователя.

10 голосов
/ 04 июня 2010

Методы получения и установки позволяют применять логику к вводу / выводу от закрытых членов, следовательно, управляя доступом к данным (инкапсуляция для тех, кто знает их термины ОО).

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

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

Имейте в виду, однако, что это не означает, что каждой частной переменной требуется свой собственный метод получения / установки. В своем примере банковского дела Нил привел хороший пример того, что иногда геттеры / сеттеры просто не имеют смысла.

5 голосов
/ 04 июня 2010

Помимо проблем инкапсуляции (которые являются достаточной причиной), очень легко установить точку останова, когда переменная установлена ​​/ доступна, когда у вас есть методы getters / setters.

5 голосов
/ 04 июня 2010

Если вы абсолютно уверены, что ваша логика проста, и вам никогда не нужно делать что-то еще при чтении / записи переменной, лучше держать данные открытыми. В случае C ++ я предпочитаю использовать struct вместо class, чтобы подчеркнуть тот факт, что данные являются публичными.

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

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

4 голосов
/ 25 июня 2015

Причины использования открытых полей, а не методов получения и установки, включают:

  1. Недопустимые значения отсутствуют.
  2. Ожидается, что клиент отредактирует его.
  3. Чтобы иметь возможность писать такие вещи, как object.XY = Z.
  4. Чтобы дать твердое обещание, что значение - это просто значение и с ним не связано никаких побочных эффектов (и не будет влибо в будущем).

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

(Из Десять вопросов по программированию на основе значений .)

3 голосов
/ 04 июня 2010

С практической точки зрения, я бы посоветовал вам начать с того, что все ваши члены данных будут частными, И сделайте их методы получения и установки частными. По мере того, как вы узнаете, что на самом деле нужно остальному миру (т. Е. Вашему «(l) сообществу пользователей»), вы можете выставить соответствующие методы получения и / или установки или написать подходящие общедоступные средства доступа.

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

2 голосов
/ 10 октября 2017

Я считаю, что использование геттеров и сеттеров просто для получения и установки значения бесполезно.Нет разницы между публичным членом и частным с такими методами.Используйте геттеры и сеттеры только тогда, когда вам нужно как-то управлять значениями или когда вы думаете, что это может быть полезно в будущем (добавление некоторой логики не заставит вас редактировать остальную часть кода).* В качестве справки прочитайте рекомендации C ++ (C.131)

...