LINUX.ORG.RU

Структура vs класс


0

0

Часто замечаю вот такое. Вместо

struct Name { int param1; int param2; }

используют класс с доступом к переменным через функции (set, get). В той же Qt я вообще ни разу не видел «прямой» доступ к переменным. Чем это объясняется? Почему использование функций предпочтительнее? Всё таки количества кода во втором случае явно больше.

Ответ на: комментарий от jtootf

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

Они позволяют сохранить интерфейс при изменении внутреннего представления.

Нет, конечно, если это struct timeval какой-нибудь, то это бред. Но если это abstract data type, то вполне.

ratatosk
()

Геттеры/сеттеры нужны, когда установка значения нетривиальна: например сеттер может проверять значение на правильность, временно запрещать установку, делать дополнительные действия во время установки. В остальных случаях это ненужность.

staseg ★★★★★
()
Ответ на: комментарий от jtootf

>никакой полезной абстракции геттеры не предоставляют

4.2 Геттер - более общая конструкция. Капитан Очевидность сообщает, что если есть getSomething то поля something может и не быть. Оно может вычисляться на основе данных класс, делигироваться подклассам и так далее. Или, например, при измененни поля в сеттере выполяться дополнительные действия, поддерживающие целоствность системы. А что бы всё было единообразно + с «запасом на будущее» советуют пользоваться геттерами всегда.

theos ★★★
()
Ответ на: комментарий от staseg

когда установка значения нетривиальна

если установка нетривиальна, то и интерфейс должен быть нетривиальным. и уж точно не состоять из «попеременной установки значений»

jtootf ★★★★★
()
Ответ на: комментарий от staseg

Хорошо. Пример QPoint. Нужно ли оно там? Не проще ли убрать setX(), setY(), x() и y() заменив их переносом соответствующих переменных в public?

truetester
() автор топика
Ответ на: комментарий от jtootf

>если установка нетривиальна, то и интерфейс должен быть нетривиальным. и уж точно не состоять из «попеременной установки значений»

Интерфейс должен быть простым и по возможности однообразным (в части установки/получения данных).

staseg ★★★★★
()
Ответ на: комментарий от truetester

>Хорошо. Пример QPoint. Нужно ли оно там? Не проще ли убрать setX(), setY(), x() и y() заменив их переносом соответствующих переменных в public?

Тут скорее всего как раз погоня за однообразностью интерфейсов всего тулкита.

staseg ★★★★★
()
Ответ на: комментарий от jtootf

>никакой полезной абстракции геттеры не предоставляют

До тех пор, пока тебе не придётся менять внутреннюю структуру объекта. В случае с геттером/сеттером ты переписываешь только их, в случае с прямым обращениям к свойствам класса - переписываешь все обращения к ним, размазанные по программе.

// Ваш К.О.

KRoN73 ★★★★★
()
Ответ на: комментарий от truetester

>Не проще ли убрать setX(), setY(), x() и y() заменив их переносом соответствующих переменных в public?

А если тебе нужно будет поменять масштаб изображения и ввести пересчёт сохраняемых/извлекаемых значений? А проверять границы диапазона? А использовать унифицированный интерфейс с другими объектами?

...

Нет, конечно, если код пишет write-once и потом забывается - то такое не нужно :)

KRoN73 ★★★★★
()
Ответ на: комментарий от jtootf

>> ООП головного мозга

по большому счету так оно и есть

kto_tama ★★★★★
()
Ответ на: комментарий от jtootf

> интерфейс должен быть нетривиальным

Интерфес в идеале должен быть тривиальным. Не тривиальным может быть реализация интерфейса.

LamerOk ★★★★★
()
Ответ на: комментарий от ratatosk

>А вдруг в полярных координатах представлять захочется! Шутка. Но в ней где-то 10% правды.

Больше :) Гораздо больше... Вот буквально вчера пришлось переписывать метод country_name(), чтобы он возвращал данные не через прямое использование поля из БД, а через (new country(country_id))->title(). Поменял одну только строчку. Если бы сразу дёргал переменную класса - пришлось бы переписывать целый раздел сайта.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

