Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Не пользуйтесь функциями типа get/set


Автор: Rockie 7.7.2006, 21:28
Цитата
ВЕРЕВКА ДОСТАТОЧНОЙ ДЛИНЫ, 
ЧТОБЫ ВЫСТРЕЛИТЬ СЕБЕ В НОГУ 
Правила программирования на С и С++ 
Ален И. Голуб


не понимаю предпоследний абзац. что хотел сказать автор этим правилом?

Цитата
110.1. Не пользуйтесь функциями типа get/set (чтения и присваивания значений). 

Это правило в действительности то же, что и предыдущее "все данные должны быть закрытыми". Я выделил его, потому что есть такая распространенная ошибка среди начинающих программистов на С++. Нет разницы между: 

struct xxx 

{ 

int x; 

}; 


и: 

class xxx {

private: 

int x; 

public

void setx  ( int ix ){ x = ix;      } 

int    getx ( void )  { return x; } 

}


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

Сообщение реализует свойство. Открытая (public) функция реализует обработчик сообщения. Поля данных - лишние во внешнем мире; вы добавляете их лишь для того, чтобы иметь возможность реализовать свойство. Доступ к ним должен быть невозможен. 

Заметьте, что вы будете изредка видеть обработчик сообщений, который ничего не делает, кроме возврата содержимого поля или помещает в поле значение, переданное в виде аргумента. Этот обработчик тем не менее не является функцией типа get/set. Вопрос в том, как возникает такая ситуация. Нет абсолютно ничего плохого в том, если вы начинаете с ряда сообщений и затем решаете, что самым простым способом реализации сообщения является помещение специального поля в определение класса. Другими словами, этот обработчик сообщений не является усложненным способом доступа к полю; скорее, это поле является простым способом реализовать сообщение. Хотя вы попали в то же место, вы попали туда совершенно другим путем. 

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

Автор: bsa 7.7.2006, 21:44
Думаю, автор имел в виду, что если у тебя член класса x методами get и set только читается и изменятся, то эти методы не имеют смысла, так как компилятор все равно при оптимизации будет просто подставлять значение данного члена (без вызова метода).
Вот только я не уверен, что неиспользование это хорошо. По-моему, лучше get/set использовать, так как в случае если при изменении данного параметра придется еще выполнять ряд действий, то не придется менять интерфейс класса (проще говоря, не придется переписывать уже написанные программы, использующие этот класс).
А читабельность можно повысить правильным форматированием. smile 

Автор: Fazil6 7.7.2006, 22:09
bsa, 
нет совсем не это имел в виду автор. В этой книге автор несколько раз приводится пример про календарь.
на самом деле имеется в виду, что данные класса в правильно спроектированном классе не представляют инттереса вне класса. Нужно рассматривать класс, как самодостаточную вещь и оперрировать не данными класса, а его интерфейсом. Подход в том, что данные закрыты не для того чтобы просто затруднить к ним доступ, а в первую очередь потому, что в правильном классе нет необходимости в доступе к данным извне класса, а есть функции типа "сделай что-то" , "создай это" и тд. 
а set и get на самом деле  по сути  нарушают инкапсуляцию 

Автор: DeadSoul 7.7.2006, 22:10
Цитата(bsa @  7.7.2006,  21:44 Найти цитируемый пост)
Думаю, автор имел в виду, что если у тебя член класса x методами get и set только читается и изменятся, то эти методы не имеют смысла, так как компилятор все равно при оптимизации будет просто подставлять значение данного члена (без вызова метода).

В этом автор ошибается. 
1. С течением времени смысл этих методов может изменится
2. С getter-ами\setter-ами проще отлаживатся, т.е. найти то место где туда устанавливается не очень корректное значение
 

Автор: Fazil6 7.7.2006, 22:11
Цитата

А читабельность можно повысить правильным форматированием.

про читабельность там с иронией говорится.

Добавлено @ 22:19 
Цитата

С getter-ами\setter-ами проще отлаживатся, т.е. найти то место где туда устанавливается не очень корректное значение
весь смысл в том, что установка 
Цитата

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

Автор: bsa 7.7.2006, 22:20
Fazil6
Тебе бы книжки писать - объяснение намного понятнее. smile 

Автор: sergejzr 7.7.2006, 22:20
Fazil6, хорошо обьяснил.

Я тоже так понял, что автор не призывает "ни в коем случае не использовать геттеры и сеттеры". А хочет сказать, что если они в программе вдруг станут необходимы - класс спроектирован плохо. 

Автор: Fazil6 7.7.2006, 22:28
вообще-то я советую всем, кто не читал эту книжку, прочитать, хоть она и не первой свежести. Автор четко знает о чем пишет, все с прекрасными примерами из жизни и юмором. Читается очень увлекательно  

Автор: Rockie 7.7.2006, 22:55
Цитата(Fazil6 @  7.7.2006,  22:09 Найти цитируемый пост)
 в правильном классе нет необходимости в доступе к данным извне класса, а есть функции типа "сделай что-то" , "создай это" и тд. 
а set и get на самом деле  по сути  нарушают инкапсуляцию 


Ага! Если я правильно понял, то, к примеру, вместо set использовать инициализацию в конструкторе, а заместо get использовать к примеру методы "Распечататься",  "Записаться в файл" и др. Таким образом, необходимость в методах set и get должна отпасть сама собой. 

bsa, 
Fazil6, 
DeadSoul, 
sergej.z, 
всем участникам большое спасибо! 

Автор: En_t_end 8.7.2006, 11:42
А почему нельзя get'ы использовать ?(c set'ами все понятно...)

Добавлено @ 11:45 
допустим, мне нужно собрать статистику со всех классов. В каждом классе есть метод GetOccupied. Неужели этот метод нарушает инкапсуляцию ? То есть технологию аксессоров нельзя применять ?  

Автор: ivashkanet 8.7.2006, 13:38
Цитата(sergej.z @  7.7.2006,  22:20 Найти цитируемый пост)
 автор не призывает "ни в коем случае не использовать геттеры и сеттеры". А хочет сказать, что если они в программе вдруг станут необходимы - класс спроектирован плохо. 

А как передавать настройки новому классу?
Через методы, или может в конструкторе?
Цитата(Rockie @  7.7.2006,  22:55 Найти цитируемый пост)
а заместо get использовать к примеру методы "Распечататься",  "Записаться в файл" и др.

То же, ИМХО, ерунда. Невозможно учесть все возможные стороны применения нашего класса. Что если через год нам понадобиться метод которого нет? Переписывать класс?

P.S. Стандартные классы направо и налево используют открытые свойства. 
Врятли программисты не знали свое дело, когда проектировали их. Даже больше -- они же и создавали стандарты языка smile 
Представьте что бы было если бы "самый главный" класс Form не имел открытых свойств. Что бы тогда было. Заголовок, положение, размеры, ... все передавать через конструктор?
+ Обясните мне разницу между методом Form.SetCaption("Название формы") и Form.Caption = "Название формы" с точки зрения работы программы? ... Да ее просто НЕТ smile 
P.P.S. Я сам .Net-чик, поэтому не знаю как в С++ называется свойство заголовка формы smile

Добавлено @ 13:51 
Продолжу smile 
Несомненным плюсом Сеттеров является то, что мы можем провести валидацию данных и в случае несоответствия выкинуть исключение. Именно поэтому не стоит использовать открытые поля классов. Они не поддерживают валидацию.
У Геттеров такого явного преимущества нет  smile Кроме как запрета на чтение значения поля.
 smile P.S. Автор может и знает свое дело, но, ИМХО, писать не умеет  smile . Сполшные сложные предложения, обороты... Пришлось дважды перечитывать, прежде чем войти в курс дела.
Перечитайте этот отрывок. Все ли понятно после первого прочтения?
Цитата(Rockie @  7.7.2006,  21:28 Найти цитируемый пост)
Заметьте, что вы будете изредка видеть обработчик сообщений, который ничего не делает, кроме возврата содержимого поля или помещает в поле значение, переданное в виде аргумента. Этот обработчик тем не менее не является функцией типа get/set. Вопрос в том, как возникает такая ситуация. Нет абсолютно ничего плохого в том, если вы начинаете с ряда сообщений и затем решаете, что самым простым способом реализации сообщения является помещение специального поля в определение класса. Другими словами, этот обработчик сообщений не является усложненным способом доступа к полю; скорее, это поле является простым способом реализовать сообщение. Хотя вы попали в то же место, вы попали туда совершенно другим путем. 


 

Автор: Daevaorn 8.7.2006, 14:36
Цитата(ivashkanet @  8.7.2006,  14:38 Найти цитируемый пост)
поэтому не знаю как в С++ называется свойство заголовка формы 

Хорошая шуткаsmile

Цитата(ivashkanet @  8.7.2006,  14:38 Найти цитируемый пост)
Стандартные классы направо и налево используют открытые свойства. 

Можно пример? Ещё привлекает внимаение слово "свойства"...

Цитата(ivashkanet @  8.7.2006,  14:38 Найти цитируемый пост)
То же, ИМХО, ерунда. Невозможно учесть все возможные стороны применения нашего класса. Что если через год нам понадобиться метод которого нет? Переписывать класс?

Да. Если такое произошло - значит существет ошибка в проекитировании этого самого класса. 

Автор: Fazil6 8.7.2006, 14:46
Цитата

А как передавать настройки новому классу?
Через методы, или может в конструкторе?

именно через методы или в конструкторе.
Цитата

То же, ИМХО, ерунда. Невозможно учесть все возможные стороны применения нашего класса. Что если через год нам понадобиться метод которого нет? Переписывать класс?
это ты ерунду пишешь. Реализовывать надо то, что нужно, а не то, что когда-нибудь может пригодиться. 

Не думай, что гуишные классы NET, VCL или MFC являются стандартом программирования и речь идет о ООП в теории, а не конкретной реализации конкретных классов.
Свойство - это интерфейс класса, а не данные класса и именно об этом говорит автор. 

Автор: ivashkanet 8.7.2006, 15:44
Цитата(Daevaorn @  8.7.2006,  14:36 Найти цитируемый пост)
Хорошая шутка

Цитата(ivashkanet @  8.7.2006,  13:38 Найти цитируемый пост)
Form.Caption

Я что угадал? У нас (в .Net) заголовок формы устанавливается через Form.Text  smile 

Цитата(Daevaorn @  8.7.2006,  14:36 Найти цитируемый пост)
Ещё привлекает внимаение слово "свойства"...

Поля класса -- конретные "переменные" класса, т.е. его данные.
Свойства -- "обертки" для полей класса, сделанные с помощью get и set.
Врятли от языка к языку эти понятия меняются.  smile 
Цитата(Daevaorn @  8.7.2006,  14:36 Найти цитируемый пост)
Можно пример?

Я ведь дал пример 
Цитата(ivashkanet @  8.7.2006,  13:38 Найти цитируемый пост)
Заголовок, положение, размеры, ...

А вообще зайди IDE, выдели любой класс и любуйся открытыми СВОЙСТВАМИ в закладке Propertes.
Там перечисленны только они, полей нет smile 
Цитата(Fazil6 @  8.7.2006,  14:46 Найти цитируемый пост)
Свойство - это интерфейс класса, а не данные класса и именно об этом говорит автор. 

А я что спорю  smile 
Цитата(Fazil6 @  8.7.2006,  14:46 Найти цитируемый пост)
это ты ерунду пишешь. Реализовывать надо то, что нужно, а не то, что когда-нибудь может пригодиться. 

