Открытые переменные плохой практики против функций Getters и Setters?
Я наткнулся на это во время его потока, и это торчало для меня, как больной палец с тех пор. Я подумал, может быть, если бы я сохранил видео и вернусь к нему в будущем, когда буду более опытным, я пойму это, но он просто продолжал беспокоиться, чтобы просто оставить его. Здесь видео...
Он автоматически запускается в 1:13:00 для вас.
https://youtu.be/uHSLHvWFkto?t=4380
Как новый программист на C/С++, услышав это, он полностью исказил мой образ мышления. С ним, будучи профессионалом, и все, что я должен взять на себя, но мне нужна определенная ясность. Из сайтов, видеороликов, книг я читал, что использование общедоступных переменных - это плохая практика, но из того, что я получаю от этого видео, говорилось иначе. В видео он использует структуру, которая по умолчанию имеет модификатор доступа "public" и класс, который имеет доступ по умолчанию к "private". Что-то я здесь не понимаю.
Я не знаю, что делать. Если я сделаю свои переменные общедоступными, я не буду рисковать двусмысленностью? То, как он говорит, что он будет автоматически запускать кого-то для кодирования в формате, доходит до меня, ха-ха! Какой из них я должен использовать? Когда и почему?
Ответы
Ответ 1
Прежде всего, struct
полностью эквивалентен class
, но с доступом к члену по умолчанию public
, а не private
.
Теперь, в объектно-ориентированном программировании (ООП), не считается хорошей практикой иметь члены данных public
(переменные), поскольку это делает весь ваш код зависимым от внутренних элементов class
и, таким образом, нарушает изначальную принцип ООП, и это...
Священная и священная инкапсуляция
Инкапсуляция - это философия кодирования, в которой говорится, что класс должен обладать как данными, так и кодом, который управляет им в одном ограниченном объекте. То есть вы не обращаетесь к директиве данных, а используете методы из class
для управления такими данными. Это имеет несколько преимуществ дизайна, например, что вы будете знать, что никакой код, кроме одного внутри класса, не может включать ошибки в отношении манипулирования такой информацией.
Теперь get()
ers и set()
ers, иначе известные как accessors, полная ложь! С аксессуарами вы обманываете себя, думая, что вы уважаете инкапсуляцию, когда вы скорее ломаете! Он добавляет раздувание, ненужную многословие, ошибки и все, кроме инкапсуляции. Вместо class Person
с unsigned getAge()
и void setAge(unsigned)
используйте его с unsigned getAge()
и a void incrementAge()
или, тем не менее, хотите его называть.
Теперь, на ваш вопрос ядро ...
"Обычные старые" структуры
Инкапсуляция не всегда желательна. Хотя вы должны (обычно) не делать это в файлах заголовков (опять же, по крайней мере, для некоторого количества инкапсуляции), вы можете создать статический простой старый struct
, который является приватным для отдельной единицы перевода. Моя рекомендация - сделать их даже "старше", чем они есть, т.е....
- Все элементы данных
public
.
- Нет методов.
- Нет конструкторов (кроме неявных).
- Наследование всегда является общедоступным и разрешено только с других простых старых
struct
s.
- Повторяю, не помещать их в файлы заголовков!
Теперь другое использование для простого старого struct
(по иронии) метапрограмматического экспорта данных и типов constexpr
, иначе известного как метапрограммирование современных-hardcore-template-without-have-to-type- public
везде, например...
template<bool B, typename T>
struct EnableIf {};
template<typename T>
struct EnableIf<true, T> {
typedef T type;
};
template<bool B, typename T>
using SFINAE = typename EnableIf<B, T>::Type;
Ответ 2
По моему опыту люди используют геттеры/сеттеры чрезмерно без уважительной причины.
Можно подумать о двух основных типах классов: о группировании связанных данных и о других, обеспечивающих поведение.
Классы поведения должны быть инкапсулированы без публичных элементов данных.
В классах данных обычно должны присутствовать члены данных и поведение.
Серая область между этими двумя является изменчивыми классами данных с инвариантами или зависимостями между членами, например. если член a
равен 1, член b
должен находиться в диапазоне [1-10]
. Для таких случаев использование геттеров/сеттеров может быть оправдано. Для неизменяемых классов данных конструктор должен установить инвариант.
Ответ 3
Конечно, трудно контролировать внутреннюю согласованность вашего объекта, если вы публикуете публичные члены данных.
Что хорошо работает, так это использовать конструкторы для настройки состояния объекта, а затем использовать общедоступные функции для извлечения значений переменных-членов, но только если это необходимо. Если вам нужно мутировать объект после построения, тогда предоставьте очень конкретные методы для этой цели.
Если, однако, ваш объект является чем-то большим, чем то, что агрегирует ортогональные типы данных, тогда используйте открытый доступ для всех членов: a struct
хорошо работает для этого.
Обратите внимание, что единственное различие между struct
и a class
заключается в том, что в первом случае доступ по умолчанию public
, тогда как в последнем он private
.
Ответ 4
Если вы сохраняете свои данные членами private, вы можете легко контролировать доступ к их значению. Например, в вашем коде есть переменная age
.
public:
int age;
Теперь кто-то вне вашего класса может легко изменить значение возраста, может также назначить недопустимое значение, например age = -10
. Но ваша логика предполагает, что возраст не может быть отрицательным, поэтому наилучшей практикой является сохранение переменной private и предоставление некоторой функции, которая присваивает значение вашей переменной.
private:
int age;
public:
void setAge(int age)
{
if (age > 0)
this->age = age;
}