>А если тебе нужно будет поменять масштаб изображения и ввести пересчёт сохраняемых/извлекаемых значений? А проверять границы диапазона? А использовать унифицированный интерфейс с другими объектами?

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

Когда мастер спросил об этом, новичок пришел в негодование.

- Hе будьте таким нетерпеливым - сказал он. - Я добавлю финансовые расчеты в окончательный вариант.

(c)Не моё :)

AF ★★★
()
Ответ на: комментарий от KRoN73

До тех пор, пока тебе не придётся менять внутреннюю структуру объекта

если у объекта есть и внутрення структура, и геттеры - значит с объектом что-то сильно не так

jtootf ★★★★★
()
Ответ на: комментарий от LamerOk

Интерфес в идеале должен быть тривиальным

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

jtootf ★★★★★
()
Ответ на: комментарий от AF

>Я добавлю финансовые расчеты в окончательный вариант.

Вот поэтому и нужно делать инкапсуляцию. Тогда можно сперва слепить несложный финансовый расчёт, а потом непринуждённо расширять его фенечками, не переписывая по 10 раз уже написанное :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

А если тебе нужно будет поменять масштаб изображения и ввести пересчёт сохраняемых/извлекаемых значений?

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

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

>если у объекта есть и внутрення структура, и геттеры - значит с объектом что-то сильно не так

Жизнь немного сложнее, чем тебе кажется.

KRoN73 ★★★★★
()
Ответ на: комментарий от jtootf

>этим должна будет заниматься другая сущность. принцип KISS - это очень полезно

Именно. KISS. Ты предлагаешь вводить лишнюю промежуточную сущность там, где она не нужна.

...

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

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Жизнь немного сложнее, чем тебе кажется.

ну тогда вопрос снимается. раз уж жизнь намного сложнее

jtootf ★★★★★
()
Ответ на: комментарий от KRoN73

Правильно!

>Вот поэтому и нужно делать инкапсуляцию. Тогда можно сперва слепить несложный финансовый расчёт, а потом непринуждённо расширять его фенечками, не переписывая по 10 раз уже написанное :)

А пользователи пусть пару лет подождут)))

Если серьезно, то оба подхода имеют право на жизнь. Какой из них надо применять в каждом конкретном случае - это вопрос не технический. Вот в твоем примере инкапсуляция более чем оправдана. А в каком другом проекте вероятность того, что что-то там внутри класса изменится - стремится к нулю.

AF ★★★
()
Ответ на: комментарий от KRoN73

>А если тебе нужно будет поменять масштаб изображения и ввести пересчёт сохраняемых/извлекаемых значений? А проверять границы диапазона? А использовать унифицированный интерфейс с другими объектами?

Непонятно чем эти функции мне помогут. Они даже не виртуальные.

truetester
() автор топика

тут все зависит от конкретной задачи конечно-же, и лепить все через геттеры - тоже не дело, а если вдруг появится задача описанная KRoN73'ом - можно легко приделать костыль сделать прозрачную замену в виде свойства( объекта с шаблонным классом и перегруженными операторами присваивания и приведения типа )

lester ★★★★
()
Ответ на: комментарий от KRoN73

>Больше :) Гораздо больше... Вот буквально вчера пришлось переписывать метод country_name(), чтобы он возвращал данные не через прямое использование поля из БД, а через (new country(country_id))->title(). Поменял одну только строчку. Если бы сразу дёргал переменную класса - пришлось бы переписывать целый раздел сайта.

Тут, вообще-то, явно нужна функция. А я говорю про хранение некоторых переменных, вроде массива.

truetester
() автор топика
Ответ на: комментарий от annoynimous

Противоположный тому, что к потолку привязан

готично. общественность одобряет

jtootf ★★★★★
()
Ответ на: комментарий от truetester

>Тут, вообще-то, явно нужна функция

В изначальной трактовке - не обязательно. ORM загружает значение из поля БД в переменную объекта. Потом её можно читать напрямую. Но если проект делается не по принципу «слепил на коленке и спихнул без поддержки», то через определённый срок (месяц, пол-года, год...) может потребоваться расширение логики объекта. И в случае прямого обращения к потрохам этого объекта, потребуется переписывать (и, что гораздо хуже, заново тестировать) всё, что с этим объектом работает.