После нас хоть потоп. Да?
Представьте ситуацию: 
Мы спроектировали класс, который что-то обрабатывает, а потом выводит на экран (ShowOnScreen()). 
Мы продали этот класс. Его используют другие люди. Всем нравится, все в восторге.
Через некоторое время понадобился вывод на принтер. Они связваются с нами, мы лезем в код добавляем метод (SendToPrinter()). Через время понадобился вывод еще на что-нибудь...
Это выход? Вместо того чтобы открыть результат вычислений, после чего любой сможет написать свой вывод на что угодно.
О  smile придумал еще пример:
Вы вешаетесь на событие формы Resize, вам нужно перепозиционировать контролы на форме. Как это сделать, если размеры формы закрыты?

Обожаю когда люди критикуют только часть сообщения, а не всё полностью
Почему не было комментариев по этому поводу:
Цитата(ivashkanet @  8.7.2006,  13:38 Найти цитируемый пост)
Обясните мне разницу между методом Form.SetCaption("Название формы") и Form.Caption = "Название формы" с точки зрения работы программы?

или
Цитата(ivashkanet @  8.7.2006,  13:38 Найти цитируемый пост)
Представьте что бы было если бы "самый главный" класс Form не имел открытых свойств. Что бы тогда было. Заголовок, положение, размеры, ... все передавать через конструктор?

 

Автор: En_t_end 8.7.2006, 16:10
Цитата(En_t_end @  8.7.2006,  15:42 Найти цитируемый пост)
А почему нельзя get'ы использовать ?(c set'ами все понятно...)
Добавлено @ 15:45 
допустим, мне нужно собрать статистику со всех классов. В каждом классе есть метод GetOccupied. Неужели этот метод нарушает инкапсуляцию ? То есть технологию аксессоров нельзя применять ?  

Мне ответьте smile пожалуйста...
 

Автор: Daevaorn 8.7.2006, 16:49
ivashkanet, Меня просто удивило, то что ты говоришь про стандартные классы и тут же про какие-то формы. В стандартном С++ нет форм. И пример просил привести именном поэтому. В стандартной библиотеке С++ надо постараться, чтобы встретить set/get, хотя найти можно.
Цитата(ivashkanet @  8.7.2006,  16:44 Найти цитируемый пост)
А вообще зайди IDE, выдели любой класс и любуйся открытыми СВОЙСТВАМИ в закладке Propertes.

А у меня в IDE нет такого. хнык-хныкsmile((

Добавлено @ 16:51 
Цитата(En_t_end @  8.7.2006,  12:42 Найти цитируемый пост)
допустим, мне нужно собрать статистику со всех классов. В каждом классе есть метод GetOccupied. Неужели этот метод нарушает инкапсуляцию ? То есть технологию аксессоров нельзя применять ?   

Скорей всего по лигике этот класс сам должен эти данные записать в класс сборщика статистики 

Автор: ivashkanet 8.7.2006, 17:03
Цитата(Daevaorn @  8.7.2006,  16:49 Найти цитируемый пост)
Меня просто удивило, то что ты говоришь про стандартные классы и тут же про какие-то формы

А класс формы разве не стандартный класс?
Цитата(Daevaorn @  8.7.2006,  16:49 Найти цитируемый пост)
А у меня в IDE нет такого

Что за IDE такое??? 

Автор: Fazil6 8.7.2006, 17:05
Цитата

Поля класса -- конретные "переменные" класса, т.е. его данные.
Свойства -- "обертки" для полей класса, сделанные с помощью get и set.
Врятли от языка к языку эти понятия меняются.

речь не о наз ваниях функций. Речь о данных, а не о свойствах. SetCaption это не установка переменной, хранящей значение заголовка, а именно изменение заголовка

Цитата

А вообще зайди IDE, выдели любой класс и любуйся открытыми СВОЙСТВАМИ в закладке Propertes.
Там перечисленны только они, полей нет

вот и непонятно что именно и кому ты хочешь доказать...

Цитата

После нас хоть потоп. Да?
Представьте ситуацию: 
Мы спроектировали класс, который что-то обрабатывает, а потом выводит на экран (ShowOnScreen()). 
Мы продали этот класс. Его используют другие люди. Всем нравится, все в восторге.
Через некоторое время понадобился вывод на принтер. Они связваются с нами, мы лезем в код добавляем метод (SendToPrinter()). Через время понадобился вывод еще на что-нибудь...

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

Базовый класс Write.
От него наследуются классы WriteInFile, WriteInScreen и т.д.

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


En_t_end, 
Цитата

Мне ответьте  пожалуйста...

Да нечего отвечать. Все уже написано выше. 
Данные класса не должны никого интересовать вне этого класса.
Цитата

Неужели этот метод нарушает инкапсуляцию ?

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

Добавлено @ 17:07 
Цитата

А класс формы разве не стандартный класс?

это по какому стандарту?????? smile  

Автор: Daevaorn 8.7.2006, 17:09
Цитата(ivashkanet @  8.7.2006,  18:03 Найти цитируемый пост)
А класс формы разве не стандартный класс?

Нет.
Цитата(ivashkanet @  8.7.2006,  18:03 Найти цитируемый пост)
Что за IDE такое???  

Блокнот + CommandPromt + MinGW 
=))) 

Автор: Void 8.7.2006, 17:19
Что-то мне вспомнилась недоброй памяти http://forum.vingrad.ru/index.php?showtopic=95670, где затрагивались вопросы инкапсуляции. В воздухе отчетливо запахло догмами…
Автор сказал именно то, что хотел сказать:
Цитата
Нет абсолютно ничего плохого в том, если вы начинаете с ряда сообщений и затем решаете, что самым простым способом реализации сообщения является помещение специального поля в определение класса.

Не надо априори заворачивать все в get/set, и не надо априори считать открытый доступ к полям злом.

Позволю себе привести цитату с одного форума, не совсем on-topic, но очень близко:
Цитата
> ... если архитектор/программист начинает на первом же шаге строить объектную модель предметной области, то он не прав дважды. Во-первых, он начинает с конца. Во-вторых, он не задумывается о функционале.

Кстати, признаком такого проектирования являются вопросы, на манер: "У меня есть класс "Корова" и класс "Доярка". Где я должен разместить метод "доить", в "Корова.доитьКем(Доярка)" или в "Доярка.доитьКого(Корова)".


P.S.
Солгасен с DeadSoul:
Цитата
1. С течением времени смысл этих методов может изменится
2. С getter-ами\setter-ами проще отлаживатся, т.е. найти то место где туда устанавливается не очень корректное значение
 

Автор: ivashkanet 8.7.2006, 17:30
Цитата(Fazil6 @  8.7.2006,  17:05 Найти цитируемый пост)
SetCaption это не установка переменной, хранящей значение заголовка, а именно изменение заголовка

Поподробнее об это, пожалуйста.
Что мешает мне в сеттере СВОЙСТВА Caption ПОЛНОСТЬЮ продублировать код функции SetCaption smile 
Или это опять нарушение инкапсуляции
Цитата(Daevaorn @  8.7.2006,  17:09 Найти цитируемый пост)
Цитата(ivashkanet @  8.7.2006,  18:03 )А класс формы разве не стандартный класс?Нет

Цитата(Fazil6 @  8.7.2006,  17:05 Найти цитируемый пост)
это по какому стандарту

Хророшо, но тогда получается, что он неправильно спроектирован?
Цитата(Daevaorn @  8.7.2006,  17:09 Найти цитируемый пост)
Блокнот + CommandPromt + MinGW =))) 

 smile  smile 
Цитата(Fazil6 @  8.7.2006,  17:05 Найти цитируемый пост)
Базовый класс Write.

ОК, согласен. Но тогда все наши классы будут неподъемными монстрами, с КУЧЕЙ "лишнего" кода, который будет только заботиться о взаиможействии с остальным миром.
Я получу, наконец, ответы на свои вопросы?
Цитата
Обясните мне разницу между методом Form.SetCaption("Название формы") и Form.Caption = "Название формы" с точки зрения работы программы?

Заметьте, Form.Caption -- это не поле, а свойство, сделанное с помощью get и set, а в set помещен 1:1 код метода SetCaption
и 
Цитата
Представьте что бы было если бы "самый главный" класс Form не имел открытых свойств. Что бы тогда было. Заголовок, положение, размеры, ... все передавать через конструктор?
 

Автор: Daevaorn 8.7.2006, 17:35
Цитата(ivashkanet @  8.7.2006,  18:30 Найти цитируемый пост)
Я получу, наконец, ответы на свои вопросы?

Цитата
Обясните мне разницу между методом Form.SetCaption("Название формы") и Form.Caption = "Название формы" с точки зрения работы программы?


Заметьте, Form.Caption -- это не поле, а свойство, сделанное с помощью get и set, а в set помещен 1:1 код метода SetCaption
и 

В С++ нет такого лексического понятия как свойства! В это то собственно и весь топик, т.к. иногда приходится писать get/set
Ты похоже со своим уставом монастырем ошибся;) 

Автор: Fazil6 8.7.2006, 18:21
Цитата

Поподробнее об это, пожалуйста.

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

Цитата

ОК, согласен. Но тогда все наши классы будут неподъемными монстрами, с КУЧЕЙ "лишнего" кода, который будет только заботиться о взаиможействии с остальным миром.

да???????  smile  нифигасе!!!!!
Код

class Write
{
public:
    virtual void write(const SomeData &d) = 0;
};
/*================================*/
class WriteInFile : public Write
{
public:
    void write(const SomeData &d)
    {
        // реализация вывода в файл 
    }
};
/*================================*/
class WriteInPrinter : public Write
{
public:
    void write(const SomeData &d)
    {
        // реализация вывода на принтер
    }
};
/*================================*/
class MyClass
{
public: 
    void output(Write *w)
    {
       w->write(d);
    }
private:
    SomeData d;
};
/*================================*/
/*================================*/


Write *f = new WriteInFile();
Write *p = new WriteInPrinter();

MyClass mcl;

// выводим в файл
mcl.output(f);
// выводим на принтер
mcl.output(p);


где у меня лишний код? Хочешь выводить файл на стену или в космос - пиши соответствующего наследника Write и все в шакаладе. Работавший до этого код никак не изменится и работа его никак не изменится

Цитата

Что мешает мне в сеттере СВОЙСТВА Caption ПОЛНОСТЬЮ продублировать код функции SetCaption
 да отстань ты со своими свойствами. Разговор не о свойствах. Разговор о данных класса.
Цитата

Заметьте, Form.Caption -- это не поле, а свойство, сделанное с помощью get и set, а в set помещен 1:1 код метода SetCaption
такое впечатление, что ты сам с собой разговариваешь
 

Автор: En_t_end 8.7.2006, 18:21
Цитата(Daevaorn @  8.7.2006,  20:49 Найти цитируемый пост)
Скорей всего по лигике этот класс сам должен эти данные записать в класс сборщика статистики 

Цитата(Fazil6 @  8.7.2006,  21:05 Найти цитируемый пост)
если единственная задача этого метода вернуть значение члена класса, то да,

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

 

Автор: Fazil6 8.7.2006, 18:37
Цитата

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

Кстати, признаком такого проектирования являются вопросы, на манер: "У меня есть класс "Корова" и класс "Доярка". Где я должен разместить метод "доить", в "Корова.доитьКем(Доярка)" или в "Доярка.доитьКого(Корова)".

пустые слова. Что мне мешает думать о фукнционале при разработке объектной модели? Пример тоже ни о чем не говорит.

Цитата

не надо априори считать открытый доступ к полям злом.

Цитата

Солгасен с DeadSoul:

ты неправ

Добавлено @ 18:44 
Цитата

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

Автор: En_t_end 8.7.2006, 18:46
Цитата(Void @  8.7.2006,  21:19 Найти цитируемый пост)
если архитектор/программист начинает на первом же шаге строить объектную модель предметной области, то он не прав дважды.

