Комментарии 27
А есть смысл обсуждать переведенную статью? Ответ от автора мы не получим.
Не нужно вводить инкапсуляцию ради инкапсуляции. А если есть логика, то отдельный метод это правильное решение.
Ну вот тогда берется и делается рефакторинг, когда это реально понадобится.
Если исходить из соображений краткости кода, лёгкости внесения в него изменений, и потенциала для совершения ошибок — то конечно, геттеры и сеттеры зло для структуроподобных объектов. Пользы от них никакой, а вред очевиден.
Setter - это на самом деле не про отдельное поле. Это функция, которая переводит объект из одного инвариантного состояния в другое. В таком значении в тупом задании сеттеров по полям (по одному на каждое) и правда нет смысла. Лучше всего концепция проиллюстрирована в GraphQL mutations, кстати.
А еще ныне популярна концепция immutability, в которой сеттеров нет вообще. И действительно, в реальном коде, если стараться все делать иммутабельно, сеттеров получается самый минимум.
Есть еще такой паттерн как builder, вот он как раз позволяет конструировать такие иммутабельные объекты, но там не совсем сеттеры.
не использование сеттеров является грубой ошибкой, по мнению идеологов ООП
А по вашему мнению — является неиспользование геттеров/сеттеров грубой ошибкой или нет?
повесить на него любой функционал, например, вы можете поставить логирование записи в поле, хеширование, сжатие, или любые проверки граничных условий, да всё что угодно
Да, можно повесить. Когда-нибудь потом, если понадобится. В следующей жизни. А код надо писать сейчас, и на текущий момент нет необходимости делать слой абстракции между полями и операциями их чтения/записи.
Вы несёте существенные затраты: время на написание геттеров и сеттеров; рост размера кода в разы; лишние места, где можно допустить ошибку; падение быстродействия; затруднение понимания программы. И всё это ради решения каких-то проблем, которых сейчас нет, и неизвестно, появятся ли они в будущем. Но это же Overengineering в чистом виде.
Как по мне — то проще в будущем при необходимости добавить геттер и провести поиск/замену доступов к полю структуры на вызов геттера.
На практике, конечно, всё несколько по другому
А как у вас на практике происходит?
вам дадут по рукам, если нет ссеторов и хорошо ещё, если не добавят пинка
И что, везде нынче так? Идеология геттеров/сеттеров является господствующей в умах начальства?
не использование сеттеров является грубой ошибкой, по мнению идеологов ООП
А по вашему мнению — является неиспользование геттеров/сеттеров грубой ошибкой или нет?
По моему мнению, сама концепция ООП не настолько ультимативна, что бы отказ от чего-либо делал её бессмыссленной в целом. Да и строго говоря, не имеют никакого отношения get и set к ООП, в классическом пониманиии, взаимодействие между объектами происходит через события. Поэтому я рассматриваю их не как закон, а как чей-то опыт, который просто принимаю к сведению, но в чужом монастыре буду строго придерживаться чужого устава.
А как у вас на практике происходит?
В моей практике, рефакторинг, это не зло, которого надо избегать, а блого, которым я занимаюсь с удовольствием и которое, зачастую, изменяет сам подход, в решении проблемы выводя его на новый уровень. А ООП, не смотря на свои постулаты, именно рефакторингу и противодействует, постулируя: делайте сразу правильно и рефакторинга не будет, вам нужен только фронт волны, остальной код это чёрный ящик и.п. Это, конечно, в корне не верно и ООП с этим не справилось.
При этом, чужой опыт я не игнорирую, а использую как инструмент и как для любого инструмента ему нужна подходящая область применения, а не гвозди микроскопом забивать.
И что, везде нынче так? Идеология геттеров/сеттеров является господствующей в умах начальства?
В умах начальства, очень простое правило, проверенное временем: если у вас есть концепт, вы должны ему следовать полностью, от и до, иначе малюсенькая деталюшка обрушит весь карточный домик. И то что, часть деталей концепции являются откровенным шлком или вещами, которые профессионалы могут оспаривать никого волновать не должно. Так было всегда и так будет, безотносительно ОПП и спорить с этим глупо.
Соглашусь с вами в том, что рефакторинга избежать невозможно. Приёмы вроде сеттеров и геттеров с этой задачей не справляются.
При разработке любой системы невозможно заранее предусмотреть все проблемы, которые возникнут в будущем. В моей практике попытки заранее предусмотреть и решить все возможные будущие проблемы приводили к существенной трате времени и сил на решение надуманных проблем, которые в будущем не возникали. А реально возникающие проблемы предугадать удавалось не все, так что приходилось тратить усилия на решение ещё и их. В итоге проекты, создаваемые с таким подходом, обычно терпели провал: у всех заканчивалось терпение ждать результата.
С тем, что любому инструменту есть своя область применения, согласен на 100%.
Кстати, насчёт функционала вида: «поставить логирование записи в поле, хеширование, сжатие, или любые проверки граничных условий, да всё что угодно» — я тут подумал и пришёл к такому решению.
Допустим, у нас есть структура с полями прямого доступа:
struct A
{
int a;
double b;
std::string c;
};
Требуется повесить функционал на запись в поле «b». Заводим новый класс:
class SmartDouble
{
public:
double operator=(const double& nv) {printf("Write to double: %f\n", nv); v=nv; return nv;}
operator double() { return v; }
private:
double v;
}
И заменяем тип поля структуры «b» с double на SmartDouble. Рефакторинг остального кода с прямым доступом не нужен. Что скажете? Заработает?
К сожалению, на некоторые мои вопросы, подразумевающие простые ответы «да» или «нет», вы ответили как-то расплывчато. Если вас не затруднит, не могли бы вы уточнить ваши ответы на следующие вопросы:
1. «по вашему мнению — является неиспользование геттеров/сеттеров грубой ошибкой или нет?»
2. «Идеология геттеров/сеттеров является господствующей в умах начальства?»
2. Да и это — нормально.
P. S. Это как с ЕГЭ, все признают что это зазубривание ничего не даёт, что само по себе это ущербно и т. д., но ответ так же будет, что требование хороших оценок по ЕГЭ иммеет место быть и это — нормально и не нужно с этим бороться. Если такая аналогия более понятна.
и это — нормально и не нужно с этим бороться
Бороться не нужно? Соглашусь, но только по причине безнадёжности такой борьбы и неравенства сил. Внутренне же соглашаться с широким внедрением нерациональных подходов считаю близорукостью мышления.
Указанные явления суть не что иное, как обыкновенный догматизм. Буду знать, что нынче в моде сеттеры и геттеры. Ну а завтра им на смену придёт какая-нибудь другая «панацея». Технологии меняются, люди — нет.
В случае с set данные часто проще проверить до вызова set.
Геттеры\Сеттеры концептуально реализуют поведение. Поведение декларируется интерфейсоv взаимодействия. Структура по сути своей является реализацией некоторого композитного хранения данных.
Вопрос о применимости интерфейса к композитному хранилищу выходит за рамки одной единственной структуры. Сами по себе геттеры и сеттеры этой структуре не нужны. Однако, если мы задаемся целью интеграции и абстрагирования поведения, то возможны варианты.
Ниже предельно искусственный пример, расширяющий предметную область.
// Интерфейс для некоторой задачи, требующей имя и фамилию
class named_entity_t{//пропущены абстрактные геттеры};
// Интерфейс для установки фамилии как индекса поиска
class last_name_index_entry_t{//пропущены абстрактные сеттеры};
// Интерфейс для установки имени для целей базы данных
class first_name_db_entry_t{//пропущены абстрактные сеттеры};
// Интерфейс для получения и установки возраста сущности, человека, мебели, дерева в парке
class aged_entity_t{//пропущены абстрактные геттеры И сеттеры};
// Геттеры и сеттеры для имени и фамилии
struct Person : public named_entity_t, public last_name_index_entry_t, public first_name_db_entry_t
{ // пропущены тривиальные реализации, или даже наследование от миксин структур}
// Где-то в совершенно другом проекте с другими названиями интерфейсов
// Визуально те же геттеры и сеттеры, но с другой реализацией для другой задачи.
struct Tree : public named_entry_t, public aged_entity_t{ //пропущено
}
Таким образом вопрос о геттерах и сеттерах в вакууме весьма холиварный. При рассмотрении задачи в предметной области и выбранного подхода геттеры могут реализовать динамическое вычисление полей, или абстрагировать нижележащие слои. Сеттеры могут помогать сохранять инвариант экземпляра структуры или проверять валидность данных. Геттеры и сеттеры не обязаны работать с единственным полем и могут использоваться для согласованных изменений совокупности полей.
Мутации экземпляра структуры само по себе усложняет логику кода. Озвученный пример с заменой А на Д — это насколько специфичен должен быть код бизнес-логики для такого требования, и учитывая требования по производительности — структура с геттерами и сеттерами уже не совсем подходящее решение.
А еще есть отдельный подход 'мухи отдельно, котлеты отдельно'. Когда структуры реализуют только композицию полей, а все операции и инварианты вынесены в отдельные сущности. И тогда геттеры и сеттеры вообще как бы внешние. Но иметь свободную функцию, устанавливающую отдельное поле переданной как аргумент структуры — это какая-то экзотика уже.
1. Низкоуровневая структура, представляющая собой отображение какой-то структуры в памяти и/или в файле (пример — структуры в WINAPI). Ни геттеры, ни сеттеры там никому не нужны (хотя их и можно было бы добавить)
2. Класс, реализующий какую-то высокоуровневую логику, напрямую не маппящийся в память и/или в файл — здесь скорее всего возникнет необходимость инкапсулировать какую-то логику, могут появиться get-only property (и, не дай бог, set-only), логика которых может быть (а может и не быть) чуть-чуть сложнее, чем return _value. Ничего плохого в этом нет, интерфейс отдельно, реализация отдельно.
Таким образом, нельзя на 100% сказать, что геттеры / сеттеры зло — разумный программист будет добавлять их там, где они имеют смысл, и не будет делать лишних движений там, где это никому не нужно.
В C++ в этом нет никакого практического смысла, но видимо, витает как карго-культ эта java-практика, что все поля должны быть с геттерами-сеттерами, а объяснения к этому потерялось.
3-х геттеров (или даже 4-х): const lvalue, rvalue, const rvalue и, по вашему усмотрению, для неконстантного lvalue (даже если это уже просто очень странно звучит, так как проще использовать прямой доступ)
Насчет const rvalue немного неясно. Разве rvalue может быть const?
смысл в геттерах и сеттерах есть тогда, когда по каким-то причинам нужен нетривиальный доступ к переменной. Это в целом очевидно. Когда он нужен? Ну, когда нам может быть нужен pimpl/виртуальный доступ (например, это может требоваться для ускорения компиляции или сохранения бинарной совместимости между модулями); синхронизированный доступ (условные мьютексы); межъязыковое взаимодействие (Qt <-> QML, boost::python, protobuf...); или хотя бы чтобы что-то из вышеперечисленного было нужно в перспективе.
И я не вижу чтобы решение автора покрывало хоть какой-то из этих юзкейсов
Я иногда добавляю сеттеры для bebug билда, в которых находятся проверки присваиваемых данных. Помогает находить ошибки рантайма.
Пара мыслей о геттерах и сеттерах в C++