Если же изначально были геттер/сеттер, то всё что потребуется - локальная правка внутренностей объекта. Всё.

...

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

KRoN73 ★★★★★
()

http://www.darronschall.com/weblog/2005/03/no-brain-getter-and-setters.cfm

http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

http://typicalprogrammer.com/?p=23

http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html

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

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

[qutoe]небольшая подборка для тех, кто считает что геттеры и сеттеры имеют какое-то отношение к инкапсуляции Я довольно-таки Ъ, и прочитал только первую ссылку. Да и рабочий день в разгаре, вроде :)

Так вот там речь не о том. Там о флеше. Разница в том, что сеттер и геттер там вызываются совершенно также, как и осуществляется доступ к public переменной. Это позволяет не менять интерфейс, если ты решил поменять представление.

А вообще в вопросе есть и терминологический аспект - что я подразумеваю под геттерами и сеттерами? Вот есть объект из предметной области, он обладает свойствами, которые надо получать/изменять. Методы, которые это делают, я называю сеттерами и геттерами. И представлять эти св-ва public полями иногда черевато.

ratatosk
()

А в D2 обращение к полю можно заменить на свойство без нарушения сорцовой совместимости.

А в D1 - на метод с таким же именем.

naryl ★★★★★
()
Ответ на: комментарий от naryl

небольшая подборка

Вторая ссылка тоже не особо в кассу - человек там сравнивает геттеры/сеттерв отнюдь не с public полями.

ratatosk
()
Ответ на: комментарий от KRoN73

> Поменял одну только строчку. Если бы сразу дёргал переменную класса - пришлось бы переписывать целый раздел сайта.

А поменять тип переменной не судьба? Не понял примера.

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

anonymous
()
Ответ на: комментарий от anonymous

>А поменять тип переменной не судьба?

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

P.S. всех веб-программистов нужно в обязательном порядке учить писать на хаскеле


Сразу после того, как Хаскелл станет доминировать в web'е.

KRoN73 ★★★★★
()
Ответ на: комментарий от naryl

>А в D2 обращение к полю можно заменить на свойство без нарушения сорцовой совместимости.

Или в Ruby. Или в Форте. Но тут речь о языках, где такого сахара нет :) Или где он осложнён родовыми проблемами.

KRoN73 ★★★★★
()
Ответ на: комментарий от ratatosk

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

ratatosk
()

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

Всё таки количества кода во втором случае явно больше.

Вы сильно беспокоитесь о размере исходного кода?

Booster ★★
()
Ответ на: комментарий от jtootf

> интерфейс должен быть максимально приближен к предметной области пользователя (следующему слою абстракции). если при использовании механизма абстракции (инкапсуляции в данном случае) мы не меняем интерфейс, значит где-то у нас случилась ошибка проектирования и code стал слегка bloated


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

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

LamerOk ★★★★★
()
Ответ на: комментарий от Booster

>Давать прямой доступ к данным это однозначно менее гибко.

Я с этим не спорю. Но так ли уж необходимо заворачивать их в функции для классов, которые являются просто хранилищем переменных? Вот смотрю сейчас на Qt и ужасаюсь. Куча функций состоит буквально из одной строчки. И главное, макрос Q_PROPERTY не позволяет работать с публичными переменными. Подавай ему две функции и всё тут.

truetester
() автор топика
Ответ на: комментарий от truetester

>Но так ли уж необходимо заворачивать их в функции для классов, которые являются просто хранилищем переменных?

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

KRoN73 ★★★★★
()
Ответ на: комментарий от LamerOk

3-я ссылка годная, я с ней согласен даже по большей части

вот и славно

Есть в этом рациональное зерно, но это в теории

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

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

Нет проблем, приведи проект с ревижн хистори, где бы было наоборот. ;))

LamerOk ★★★★★
()
Ответ на: комментарий от kto_tama

>>> Хаскелл станет доминировать в web'е.

и когда он начинал доминировать ?


Вот я и пишу - «_когда_ он станет доминировать, тогда...» Неужели так трудно читать?

KRoN73 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.