Я наоборот считаю, что если сначала строить ОО-модель, то можно получить результат не только быстрей, но и качественне. Ты сразу накладываешь условие на некоторые параметры, подстраиваешь условия под возможности ООП, проектируешь с учетом ООП. Если же строить модель потом(я пробывал и так), приходится "обрезать" идею под возможности. Методом проб и ошибок, выбрал первое.

Добавлено @ 18:59 
Цитата(Fazil6 @  8.7.2006,  22:37 Найти цитируемый пост)
ну так это вообщето очевидно, что если есть правила, то правильно - это соблюдать правила и программирование здесь ничем от других областей жизни не отличается 

я не это имел ввиду smile Я говорил не просто о соблюдении правил. Если одна часть ПО будет спроектированна в виде библиотеки высокачественных классов, а другая в виде простого кода, набора итеративно-функциональных операторов, то это неизбежно приведет к возникновению get/set в библиотеке. Поэтому, чтобы в проекте ПО не было узких мест, в виде такого кода, следует проектировать ВСЁ ПО, как набор ОО-моделей.  

Автор: Void 8.7.2006, 19:00
Цитата(Fazil6 @  8.7.2006,  20:37 Найти цитируемый пост)
пустые слова. Что мне мешает думать о фукнционале при разработке объектной модели? Пример тоже ни о чем не говорит.

ОК. Вот тебе http://rsdn.ru/forum/Message.aspx?mid=1963888&only=1 на корень той дискуссии. Я не настаиваю на полной правоте этих слов.
Цитата(Fazil6 @  8.7.2006,  20:37 Найти цитируемый пост)
не надо априори считать открытый доступ к полям злом.

Цитата

Солгасен с DeadSoul:

ты неправ

По линку на ту ветку (в здешних РВ) сходил? Там тоже человек отстаивал антиобъектнориентированность геттеров/сеттеров и свойств, как синтаксического сахара над ними.

Кстати, смысл есть и в закрытых или защищенных mutators. Уж они-то инкапсуляцию не нарушают, но иногда оправданны, т.к. повышают сопровождаемость кода. 

Автор: Fazil6 8.7.2006, 19:46
Цитата

По линку на ту ветку (в здешних РВ) сходил?

там на другую тему дискуссия 

Автор: ivashkanet 9.7.2006, 10:33
Цитата(Fazil6 @  8.7.2006,  18:21 Найти цитируемый пост)
для того,чтобы в заголовке окна на экране поменялся текст недостаточно только изменить одну переменную в класе

А кто говорит, что я собираюсь изменить только одну переменную? В сеттере я могу написать хоть сотню строк кода  smile 
Код

public
void setCaption  ( string iCaption )
{ 
// Что-нибудь делаем
// Делаем проверки на валидность
_caption = iCaption;
// И в завершение то же что нибудь делаем     } 

int    getCaption ( void )  { return _caption; } 

Цитата(Daevaorn @  8.7.2006,  17:35 Найти цитируемый пост)
В С++ нет такого лексического понятия как свойства! В это то собственно и весь топик, т.к. иногда приходится писать get/set

Ага, читал. А то что вы сейчас имеете досталось вам от несовершенного C smile 
Цитата(Daevaorn @  8.7.2006,  17:35 Найти цитируемый пост)
Ты похоже со своим уставом монастырем ошибся;) 

Монастырь агрономов-осеннизаторов?
Цитата(Fazil6 @  8.7.2006,  18:21 Найти цитируемый пост)
 Разговор не о свойствах. Разговор о данных класса.

С какой такой стати?
Цитата(Fazil6 @  8.7.2006,  18:21 Найти цитируемый пост)
   void write(const SomeData &d)

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

Цитата(Void @  8.7.2006,  19:00 Найти цитируемый пост)
геттеров/сеттеров и свойств, как синтаксического сахара

Полностью согласен с Void. Сеттеры и геттеры это синтаксический сахар, ИМХО.
Сеттер -- более удобная (синтаксическая) замена функции типа void, принимающей один параметр.
Геттер -- функции любого типа без параметров.

 

Автор: Daevaorn 9.7.2006, 11:29
Цитата(ivashkanet @  9.7.2006,  11:33 Найти цитируемый пост)
 А то что вы сейчас имеете досталось вам от несовершенного C 

Если учесть что в С вообще нет ООП, то о каком наследстве может идти речь?
Цитата(ivashkanet @  9.7.2006,  11:33 Найти цитируемый пост)
С какой такой стати?

А с такой, что пытался кике-то "свойства" и формы приписать
 

Автор: En_t_end 9.7.2006, 11:35
Цитата(ivashkanet @  9.7.2006,  14:33 Найти цитируемый пост)
Монастырь агрономов-осеннизаторов?

по-моему это оскорбление... 

Автор: ivashkanet 9.7.2006, 12:04
Цитата(Daevaorn @  9.7.2006,  11:29 Найти цитируемый пост)
"свойства"

Повторяю: свойства -- это данные класса завернутые в get и set (неважно каким образом завернутые).
Цитата(Daevaorn @  9.7.2006,  11:29 Найти цитируемый пост)
 формы

Это просто пример класса у которого много открытых "данных". И как их закрыть я ума не приложу  smile 
Цитата(Daevaorn @  9.7.2006,  11:29 Найти цитируемый пост)
Если учесть что в С вообще нет ООП, то о каком наследстве может идти речь?

Почитай внимательно первый пост
Цитата(En_t_end @  9.7.2006,  11:35 Найти цитируемый пост)
по-моему это оскорбление... 

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

Добавлено @ 12:08 
Цитата(ivashkanet @  9.7.2006,  12:04 Найти цитируемый пост)
Почитай внимательно первый пост

Цитата(Rockie @  7.7.2006,  21:28 Найти цитируемый пост)
Конечно, эта организация означает, что С++ не может быть эффективно использован в гибридной среде С/С++, потому что интерфейс между двумя половинами программы уничтожает инкапсуляцию, которой вы так сильно старались добиться. В известном смысле жаль, что С++ создан на основе С, потому что это просто подстрекает нас к ошибкам





 

Автор: Fazil6 9.7.2006, 12:26
ivashkanet, 
Цитата

Повторяю: свойства -- это данные класса завернутые в get и set (неважно каким образом завернутые).

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

Это просто пример класса у которого много открытых "данных". И как их закрыть я ума не приложу

пример с формой не говорит, что это стандарт и так нужно делать всегда. 
Цитата

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

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

ВЕРЕВКА ДОСТАТОЧНОЙ ДЛИНЫ, 
ЧТОБЫ ВЫСТРЕЛИТЬ СЕБЕ В НОГУ 
Правила программирования на С и С++ 
Ален И. Голуб


Цитата

Ага, читал. А то что вы сейчас имеете досталось вам от несовершенного C 

И все таки,  что мы такое сейчас имеем???? 

Автор: ivashkanet 9.7.2006, 12:40
Цитата(Fazil6 @  9.7.2006,  12:26 Найти цитируемый пост)
в том то и дело, что ты неправильно себе представляешь тему изначально поднятую в топике

Поднятая тема -- не стоит использовать gettter и setter в коде?
Или я не так понял, и поднятая тема -- не стоит использовать gettter и setter в коде, если они ничего не делает кроме как напрямую обращаются к данным? Как в примере.
Цитата(Fazil6 @  9.7.2006,  12:26 Найти цитируемый пост)
мало того - ты неправильно представляешь даже то, что пытаешься здесь доказать.

Это врятли. Но то что я плохо изъясняю свою позицию --- с этим  я могу согласиться

Цитата(Fazil6 @  9.7.2006,  12:26 Найти цитируемый пост)
Свойства - это не данные класса, это такая реализация интерфейса класса.

Нееее, эт я в курсе smile Даже много раз писал... Но тема-то про гет и сет, а это и есть свойства  smile 
Цитата(Fazil6 @  9.7.2006,  12:26 Найти цитируемый пост)
И все таки,  что мы такое сейчас имеем???

Забудь, я просто перефразировал автора.
Цитата(Fazil6 @  9.7.2006,  12:26 Найти цитируемый пост)
пример с формой не говорит, что это стандарт и так нужно делать всегда. 

Но таких классов не одна форма. Их сотни smile 
P.S. Предлагаю закончить ругаться smile . Объясните мне четко и понятно в чем я не прав, пожалуйста. 

Автор: likehood 9.7.2006, 18:42
Цитата(ivashkanet @  8.7.2006,  18:30 Найти цитируемый пост)
Что мешает мне в сеттере СВОЙСТВА Caption ПОЛНОСТЬЮ продублировать код функции SetCaption

Вообще-то, SetCaption и есть сеттер для "свойства" Caption, куда ты собираешься дублировать код?
Цитата(ivashkanet @  9.7.2006,  11:33 Найти цитируемый пост)
В С++ нет такого лексического понятия как свойства! В это то собственно и весь топик, т.к. иногда приходится писать get/set    

Ага, читал. А то что вы сейчас имеете досталось вам от несовершенного C

В отсутствии свойств С не при чем, вон в C++ Builder'е свойства давно есть как расширение языка. Может в следующий стандарт их все же включат. 

Автор: DeadSoul 9.7.2006, 18:52
Цитата(baronp @  9.7.2006,  18:42 Найти цитируемый пост)
Может в следующий стандарт их все же включат

А зачем? 

Автор: ivashkanet 9.7.2006, 18:59
Цитата(baronp @  9.7.2006,  18:42 Найти цитируемый пост)
Вообще-то, SetCaption и есть сеттер для "свойства" Caption, куда ты собираешься дублировать код?

Дя..., Это все отличие синтаксисов языков. Под SetCaption я имел в виду просто функцию, а не сеттер.
Функциями ж типа можно пользоваться.
А вообще, повторю свою позицию:
Цитата(ivashkanet @  9.7.2006,  10:33 Найти цитируемый пост)
Сеттер -- более удобная (синтаксическая) замена функции типа void, принимающей один параметр.
Геттер -- функции любого типа без параметров.

И никакой разницы между ними нет

Добавлено @ 19:00 
Цитата(baronp @  9.7.2006,  18:42 Найти цитируемый пост)
В отсутствии свойств

Как то странно: get и set есть, а свойств нет  smile  

Автор: ivashkanet 9.7.2006, 19:17
Цитата(Fazil6 @  9.7.2006,  12:26 Найти цитируемый пост)
Ничего здесь не открывается.

Как так?
Код

class DataGetter : public Write
{
public:
    void write(const SomeData &d)
    {
        Data=d;
    }
public someData Data;
};

Write *dg= new DataGetter();

MyClass mcl;

// копируем данные
mcl.output(dg);

// получаем данные
public someData Data= dg.Data

// делаем с данными все что хотим

 smile 
P.S. Мог накосячить с синтаксисом, так как не в курсе про * и & (приведение к ссылке и назад). Но идея, думаю, ясна. 

Автор: likehood 9.7.2006, 19:24
Цитата(ivashkanet @  9.7.2006,  19:59 Найти цитируемый пост)
Сеттер -- более удобная (синтаксическая) замена функции типа void, принимающей один параметр.

В С++ сеттер это и есть "функция типа void, принимающая один параметр".

Добавлено @ 19:28 
Цитата(DeadSoul @  9.7.2006,  19:52 Найти цитируемый пост)
Может в следующий стандарт их все же включат    

А зачем? 

Удобная все таки штука - свойства! 

Автор: Fazil6 9.7.2006, 19:33
ivashkanet
мда.... как все запущено....
ты бы почитал первые сообщения в этой ветке.

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

Автор: ivashkanet 9.7.2006, 19:57
Цитата(baronp @  9.7.2006,  19:24 Найти цитируемый пост)
В С++ сеттер это и есть "функция типа void, принимающая один параметр"

А с ней (SetCaption()) нельзя делать вот так: Caption="Caption" smile 
Цитата(Fazil6 @  9.7.2006,  19:33 Найти цитируемый пост)
ты бы почитал первые сообщения в этой ветке.

Перечитал, ну и что?
Меня возмутило то, как понял вас автор топика (а по другому вас понять невозможно smile, он понял правильно)
Цитата(Rockie @  7.7.2006,  22:55 Найти цитируемый пост)
Ага! Если я правильно понял, то, к примеру, вместо set использовать инициализацию в конструкторе, а заместо get использовать к примеру методы "Распечататься",  "Записаться в файл" и др. Таким образом, необходимость в методах set и get должна отпасть сама собой. 

Инициализация в конструкторе:
А если у нас десяток полей (настроек класса) и все они необязательные? Делать конструктор на 10 параметров, или, еще лучше, 10! конструкторов на все случаи жизни. smile 
Методы "Распечататься",  "Записаться в файл" и др.:
Невозможно предусмотреть все варианты.
А твой, Fazil6, пример ничем не лучше чем get или функции, возвращающей значение.
Цитата(Fazil6 @  9.7.2006,  19:33 Найти цитируемый пост)
Если у тебя в классе есть необходимость в данных другого класса

А если цель моего класса -- обработать данные, то как мне их передавать?
Наилучший выход метод Execute() или Calculate(), которые вернут вычисленное значение/структуру.
А это ничем не лучше чем get 

Автор: Daevaorn 9.7.2006, 20:02
Цитата(ivashkanet @  9.7.2006,  20:57 Найти цитируемый пост)
А если у нас десяток полей (настроек класса) и все они необязательные? 

Ошибка проектирования
Цитата(ivashkanet @  9.7.2006,  20:57 Найти цитируемый пост)
Невозможно предусмотреть все варианты.

Ошибка проектирования 

Автор: Fazil6 9.7.2006, 20:36
Цитата

Перечитал, ну и что?
Меня возмутило то, как понял вас автор топика (а по другому вас понять невозможно , он понял правильно)

именно. Именно это я и говорил. Именно так он меня и понял. Иэто правильно. Пользователь класса не должен задумываться о данных класса. Они его не интересуют.

Цитата

Инициализация в конструкторе:
А если у нас десяток полей (настроек класса) и все они необязательные? Делать конструктор на 10 параметров, или, еще лучше, 10! конструкторов на все случаи жизни.

изменить класс. Это плохой класс, если нужно создав класс начинать настраивать его установкой  значений его переменных. Не надо только начинать мне приводить всякие классы форм из NET или VCL. Это не стандарт правильного проектирования классов всилу ряда причин. Сейчас разговор об объектно ориентированном подходе в теории.
Цитата

Методы "Распечататься",  "Записаться в файл" и др.:
Невозможно предусмотреть все варианты.
 
Во первых перед программистом никогда не должна стоять задача предусмотреть все варианты. Перед ним стоит конкретная задача, а не некий гипотетический худший случай. Во вторых я привел тебе пример, когда классы разработаны так, что добавления нового функционала не затрагивают ни одной строчки работавшего до этого кода. И это правильно. 
Цитата

А твой, Fazil6, пример ничем не лучше чем get или функции, возвращающей значение.

Причем здесь лучше или хуже? Я привел тебе пример того, что get и set вообще не нужныпри таком подходе, и это правильно. 

Цитата

А если цель моего класса -- обработать данные, то как мне их передавать?
Наилучший выход метод Execute() или Calculate(), которые вернут вычисленное значение/структуру.
А это ничем не лучше чем get 

бред какой-то. Ничего не понял. В моем примере Write как раз обрабатывает данные. Разве он говорит "дай мне вот эти свои данные и я их будуобрабатывать"?
 

Автор: ivashkanet 9.7.2006, 20:45
Забейте. Каждый остался при своем мнении.
Всем спасибо, что уделили мне время smile  

Автор: Rockie 10.7.2006, 13:11
возможно повторюсь за Fazil6, но все-таки. я думаю здесь можно вспомнить The Dependency Inversion Principle - принцып иверсии зависимостей. 

по сути он состоимт из двух частей:
Цитата
A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS  SHOULD DEPEND UPON ABSTRACTIONS.


и в пример приводится программа "Copy". модуль copy может читать с клавиатуры и записывать на принтер.

                          ---- Copy-----
                         |                    | 
                        Read            Write
                      Keyboard       Printer

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

                          ---->Copy<----
                         |                      | 
                      Abstract       Abstract       
                       Reader         Writer
                         |                      |
                        Read          Write
                      Keyboard       Printer

то есть в классах Abstr. Reader и Writer содержатся чистые виртуальные функции, и мы сможем добавить в систему "чтение из файла" или принтер другой модели, не трогая при этом Copy. предусмотреть 50 моделей принтеров действительно невозможно, но можно постараться сделать так, чтобы их потом можно было безболезненно добавлять. 

Автор: likehood 11.7.2006, 09:10
Цитата(ivashkanet @  9.7.2006,  20:57 Найти цитируемый пост)
А с ней (SetCaption()) нельзя делать вот так: Caption="Caption" 

нет


Цитата(Fazil6 @  9.7.2006,  21:36 Найти цитируемый пост)
Не надо только начинать мне приводить всякие классы форм из NET или VCL. Это не стандарт правильного проектирования классов всилу ряда причин.

Какая GUI библиотека является стандартом и что это за ряд причин? 

Автор: Leksey 12.7.2006, 01:54
А еще вопрос можно?

как реализовать класс Vector с тремя полями x,y,z не используя доступ к данным?

Или его существование будет ошибкой проектировки? 

Автор: Daevaorn 12.7.2006, 09:44
Leksey, А чем этот Vector принципиально отличается от всего выше рассмотренного? Ничем, а значит и требования для него те же. 

Автор: Fazil6 12.7.2006, 09:54
Цитата

А еще вопрос можно?

можно
Цитата

как реализовать класс Vector с тремя полями x,y,z не используя доступ к данным?

напрягаешь голову, думаешь и реализовываешь

Цитата

Или его существование будет ошибкой проектировки?
 
если он мне нахрен не надо в программе, то конечно это 
Цитата
будет ошибкой проектировки
 

Автор: UnrealMan 12.7.2006, 10:26
Цитата(Fazil6 @  7.7.2006,  22:09 Найти цитируемый пост)
на самом деле имеется в виду, что данные класса в правильно спроектированном классе не представляют инттереса вне класса

Даже если речь идёт о классе-наследнике? Т.е. код вроде

Код
class BaseClass
{
    DataType1 data1;
    DataType2 data2;
protected:
    DataType2 &GetData2() { return data2; }
public:
    // ...
};

class DerivedClass : public BaseClass
{
    // ...
};

это уже ошибка проектирования?

Цитата(Fazil6 @  8.7.2006,  18:21 Найти цитируемый пост)
где у меня лишний код? Хочешь выводить файл на стену или в космос - пиши соответствующего наследника Write и все в шакаладе. 

Странный код, однако :-‎)
Начнём с того, что непонятна природа SomeData. Что будут с ней делать функции write производных от Write классов? Кроме того, что мы будем делать, если нам понадобится не просто «распечатать» какое-то одно поле, а ещё и какие-то операции осуществить (причём, возможно, с несколькими полями)? Или все данные класса нужно непременно сосредоточить в одном-единственном поле d типа SomeData, и это есть хорошо?

Цитата(Fazil6 @  8.7.2006,  18:21 Найти цитируемый пост)
Write *f = new WriteInFile();
Write *p = new WriteInPrinter();

Ну это вообще шедевр, заслуживащий наивысших похвал :-‎) 

Автор: Leksey 12.7.2006, 12:38
Fazil6  А что так грубо?

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

Автор: Fazil6 12.7.2006, 12:50
Цитата

Даже если речь идёт о классе-наследнике? Т.е. код вроде
...
это уже ошибка проектирования?

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

Цитата

Странный код, однако :-‎)
Начнём с того, что непонятна природа SomeData.

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

Что будут с ней делать функции write производных от Write классов?

Вотм то и дело. Есть интерфейс и он определен

Код

void output(Write *w)
    {
       w->write(d);
    }


Классу по барабану, что вы будете делать с данными. Куда хотите, туда и выводите. Класс Сам решает какие данные он хочет вывести и передает их.    
Цитата

Кроме того, что мы будем делать, если нам понадобится не просто «распечатать» какое-то одно поле, а ещё и какие-то операции осуществить (причём, возможно, с несколькими полями)?
 
 Опять пишем класс на на все случаи жизни?
и что? Необходимость в доступе к данным == неправильный дизайн. Проектирование  + виртуальные функции - вот инструмент решения проблем. Сколько еще раз повторить? 

Или Вы мне надеетесь доказать, что инкапсуляция куйня?

Цитата

Или все данные класса нужно непременно сосредоточить в одном-единственном поле d типа SomeData, и это есть хорошо?

данные вне класса никому не нужны.
Представьте себе функцию swap. Функция сидит себе и ничего не делает. Вдруг ее вызвали. Она получила 2 ссылки и поменяла их значения местами. Какое ей дело кто ее вызвал? Какое ей дело кто ей эти аргументы передал? По барабану что эти данные значат. Тоже самое происходит и снаружи этой функции. Когда кто-то вызывает функцию swap ему 100% до лампады как эта функция будет выполнять то, что от нее ждут. Она выдала результат  и ауфидерзейн. Теперь представляем функцию, которая ходит за всеми и просит их дать ей 2 переменные и уж она их обработает. Бред.

Почему по вашему с классами это не Бред?
 
При хорошем проектировании вопросы, которыми вы пытаетесь меня победить не возникают вообще. 
UnrealMan, 
Цитата

Ну это вообще шедевр, заслуживащий наивысших похвал :-‎)

а тут уж что тебя не устраивает?
 

Автор: Fazil6 12.7.2006, 13:07
Цитата

Fazil6  А что так грубо?

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

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

Автор: likehood 12.7.2006, 14:14
Fazil6, в твоем первом примере с классом Write ты говорил, что нет необходимости давать доступ  к внутренней структуре класса с помощью get/set? Если да, то твой пример ничего не объяснет, поскольку Write это абстракный класс, не содержащий никаких данных. Все данные передаются явно методу write, но откуда беруться эти данные? Если из другого класса, то здесь без get() не обойтись, а если это просто внешний объект, то причем здесь тогда инкапсуляция?
Может на более высоких уровнях абстракции и можно обойтись без get/set, то при реализации таких базовых классов как Point сложно обойтись без getX(), getY(), getZ().
Вообще, если класс представляет из себя некоторую структуру данных, то он далеко не всегда знает как эти данные обрабатывать. Тут одним наследованием и полиморфизмом не обойтись, предется открывать доступ к части данных. 

Автор: Fazil6 12.7.2006, 14:30
Цитата

Fazil6, в твоем первом примере с классом Write ты говорил, что нет необходимости давать доступ  к внутренней структуре класса с помощью get/set? Если да, то твой пример ничего не объяснет, поскольку Write это абстракный класс, не содержащий никаких данных. Все данные передаются явно методу write, но откуда беруться эти данные? Если из другого класса, то здесь без get() не обойтись, а если это просто внешний объект, то причем здесь тогда инкапсуляция?
Write - это класс обрабатывающий данные. MyClass - это класс у которого есть данные (вот они!!! данные!!!), которые он хочет обработать. Ему передается "обработчик" (наследник Write) и с помощью этого обработчика(у которого тоже есть открытый интерфейс) класс MyClass обрабатывает свои данные. Никто ни у кого никакие данные здесь не запрашивает. Этому обработчику глубоко до фени откуда взялись данные, чьи они и зачем. Он знает тип этих данных и знает что с ними нужно сделать.

Добавлено @ 14:36 
Цитата

Может на более высоких уровнях абстракции и можно обойтись без get/set, то при реализации таких базовых классов как Point сложно обойтись без getX(), getY(), getZ().
Вообще, если класс представляет из себя некоторую структуру данных, то он далеко не всегда знает как эти данные обрабатывать. Тут одним наследованием и полиморфизмом не обойтись, предется открывать доступ к части данных. 

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

Автор: likehood 12.7.2006, 15:38
А как класс Write получит доступ к данным SomeData?
С помощью get/set или того хуже SomeData - это структура с открытыми данными? 

Автор: Дрон 12.7.2006, 15:49
baronp, не тут идея как раз в том, что для Write доступ к SomeData не нужен.
Наоборот SomeData использует Write и внутри себя подготваливает данные в том виде, в котором Write готов их принимать.
Всё красиво в теории. Но "В теории нет разницы между теорией и практикой, на практике же она есть" (см. мою подпись).
Никогда не поверю, что можно эффективно писать не интересуясь состоянием объекта до того, как потребовать от него действия.

Вы же предпочтёте проверить сколько у вас денег в кошельке прежде, чем сделать дорогую покупку, а не говорить потом у кассы: "Ooops, an exception occured" smile   

Автор: likehood 12.7.2006, 16:47
Цитата(Дрон @  12.7.2006,  16:49 Найти цитируемый пост)
Наоборот SomeData использует Write и внутри себя подготваливает данные в том виде, в котором Write готов их принимать.

То есть в SomeData есть метод getDataToWrite()?
Понятно, что дело не в названии метода, но чем же он в таком случае отличается от геттера? 

Автор: Fazil6 12.7.2006, 17:08
Цитата

А как класс Write получит доступ к данным SomeData?
С помощью get/set или того хуже SomeData - это структура с открытыми данными?

Повторяю : это абстрактный пример. Не важно что такое SomeData. Считайте, что это int. Вы за деревьями леса не видите.
Цитата

Вы же предпочтёте проверить сколько у вас денег в кошельке прежде, чем сделать дорогую покупку, а не говорить потом у кассы: "Ooops, an exception occured"
   
Я - класс , деньги - мои данные. Я проверяю и ничего в этом страшного нет. Это ведь в классе происходит. А вот когда на просьбу показать товар, продавец потребует показать сколько у меня есть денег...  Никого не должны интересовать мои деньги кроме меня. 
 

Автор: likehood 12.7.2006, 17:20
То есть класс, содержащий данные должен знать о классах, которые будут эти данные обрабатывать (точнее, не о самих классах, а о интерфейсе Write). Если это библиотечный класс, то имхо будет сложно учесть в нем все возможные способы работы с его данными. 

Автор: Fazil6 12.7.2006, 17:30
Цитата

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

с его данными тоже никто работать не собирается. smile 
Никто не должен предусматривать все способы. Пиши те способы, которые нужны на данный момент. Если потребуется новый способ, ты напишешь новый класс, наследник Write реализуя интерфейс нужным тебе способом и все. С работавшим до этого кодом ничего делать не нужно. 

Автор: likehood 12.7.2006, 17:39
Цитата(Fazil6 @  12.7.2006,  18:30 Найти цитируемый пост)
с его данными тоже никто работать не собирается

его данные использует метод write для вывода чего-то там на печать. 

Автор: Fazil6 12.7.2006, 17:58
Цитата

его данные использует метод write для вывода чего-то там на печать. 

ну да, конечно. Но это ведь данные этого класса(наследника Write) и нигде кроме самого класса они не используются. 

А... Я понял о чем вы. В том смысле, что данные Write используются в его наследнике? Так это все из тойже оперы. Классы не должны наследовать данные (бывают конечно исключения но это исключения). Ведь я стою на принципе, что данные не интересуют никого вне класса в том числе и наследников. Почему тогда они наследуются? Если они не интересуют никого Я уже писал выше, плохо когда детали реализации становятся частью интерфейса между родителем и наследником 

Автор: likehood 12.7.2006, 19:39
что то я запутался: где у тебя храняться данные, которые надо вывести на печать или на экран?
в потомке Write или где-то еще? 

Автор: Fazil6 12.7.2006, 22:16
Цитата

что то я запутался: где у тебя храняться данные, которые надо вывести на печать или на экран?
в потомке Write или где-то еще? 

по моему очень простой пример. Функция write получает данные в аргументе от MyClass и куда-то их записывает.

 

Автор: likehood 12.7.2006, 22:37
Еще раз повторю свой вопрос: как метод write получит доступ к SomeData.
В данном случае это принципиально, поскольку класс Write и его наследники не имеют своих данных, а служат лишь для обработки данных, полученных извне. Если SomeData это простая структура, то тогда причем тут полиморфизм, обращаемся напрямую к полям этой структуры и выводим все на печать. Если структура SomeData изменится, придется переписывать много кода. Если же доступ к SomeData идет через get/set, то есть надежда, что придется переписать только эти методы.
Кстати, в данном первом примере вполне можно было обойтись без полиморфизма: просто наделать функции типа writeToFile, writeToPrinter и т.д. Как же тогда получить доступ к данным SetData без get/set и без нарушения инкапсуляции? 

Автор: Fazil6 12.7.2006, 22:50
еще раз повтаряю
Цитата

Повторяю : это абстрактный пример. Не важно что такое SomeData. Считайте, что это int. Вы за деревьями леса не видите.


Добавлено @ 23:02 
Цитата

Если SomeData это простая структура, то тогда причем тут полиморфизм, обращаемся напрямую к полям этой структуры и выводим все на печать. Если структура SomeData изменится, придется переписывать много кода. Если же доступ к SomeData идет через get/set, то есть надежда, что придется переписать только эти методы.
Кстати, в данном первом примере вполне можно было обойтись без полиморфизма: просто наделать функции типа writeToFile, writeToPrinter и т.д. Как же тогда получить доступ к данным SetData без get/set и без нарушения инкапсуляции? 
 
ничего не понял. 

 

Автор: likehood 12.7.2006, 23:16
Цитата(Fazil6 @  12.7.2006,  23:50 Найти цитируемый пост)
Повторяю : это абстрактный пример.

Цель этого примера была конкретная: показать, что можно обойтись без меода get().
Я просто пытаюсь понять, действительно ли это так в данном "абстрактном" случае.

Цитата(Fazil6 @  12.7.2006,  23:50 Найти цитируемый пост)
ничего не понял.

Возможно, мы просто говорим о разных вещах. 

Автор: Fazil6 13.7.2006, 09:22
Цитата

Цель этого примера была конкретная: показать, что можно обойтись без меода get().
Я просто пытаюсь понять, действительно ли это так в данном "абстрактном" случае.

там разве где-нибудь есть такой или подобный метод? 

Автор: likehood 13.7.2006, 10:35
Fazil6, похоже мы и вправду говорим о разных вещах. 

Автор: UnrealMan 13.7.2006, 11:59
Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
да. 
Это тоже самое, когда твои данные были бы просто protected

Нет, это не совсем то же самое.

Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
Если DerivedClass оперирует данными, то почему эти данные находятся в другом классе.

А если эти данные нужны в обоих классах (BaseClass и DerivedClass) и они используются несколькими общими методами? Предлагаешь продублировать все эти данные и связанные с их обработкой методы?

Код
class Base
{
    Data data;
protected:
    Data &GetData() { return data; }
public:
    ReturnType1 Method1() { /* использует data */ };
    ReturnType2 Method2() { /* использует data */ };
    ....
};

class Derived : public Base
{
    /* ну что, заново объявляем data, заново пишем Method1, Method2 и т.д.? */
    ReturnType3 Method3( /* может использовать data через GetData */ );
};


Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
Данные класса - это деталь реализации, когда в наследовании принимают участия данные, то детали реализации становятся частью интерфейса

Но интерфейс-то не открытый, а защищённый.

Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
а это плохо.

А терять в общности, которую мы могли бы выразить с помощью защищённых данных и применённых к ним методов, – это хорошо?

Цитата(UnrealMan @  12.7.2006,  10:26 Найти цитируемый пост)
Начнём с того, что непонятна природа SomeData. 

Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
никакого значения это не имеет

Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
Опять пишем класс на на все случаи жизни?

Эт я просто намекаю на то, что SomeData – это скорее некий универсальный тип, предназначенный для передачи данных в методы вывода, нежели тип поля класса (часто ли нужно выводить именно значение какого-то одного поля?). Т.е. метод output в MyClass должен преобразовывать члены-данные в данные вывода, которые дальше отправляются куда надо. Но тогда зачем вся эта возня с наследованием?

Код
#include <stdio.h>
#include <string>
#include <list>
#include <iostream>

namespace Output
{
    class Data
    {
        const std::string &str;
    public:
        Data(const std::string &s) : str(s) {}
        const std::string &GetStr() const { return str; }
    };

    typedef void (*Writer)(const Data &);
    
    void ToScreen(const Data &data)
    {
        printf("%s", data.GetStr().c_str());
    }
    void ToFile(const Data &data)
    {
        FILE *F = fopen("Output.txt", "ab");
        fprintf(F, "%s", data.GetStr().c_str());
        fclose(F);
    }
}

class UserClass
{
    std::list<std::string> ls;
    std::list<int> li;
    int nItems;
public:
    UserClass() { nItems = 0; }
    void AddItem(const std::string &s, int i)
    {
        ls.push_back(s);
        li.push_back(i);
        ++nItems;
    }

    void Write(Output::Writer writer)
    {
        char buf[0x40];
        std::string s;
        
        std::list<std::string>::const_iterator ls_i = ls.begin();
        std::list<int>::const_iterator li_i = li.begin();
        for (int i=0; i<nItems; ++i, ++ls_i, ++li_i)
        {
            s += *ls_i;
            sprintf(buf, " %d\n", *li_i); s += buf;
        }
        writer(Output::Data(s));
    }
};

int main()
{
    UserClass uc;
    uc.AddItem("One", 11);
    uc.AddItem("Two", 22);
    uc.AddItem("Three", 33);

    int OutputKind;
    Output::Writer f;
    std::cin>>OutputKind; std::cout<<'\n';
    if (OutputKind)
        f = &Output::ToScreen;
    else
        f = &Output::ToFile;
    uc.Write(f);

    return 0;
}


Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
Необходимость в доступе к данным == неправильный дизайн. Проектирование + виртуальные функции - вот инструмент решения проблем. Сколько еще раз повторить?

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

Цитата(Fazil6 @  12.7.2006,  12:50 Найти цитируемый пост)
а тут уж что тебя не устраивает?

Забота о времени жизни каких-то левых вспомогательных объектов. 

Автор: Fazil6 13.7.2006, 12:51
UnrealMan, 
Цитата

А если эти данные нужны в обоих классах (BaseClass и DerivedClass) и они используются несколькими общими методами? Предлагаешь продублировать все эти данные и связанные с их обработкой методы?
Код
class Base
{
    Data data;
protected:
    Data &GetData() { return data; }
public:
    ReturnType1 Method1() { /* использует data */ };
    ReturnType2 Method2() { /* использует data */ };
    ....
};

class Derived : public Base
{
    /* ну что, заново объявляем data, заново пишем Method1, Method2 и т.д.? */
    ReturnType3 Method3( /* может использовать data через GetData */ );
};


из этого примера вообще непонятно, зачем нужен наследник. Я вам говорю, что методы get и set, наследование данных как правило свидетельствуют о плохом дизайне. Почти всегда можно сделать по настоящему ОО классы и на самом деле текста будет меньше и модель будет гибче и в сопровождении будет проще. А вы мне что отвечаете? "Нет! Доступ к данным необходим! Без него вот этот пример (здесь идет пример) не работает!" так я повторю - неправильный дизайн. Вот этот твой пример ничего не доказывает. Я вообще из него не вижу никаких резонов иметь родителя и наледника. Включи Method3 в родителя и все. 

Цитата

Эт я просто намекаю на то, что SomeData – это скорее некий универсальный тип, предназначенный для передачи данных в методы вывода, нежели тип поля класса (часто ли нужно выводить именно значение какого-то одного поля?). Т.е. метод output в MyClass должен преобразовывать члены-данные в данные вывода, которые дальше отправляются куда надо. Но тогда зачем вся эта возня с наследованием?

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

Теперь по поводу твоего последнего примера.
Ржунимагу.
Цитата

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

никакого. Ты написал абсолютно тоже самое, что писал я. Я могу конечно начать придираться к реализации (а есть к чему у тебя придираться и даже очень, особенно после Забота о времени жизни каких-то левых вспомогательных объектов. ), но сейчас мы говорим о другом.
есть класс UserClass, у него есть данные. Но ведь у него нет методов get/set и ничего похожего на них. Вывод данных осуществляет сам класс UserClass, он решает что выводить. Где в твоем примере кому-то потребовались данные от UserClass? Все нормально. writer - это таже самая виртуальная функция по сути. Просто виртуальность ее ты сам обеспечиваешь. Ну пожалуста.

А вот это 
Цитата

class Data
    {
        const std::string &str;
    public:
        Data(const std::string &s) : str(s) {}
        const std::string &GetStr() const { return str; }
    };

я вообще не замечаю. Если вокруг переменной нарисовать фигурные скобки и написать слово class, то классом эта конструкция не станет. Это в данном примере не класс, а структура данных, и как я писал выше тут смысл в самих данных и доступ к ним какбы очевидно должен быть, иначе что это за данные, которые я не могу видеть. Это тоже самое, что и SomeData в моем примере и если вас так сильно сбило всех с толку это слово то я уже писал, считайте это int или string или как хотите

 

Автор: UnrealMan 13.7.2006, 14:38
Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
из этого примера вообще непонятно, зачем нужен наследник. 

Ладно, приведу пример поконкретней.

Пусть у нас имеется игра. В ней есть два вида персонажей: игрок (управляется пользователем) и монстр (управляется программно – простеньким искусственным интеллектом). Каждый персонаж обладает следующим набором свойств: положение в пространстве Pos, запас здоровья Health, запас брони Armor. Каждому персонажу можно нанести повреждение (Damage), проверить, не мёртвый ли он (IsDead), а также переместить в пространстве (Move) (каким образом – зависит от вида персонажа).

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

Код
struct Vect3d { double x,y,z; /* далее операции с вектором */ };

class Pawn
{
    Vect3d Pos;
    int Health, Armor;
protected:
    Vect3d &GetPos() { return Pos; }
    int &GetHealth() { return Health; }
    int &GetArmor()  { return Armor; }
public:
    void Damage(int damage)
    {
        int ArmorDam = min(Armor, (damage+1)/2);
        Armor  -= ArmorDam;
        Health -= damage-ArmorDam;
    }
    bool IsDead() { return Health<=0; }
    virtual void Move() = 0; // что бы это ни было, здесь используется Pos
    // ... // ещё какие-то члены
};

class Player : public Pawn
{
    int WeaponAmmo;
public:
    PickUpHealthPack() { GetHealth() = min(100, GetHealth()+20); }
    PickUpArmor()      { GetArmor() = 100; }
    void Move()
    {
        /* перемещаем игрока, используя состояние клавиш клавиатуры */
    }
    // ... // ещё какие-то члены
};

class Monster : public Pawn
{
    int Mode;
public:
    void Move()
    {
        /* перемещаем монстра, используя простой ИИ,
        учитывающий текущие значения Mode, Health и Armor  */
    }
    // ... // ещё какие-то члены
};

Где тут «неправильный» дизайн? И как его сделать «правильным»? Ваш ход, сэр.

Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
Включи Method3 в родителя и все.

Ну-ну, напихать в родителя всё, что только можно... Тогда действительно непонятно, зачем нужно наследование.

Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
У меня для вывода может использоваться одна-единственная (невиртуальная, неперегруженная, никак не переопределенная функция), а результат я буду получать в ней совершенно разный в зависимости от типа аргумента переданного в нее. Вот зачем наследование и полиморфизм.

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

Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
никакого. Ты написал абсолютно тоже самое, что писал я.

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

Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
Я могу конечно начать придираться к реализации (а есть к чему у тебя придираться и даже очень, особенно после Забота о времени жизни каких-то левых вспомогательных объектов. ), но сейчас мы говорим о другом.

Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
writer - это таже самая виртуальная функция по сути.

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

Автор: Fazil6 15.7.2006, 14:25
Цитата

Где тут «неправильный» дизайн? И как его сделать «правильным»?
ты считаешь свои решения безупречными и переубеждать тебя я не собираюсь. В твоем примере реализация монстров и солдат связаны между собой. Мы не можем внести никаких измений в использование этих закрытых переменных для одного класса без учета того как это может отразиться на другом. Многие знают пример с эмулятором полетов австралийских ВВС когда стадо кенгуру начинало перегрупировываться и обстреливать самолеты "стингерами".
 Ладно. Примем твою идею с общностью и необходимостью доступа к этим данным из наследников. Используем защищенный интерфейс. Хорошо. Избранный вариант самый гибкий, самый красивый, самый короткий и самый опасный. Это немногим лучше чем открытые данные. Этот интерфейс будет использоваться именно как доступ к переменным (ты так и пользуешься). Весь код использующий этот интерфейс становится зависимым от его реализации. Опять же, имя метода для установки значения Get - это плохой дизайн.
Если уж делать, то
Код

Vect3d GetPos() const { // бла-бла }
void SetPos(Vect3d) { // бла-бла }
по крайней мере это уменьшит зависимость наследника от родителя. Замечания по поводу эфективности передачи по ссылке здесь не принимаются.
Цитата

Ну-ну, напихать в родителя всё, что только можно... Тогда действительно непонятно, зачем нужно наследование.

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

Цитата

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

Цитата

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

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

Автор: Meeer 15.7.2006, 19:26
Вот почитал-почитал всю эту дискуссию.
Нам Rockie дает вырезку из какой-то книги. Как мы видим Fazil6 также читал (или читает) эту самую книгу, и

Цитата

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


Fazil6
Я тебя понял. Но это в теории так идеально. А на практике довольно таки неудобно (судя из твоего примера там больше путаницы). Тем более всеравно те же самые значения передаются как параметр smile.

"ООП С++", автор Айра Пол, второе издание:
Цитата

...
В С++ новое понятие классов предоставляет механизм инкапсуляции (encapsulation) для реализации АТД (абстрактный тип данных). Инкапсуляция сочетает в себе, с одной стороны, внутренние детали реализации конкретного типа и, с другой, доступные извне операции и функции, которые могут действовать на объекты этого типа. Детали реализации могут быть недоступны для программы, которая использует данный тип. Например, стек может быть реализован как массив фиксированной длинны, а доступные всем операции должны включать в себя функции push(поместить в стек) и pop (извлеч из стека). Изменение внутренней реализации на связной список не должно повлиять на то, как push и pop используются снаружи класса ... Реализация стека скрыта от его клиентов.
...


smile 

Автор: Fazil6 15.7.2006, 19:32
Цитата

 smile 

пример чего? 
Цитата

Я тебя понял. Но это в теории так идеально. А на практике довольно таки неудобно (судя из твоего примера там больше путаницы). Тем более всеравно те же самые значения передаются как параметр

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

Автор: UnrealMan 15.7.2006, 22:45
Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
ты считаешь свои решения безупречными

Уж кто бы говорил... :-‎)

Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
Опять же, имя метода для установки значения Get - это плохой дизайн.
Если уж делать, то

Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
по крайней мере это уменьшит зависимость наследника от родителя. Замечания по поводу эфективности передачи по ссылке здесь не принимаются.

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

Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
совсем не для того, чтобы размазывать реализацию по нескольким классам.

Пока применительно к моей задаче ты не предложил ничего лучшего.

Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
не думаю, что первоочередная задача программиста неиспользование виртуальных функций.

А «не забивать гвозди микроскопом» – это наверняка не первоочередная задача плотника. Тем не менее, разумный плотник скорее всего воспользуется для забивания гвоздей тем инструментом, который для этого предназначен.

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

Цитата(Fazil6 @  13.7.2006,  12:51 Найти цитируемый пост)
writer - это таже самая виртуальная функция по сути. Просто виртуальность ее ты сам обеспечиваешь. Ну пожалуста.

Это как раз ты используешь виртуальность не по назначению, обеспечивая тот же эффект, что и при вызове функций через их явно прописанные в исходниках указатели.

Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
и посмотри на название ветки - это был не пример использования полиморфизма, а пример неиспользования доступа к данным.

С этой точки зрения дела обстоят не намного лучше :-‎) Что показывает твой пример? Не может ли, скажем, оказаться, что иногда нам придётся формировать данные вывода, которые при выводе каким-то способом вообще не будут использоваться (а нам всё равно надо будет по-честному заполнять всю структуру данных вывода – иначе при каком-нибудь другом виде вывода недополучим какую-то информацию)? 

Автор: Fazil6 16.7.2006, 00:44
UnrealMan, 
Цитата

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

плохой дизайн вынуждает идти на компромис. Эфективность и сопровождаемость не взаимоисключающие.

Цитата

Пока применительно к моей задаче ты не предложил ничего лучшего.

не собирался и не собираюсь. Не собираюсь тебе ничего доказывать.
Цитата

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

я и не пытался ничего явно указывать. Наоборот, я полностью скрыл всю реализацию и там, где непосредственно вызывается функция с передачей ей указателя объекта для вывода совершенно необязательно известно куда будет производиться вывод. А писать цепочки вызовов, чтобы продемонстрировать, что в месте вызова
Код

 mcl.output(f);
 совсем не обязательно знать, что такое f в задачи примера не входило. Это абстрактный пример, показать, что нарушение инкапсуляции для обеспечения универсальности не требуется, а не пример использования наследования или какая-то реальная программа. Я уже удивляюсь, что нет возмущения, что этот огрызок кода не компилируется в исполняемую программу.
Цитата

С этой точки зрения дела обстоят не намного лучше :-‎) Что показывает твой пример? Не может ли, скажем, оказаться, что иногда нам придётся формировать данные вывода, которые при выводе каким-то способом вообще не будут использоваться (а нам всё равно надо будет по-честному заполнять всю структуру данных вывода – иначе при каком-нибудь другом виде вывода недополучим какую-то информацию)? 

причем здесь формирование данных для вывода? Где у меня в примере формирование данных? Что вы прицепились к структуре данных? Чем твой пример в этом плане от моего отличается? Разве я как-то поразному использую в MyClass данные для вывода?
Цитата

Что показывает твой пример?

мой пример показывает, что для обеспечения вывода данных класс MyClass должен знать , что есть класс Write для вывода и знать его интерфейс и все. И таких классов как MyClass может быть сколько угодно, и они могут быть никак между собой не связаны. Write о них ничего не знает, а они друг о друге.  
А если подходить с точки зрения когда класс вывода будет запрашивать данные у класса и выводить их, то каждый класс(либо просто функция, как тебе удобнее) вывода куда попало, должен знать все эти классы и их интерфейсы, да еще и функции должны быть перегруженные, если все эти классы выдающие данные не связаны наследованием. Это смысл моего примера. И мне непонятно какого ражна вы цепляетесь к деталям типа, что такое SomeData, откуда это берется и что это такое. Строка это. 
 

Автор: UnrealMan 16.7.2006, 18:45
Цитата(UnrealMan @  15.7.2006,  22:45 Найти цитируемый пост)
Пока применительно к моей задаче ты не предложил ничего лучшего.

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
не собирался и не собираюсь. Не собираюсь тебе ничего доказывать.

Что, при виде нетривиальных задач сразу в кусты? :-‎) Тогда к чему все эти твои пустые разговоры про «плохой дизайн»?

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
я и не пытался ничего явно указывать

А это тогда что?
Цитата
// выводим в файл
mcl.output(f);
// выводим на принтер
mcl.output(p);

Явное указание функции заменили явным указанием объекта, который ассоциирован с нужным классом, который в свою очередь ассоциирован с нужной функцией. Да, необязательно явно указывать требуемый способ вывода именно в месте вызова mcl.output – это можно сделать раньше, и между явно указанными способами можно делать выбор на этапе выполнения программы (см. мой пример с функциями в пространстве имён Output) – и будет тогда твоё

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
там, где непосредственно вызывается функция с передачей ей указателя объекта для вывода совершенно необязательно известно куда будет производиться вывод
(на этапе компиляции).
Но речь-то не идёт о том, где мы размещаем явное указание требуемого способа вывода, – факт в том, что явное указание имеет место.

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
причем здесь формирование данных для вывода? Где у меня в примере формирование данных?

Ну правильно, зачем же тебе рассматривать задачи, где могут возникнуть затруднения, когда можно продемонстрировать решение какой-то тривиальной задачки – дабы подтвердить какие-то твои тезисы? :-‎)

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
Чем твой пример в этом плане от моего отличается?

Мой пример призван:
1) уточнить, чем станет твоё SomeData в случае, когда требуется сформировать данные вывода;
2) показать, что полиморфизм в таком виде тут не нужен.

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
И таких классов как MyClass может быть сколько угодно

Насмешил :-‎) В твоём классе вывод юзает только одно поле данных – d (причём без какой-либо обработки со стороны класса MyClass). Как часто приходится сталкиваться с таким случаем в реальной практике?

Цитата(Fazil6 @  16.7.2006,  00:44 Найти цитируемый пост)
и они могут быть никак между собой не связаны. Write о них ничего не знает, а они друг о друге.

На тебе то же самое:

Код
namespace Output
{
    class ToFile
    {
        // ...
    public:
        // ...
        void Write() { /* ... */ }
    };

    class ToScreen
    {
        // ...
    public:
        // ...
        void Write() { /* ... */ }
    };
}

class UserClass
{
    Data data;
    // ...
public:
    Data Get_data() const { return data; }
    // ...
};

namespace UserClass_
{
    namespace Output
    {
        void ToFile(const UserClass &uc)
        {
            ::Output::ToFile otf;
            // ... // формируем данные вывода и записываем их в otf
            otf.Write();
        }

        void ToScreen(const UserClass &uc)
        {
            ::Output::ToScreen ots;
            // ... // формируем данные вывода и записываем их в ots
            ots.Write();
        }
    }
}

void Func()
{
    UserClass uc;
    // ...
    UserClass_::Output::ToFile(uc);
}

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

Автор: Fazil6 16.7.2006, 20:22
Цитата

А это тогда что?

пример с потолка.
Цитата

Явное указание функции заменили явным указанием объекта, который ассоциирован с нужным классом, который в свою очередь ассоциирован с нужной функцией. Да, необязательно явно указывать требуемый способ вывода именно в месте вызова mcl.output – это можно сделать раньше, и между явно указанными способами можно делать выбор на этапе выполнения программы (см. мой пример с функциями в пространстве имён Output) – и будет тогда твоё

Цитата

(на этапе компиляции).
Но речь-то не идёт о том, где мы размещаем явное указание требуемого способа вывода, – факт в том, что явное указание имеет место.

если ты не понимаешь смысл того, что я сказал - это твоя проблема.
Цитата

Мой пример призван:
1) уточнить, чем станет твоё SomeData в случае, когда требуется сформировать данные вывода;

какое это имеет значение в данном вопросе?

а это вообще бред какой-то 
Вопрос
Цитата

причем здесь формирование данных для вывода? Где у меня в примере формирование данных? Что вы прицепились к структуре данных? Чем твой пример в этом плане от моего отличается? Разве я как-то поразному использую в MyClass данные для вывода?

ответ
Цитата

2) показать, что полиморфизм в таком виде тут не нужен.


Цитата

Насмешил :-‎) В твоём классе вывод юзает только одно поле данных – d (причём без какой-либо обработки со стороны класса MyClass). Как часто приходится сталкиваться с таким случаем в реальной практике?
можно подумать, что если бы я написал там еще одно поле и перед выводом сложил их, то это как-то изменило смысл.

Цитата

На тебе то же самое:

совсем не тоже самое.
Код

void Func()
{
    UserClass uc;
    // ...
    UserClass_::Output::ToFile(uc);

    UserClass1 uc1;    //  у тебя появился еще один класс
                                 //  и как быть? Опять вывод переписывать

}

Цитата

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

интересно какую ерунду ты писал бы, если бы я вместо SomeData написал int...
  

Автор: Meeer 16.7.2006, 20:26
Господа, это лишняя трата времени и нервов. По-моему все равно каждый останется при своем мнении. Прекращайте! smile 

Автор: likehood 16.7.2006, 21:36
Цитата(Fazil6 @  16.7.2006,  01:44 Найти цитируемый пост)
Это смысл моего примера. И мне непонятно какого ражна вы цепляетесь к деталям типа, что такое SomeData, откуда это берется и что это такое. Строка это.

Сейчас объясню, но для этого придется вернуться к началу беседы:
Цитата(ivashkanet @  8.7.2006,  16:44 Найти цитируемый пост)
Представьте ситуацию: 
Мы спроектировали класс, который что-то обрабатывает, а потом выводит на экран (ShowOnScreen()). 
Мы продали этот класс. Его используют другие люди. Всем нравится, все в восторге.
Через некоторое время понадобился вывод на принтер. Они связваются с нами, мы лезем в код добавляем метод (SendToPrinter()). Через время понадобился вывод еще на что-нибудь...
Это выход? Вместо того чтобы открыть результат вычислений, после чего любой сможет написать свой вывод на что угодно.

Здесь имелось ввиду, что спроектированный класс (для определенности назовем его Renderer) сам производит нужные вычисления, сохраняет их результат в своих закрытых полях, и сам же выводит все это на экран. Почему Rendedrer сам делает весь вывод, хотя логичнее было бы поручитьт его другому классу (Writer'у)? Да потому что нам нельзя использовать геттеры, а как иначе Writer получит доступ к закрытым полям класса Renderer (и эти поля отнюдь не сводятся к одному единственному string'у). Ведь нельзя же нарушать инкапсуляцию!
Остается единственный выход: сделать в базовом классе Renderer абстрактную ф-ю write, которую можно переопределить в классе RenderToScreen, при этом данные в Renderer'е придется сделать защищенными (protected), иначе как получить к ним доступ (здесь мы еще не знаем, что protected - это тоже плохой дизайн, Fazil6 сказал об этом лишь несколько десятков постов спустя).
Отсюда и фраза:
Цитата(ivashkanet @  8.7.2006,  18:30 Найти цитируемый пост)
ОК, согласен. Но тогда все наши классы будут неподъемными монстрами, с КУЧЕЙ "лишнего" кода, который будет только заботиться о взаиможействии с остальным миром.

ведь тогда надо будет в базовом классе позаботиться не только о выводе данных, но и обо всем остальном, что можно с этими данными делать.
Только Fazil6 не увидел здесь никаких проблем:
Цитата(Fazil6 @  8.7.2006,  19:21 Найти цитируемый пост)
да???????    нифигасе!!!!!

и в последовавшем примере продемонстрировал всю силу ОО-полиморфизма.
Пример неплохой, вот только куда там делся класс Renderer? Нет, конечно суть примера была продемонстрировать как можно изменить структуру программы (при этом класс Renderer мог вообще исчезнуть), но ведь поведение программы должно остаться тем же: кто-то вычисляет исходные данные, которые где-то храняться, их кто-то потом выводит на экран. Вот только класс Write (да и его потомки) не содержат никаких данных. Значит они берут эти данные из параметра SomeData (а откуда их иначе брать), который в таком случае вполне может играть роль класса Renderer. Но не тут-то было: на вопрос "что это за SomeData такое" последовал ответ:
Цитата(Fazil6 @  12.7.2006,  13:50 Найти цитируемый пост)
никакого значения это не имеет. Это пример и это может быть все, что угодно. Не в этом смысл примера.

Ну все, приехали! Как это не имеет значения! Если цель была продемонстрировать всю силу виртуальных функций, то может и не имеет, а если как избавиться от get/set, то это принципиальный момент. Похоже, Fazil6 пошел по первому пути, отсюда и все дальнейшие непонятки. Людям то охото узнать как Writer берет чужие данные без геттеров, не нарушая при этом инкапсуляцию, а Fazil6 пытается при этом доказать что-то другое. Может то что он говорит и правильно (так оно и есть), но только разговор этот немного "не по теме". Такое впечатление, что методу write все равно откуда приходят данные, а SomeData это просто сокет, из которого идет поток байтов. smile 
Короче говоря, из этого примера я так и не понял как можно обойтись без геттеров в данном случае, но судя по тому, что
Цитата(Fazil6 @  15.7.2006,  20:32 Найти цитируемый пост)
Поверьте, я пишу программы и знаю, что то о чем я говорю, прекрасно без напрягов реализуется на практике.

это все же можно сделать и было бы неплохо это увидеть. 

Автор: Fazil6 16.7.2006, 21:54
Цитата

Людям то охото узнать как Writer берет чужие данные без геттеров, не нарушая при этом инкапсуляцию, а Fazil6 пытается при этом доказать что-то другое.
Writeк ни у кого ничего не берет. Ему данные другие классы сами дают. Если для вас в общем виде не понятно, то конкретизирую.
Например Write в ф-ции write принимает массив строк. Его потомки умеют записать этот массив строк в файл или принтер или на экран или еще куда. Вот класс имеющий данные передает этот массив в функцию write. 
Параметр, передаваемый в write - это не класс, это данные.  

Автор: likehood 16.7.2006, 22:59
Fazil6, вы хотите сказать, что класс MyClass вместо того, чтобы открывать доступ к своим данным сам выбирает, кому он будет передавать эти данные и в каком виде? На счет того, что ф-ии write передаются данные, а не класс, то здесь наверное имеется ввиду, что это может быть и структура если данные довольно сложные, но в данном случае использование структуры с открытыми полями не будет нарушением инкапсуляции (можно было сделать ф-ю с 10-ю параметрами, но структура удобнее)? Класс MyClass отвечает за обработку и хранение данных, а если понадобиться сделать с данными что-то еще кроме вывода, можно просто добавить в MyClass еще один метод (не обязательно виртуальный) и в данном случае это не преведет к большим изменениям в программе? Я правильно понял мысль? 

Автор: skyboy 16.7.2006, 23:45
Цитата(baronp @  16.7.2006,  21:36 Найти цитируемый пост)
 Почему Rendedrer сам делает весь вывод, хотя логичнее было бы поручитьт его другому классу (Writer'у)? Да потому что нам нельзя использовать геттеры, а как иначе Writer получит доступ к закрытым полям класса Renderer (и эти поля отнюдь не сводятся к одному единственному string'у). Ведь нельзя же нарушать инкапсуляцию!

вот именно! только место "нельзя" я бы поставил "нежелательно". Категоричность - плохой друг и подлый враг. А касательно данных... Данные формируются в классе Render. Так? Если выводятся им же(или его потомками), то инкапсуляция ненарушена: логика и формирования изображения(первоначальная функциональность) и вывода его на принтер(добавленный метод) в пределах одного класса. Изменим один метод(его логику, структуру опериуемых данных) изменим и другой. А если будем использовать другой класс для вывода, как нам дать понять, что данные имеют другую структуру/формируются по-другому? Getter будет вместо данных возвращать надпись "проверьте свой класс"? Или писать два разных метода рендеринга(под старую логику и новую) и два новых getter'a с разными именами(под данные старой структуры и новой)? Какой вариант предпочтительнее? Когда класс и его потомки вполне самодостаточны или когда один класс "почти универсален", но универсальность эта - липовая и достигается экстенсивными методами? 

Автор: Fazil6 17.7.2006, 01:31
Цитата

класс MyClass вместо того, чтобы открывать доступ к своим данным сам выбирает, кому он будет передавать эти данные и в каком виде?
вообще-то "куда" он не выбирает. Куда сказали - туда и передает.
Цитата

Я правильно понял мысль?
вобщем да.  

Автор: likehood 17.7.2006, 10:34
Цитата(Fazil6 @  17.7.2006,  02:31 Найти цитируемый пост)
вообще-то "куда" он не выбирает.

"куда" - имеется ввиду методу с определенным прототипом.
Все же если мы пишем класс "на все случаи жизни", то придется предусмотреть множество (в смысле много) операций над данными в самом базовом классе (или если хотите набор прототипов виртуальных ф-ий), что приведет к разрастанию интерфейса класса. Если этот класс библиотечный, предусмотреть все операции над данными будет очень сложно, потому то в GUI классах сложно обойтись без геттеров/сеттеров.


Цитата(skyboy @  17.7.2006,  00:45 Найти цитируемый пост)
 только место "нельзя" я бы поставил "нежелательно".

это было сказано с долей сарказма, категоричность мне вообще не свойственна. 

Автор: UnrealMan 17.7.2006, 10:41
Цитата(Fazil6 @  16.7.2006,  20:22 Найти цитируемый пост)
пример с потолка.

Цитата(Fazil6 @  16.7.2006,  20:22 Найти цитируемый пост)
если ты не понимаешь смысл того, что я сказал - это твоя проблема

Отмазки в рассмотрение не принимаются.

Цитата(UnrealMan @  16.7.2006,  18:45 Найти цитируемый пост)
Мой пример призван:
1) уточнить, чем станет твоё SomeData в случае, когда требуется сформировать данные вывода;

Цитата(Fazil6 @  16.7.2006,  20:22 Найти цитируемый пост)
какое это имеет значение в данном вопросе?

Объясняю на пальцах. Пусть у нас есть классы Class1 и Class2, данные которых нужно выводить. При выводе на экран данных Class1 и Class2 не требуется никаких действий со стороны пользователя, однако при выводе данных Class1 в файл пользователю предлагается в диалоговом окне выбрать путь и имя файла для записи. В свою очередь для вывода в файл данных Class2 от пользователя никаких действий не требуется – объект класса сам предоставляет нужный путь и имя файла. Ну и что ты будешь делать со своим SomeData? А если нам ещё третий класс предстоит разработать – тоже со своими условиями, – что будет тогда? Не такая уж красивая картина получается, правда?

Цитата(Fazil6 @  16.7.2006,  20:22 Найти цитируемый пост)
а это вообще бред какой-то

Это не бред, а напоминание – на всякий случай (только не говори, что у тебя, кроме твоего кода, про ООП больше нигде ничего не упоминалось).

Цитата(Fazil6 @  16.7.2006,  20:22 Найти цитируемый пост)
// у тебя появился еще один класс
// и как быть? Опять вывод переписывать

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

Цитата(Fazil6 @  16.7.2006,  20:22 Найти цитируемый пост)
если бы я вместо SomeData написал int...

Были бы те же самые проблемы + необходимость для каждого типа писать свой метод write (в классе Write и его наследниках) – кстати, только тогда полиморфизм и будет уместен.

Цитата(baronp @  16.7.2006,  21:36 Найти цитируемый пост)
Остается единственный выход: сделать в базовом классе Renderer абстрактную ф-ю write, которую можно переопределить в классе RenderToScreen, при этом данные в Renderer'е придется сделать защищенными (protected), иначе как получить к ним доступ

Что-то я не понял, как это даст получить доступ к этим данным. Приведи пример.

Цитата(Fazil6 @  15.7.2006,  14:25 Найти цитируемый пост)
Ладно. Примем твою идею с общностью и необходимостью доступа к этим данным из наследников. Используем защищенный интерфейс. Хорошо. Избранный вариант самый гибкий, самый красивый, самый короткий и самый опасный. Это немногим лучше чем открытые данные. Этот интерфейс будет использоваться именно как доступ к переменным (ты так и пользуешься). Весь код использующий этот интерфейс становится зависимым от его реализации. 

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

Автор: Meeer 18.7.2006, 00:11
Я всю тему прочитал с начала, и для меня, как наблюдателя (с большей стороны), более убедительная позиция выглядит со стороны "использования get-ов", т.к. получается код гибче. И это неплохая довольно-таки  привычка(на мой взгляд). Правила существуют что бы их нарушать (всем известно). Можно конечно его и придерживаться (правила). Это решать каждому. smile 

Автор: UnrealMan 18.7.2006, 14:01
Цитата(Meeer @  18.7.2006,  00:11 Найти цитируемый пост)
более убедительная позиция выглядит со стороны "использования get-ов", т.к. получается код гибче

Только в отношении свойств, закреплённых за классом условием задачи. Например, возьмём тот же класс строк STL basic_string и посмотрим на одну из реализаций length():

Код
size_type length() const
    {return (_Len); }

По сути length – тот же get (можно было бы назвать этот метод «Get_Len»), получающий жёстко закреплённое за строкой свойство – длину. Никакого нарушения инкапсуляции тут нет.

В остальных же случаях использования get/set следует избегать. 

Автор: maxzone 25.8.2006, 18:30
Очень жалко, что я не успел во время к этой (и более ранней http://forum.vingrad.ru/index.php?showtopic=95670&st=150 ) теме.  smile 
Очень понравилось сравнение себя как класса, стоящего в супермарке в очереди к кассиру:
Не дело это - самому кассиру залазит в мой кошелек и вычитать из того, что он там нашел  smile  сумму за покупки (что-то типа SetCash(GetCash()-5$)).
Он просто должен сказать: "С вас столько-то!" (object.Pay(X dollars))
2-ой вариант, действительно, отражает характер ООП.

А вдруг у меня денег не хватит, тогда (во 2-ом случае, когда данные не выствленны наружу) я могу ну, например, делегировать оплату стоящему с зади меня другу с большим кошельком  smile  или еще что придумать.
Пофантазируем еще, во мне, как в классе, а точнее в моем кошелке, появляется новое поле - КРЕДИТКА! (не являющейся наличкой как бы)

Что мы делаем в 1-ом случае? Правильно, вводим гетеры/сеттеры для нового поля. А кассир что? Правильно. Теперь он лезет ко мне в карман считает деньги, принимает решение, что денег не хватает, смотрит что есть кридитка и т.д. (В прочем, вариант с делегированием другу опять может остаться в стороне)

А во 2-ом случае? Да н_и_ч_е_г_о. Данные скрыты(Свойств, если вам так удобнее, нет). Это мое внутреннее дело, как я буду оплачивать  smile

Конечно, можно сказать, что это пример надуманного ООП, и геттеры/сеттеры здесь притянуты за уши (гм, разве?), но лучше  я посоветую прочитать ответ на поставленный здесь вопрос, почему get/set не есть хорошо в книжке (http://www.amazon.com/gp/product/0321125185/sr=1-1/qid=1156516783/ref=pd_bbs_1/103-5039994-6781457?ie=UTF8&s=books) Она представляет из себя сборник из 100 наиболее распространненых ловушек, куда попадают программисты на с++ (по моему Gotcha N67, но могу и ошибаться).
Не буду гнуть пальцы и кричать, как? вы не знаете кто такой Дьюхарст.. Просто советую. Тем более, что эта книга уже переведена на русский язык...

Автор: akizelokro 30.7.2007, 14:56
Предлагаю посмотреть с другой стороны.
С++ нужен для работы программерского коллектива. Отсюда и большинство требований. Так что не парьтесь. Есть дядька, который будет вам расписывать задание на класс. Ваше дело будет выдать ему код в конце рабочего дня. Дальше код пойдет к другому программисту, который не особо будет вникать в вашу начинку. Он воспользуется вашим классом и его методами. Отсюда и требования, чтобы было поменьше дыр, которые дадут вашему коллеге воспользоваться  незадокументированной вами возможностью и чего-то там навернуть лишка в данных.
Сами вы можете писать на чем угодно. На асме или на сях. В одиночку большую программу не поднять, так что вы можете для себя прописывать даже просто структуры вместо классов. С++ конечно облегчил жизнь в "одиночном программировании", но, иной раз вы залазите в конкретную тину, о которой вам даже думать не надо. И мне тоже.
Кстати, С++ тоже не идеал... как показала жизнь. И даже проблема не в наследстве сей. В один момент процесс пухнет. И где там при наследовании классов лишнее сработает не туда.. Рассказать, как я три дня просто у себя искал тупую ошибку в for() и не мог ее увидеть? Просто не верил, что она может быть именно там.

Автор: ivashkanet 30.7.2007, 15:03
 smile Ааааа некрофилы, спасайся кто может  smile  smile  smile  smile  smile  smile 

P.S. akizelokro, ИМХО, ты прав, но не надо было разжигать спор опять

Автор: Daevaorn 30.7.2007, 16:21
Цитата(akizelokro @  30.7.2007,  15:56 Найти цитируемый пост)
С++ нужен для работы программерского коллектива. Отсюда и большинство требований. Так что не парьтесь. Есть дядька, который будет вам расписывать задание на класс.

к счастью не все работают кодерами, а некоторые всё-таки программисты и и более "думающие спецы", поэтому не всегда кто-то водит тебя за руку. 

Автор: akizelokro 31.7.2007, 08:42
Цитата(Daevaorn @  30.7.2007,  16:21 Найти цитируемый пост)
к счастью не все работают кодерами, а некоторые всё-таки программисты и и более "думающие спецы", поэтому не всегда кто-то водит тебя за руку.  


Тогда ты сам должен понимать, что не всем правилам нужно следовать в 100% случаев. Они, правила, гораздо интереснее своими исключениями. А тогда окажется, что и какой-то get/set будет гораздо более по теме.
А в общем, приведенном в теме смысле, думающие спецы давно бы привели конкретный пример, когда постоянное использование get/set осложнило бы работу при наследовании классов, когда делают дело разные программисты. Эхмы, мой неконкретный опыт мне только подтверждает постоянную сферу действия законов паркинсона и др. и др.
Если полазить раньше по этой теме, то можно было найти вопрос о том, что любой класс нуждается в функции GetOccupied. Это, на мой взгляд, неправильно. Для этого пишется общий предок (либо виртуальный класс, либо класс с виртуальным методом,- четыре года ничего не писал, пошел Страуструпа чтить и искать различия между первоначальным стандартом и практической реализацией стандарта).

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)