![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| Fazil6 |
|
||||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
пример чего?
странные вы какие-то люди. Вы, наверное, думаете, что я понятия не имею, что такое на практике ? Поверьте, я пишу программы и знаю, что то о чем я говорю, прекрасно без напрягов реализуется на практике. Особенно начинаешь это вспоминать при сопровождении (особенно чужого кода), при работе над проектом в команде Это сообщение отредактировал(а) Fazil6 - 15.7.2006, 20:07 |
||||
|
|||||
| UnrealMan |
|
||||||||||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Уж кто бы говорил... :-)
Вообще-то выбор между эффективностью+удобством и сопровождаемостью решается не в общем виде, а отдельно для каждого конкретного случая. Я допускаю оба способа.
Пока применительно к моей задаче ты не предложил ничего лучшего.
А «не забивать гвозди микроскопом» – это наверняка не первоочередная задача плотника. Тем не менее, разумный плотник скорее всего воспользуется для забивания гвоздей тем инструментом, который для этого предназначен. В данном случае для вызова нужной (конкретной) функции посредством явного её указания уже существует механизм – вызов через указатели на функции. Виртуальные же функции тем и хороши, что избавляют от необходимости указывать конкретную фунцию явно (и именно в этом их предназначение).
Это как раз ты используешь виртуальность не по назначению, обеспечивая тот же эффект, что и при вызове функций через их явно прописанные в исходниках указатели.
С этой точки зрения дела обстоят не намного лучше :-) Что показывает твой пример? Не может ли, скажем, оказаться, что иногда нам придётся формировать данные вывода, которые при выводе каким-то способом вообще не будут использоваться (а нам всё равно надо будет по-честному заполнять всю структуру данных вывода – иначе при каком-нибудь другом виде вывода недополучим какую-то информацию)? |
||||||||||||
|
|||||||||||||
| Fazil6 |
|
||||||||||||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
UnrealMan,
плохой дизайн вынуждает идти на компромис. Эфективность и сопровождаемость не взаимоисключающие.
не собирался и не собираюсь. Не собираюсь тебе ничего доказывать.
я и не пытался ничего явно указывать. Наоборот, я полностью скрыл всю реализацию и там, где непосредственно вызывается функция с передачей ей указателя объекта для вывода совершенно необязательно известно куда будет производиться вывод. А писать цепочки вызовов, чтобы продемонстрировать, что в месте вызова
причем здесь формирование данных для вывода? Где у меня в примере формирование данных? Что вы прицепились к структуре данных? Чем твой пример в этом плане от моего отличается? Разве я как-то поразному использую в MyClass данные для вывода?
мой пример показывает, что для обеспечения вывода данных класс MyClass должен знать , что есть класс Write для вывода и знать его интерфейс и все. И таких классов как MyClass может быть сколько угодно, и они могут быть никак между собой не связаны. Write о них ничего не знает, а они друг о друге. А если подходить с точки зрения когда класс вывода будет запрашивать данные у класса и выводить их, то каждый класс(либо просто функция, как тебе удобнее) вывода куда попало, должен знать все эти классы и их интерфейсы, да еще и функции должны быть перегруженные, если все эти классы выдающие данные не связаны наследованием. Это смысл моего примера. И мне непонятно какого ражна вы цепляетесь к деталям типа, что такое SomeData, откуда это берется и что это такое. Строка это. |
||||||||||||
|
|||||||||||||
| UnrealMan |
|
||||||||||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Что, при виде нетривиальных задач сразу в кусты? :-) Тогда к чему все эти твои пустые разговоры про «плохой дизайн»? А это тогда что?
Явное указание функции заменили явным указанием объекта, который ассоциирован с нужным классом, который в свою очередь ассоциирован с нужной функцией. Да, необязательно явно указывать требуемый способ вывода именно в месте вызова mcl.output – это можно сделать раньше, и между явно указанными способами можно делать выбор на этапе выполнения программы (см. мой пример с функциями в пространстве имён Output) – и будет тогда твоё
Но речь-то не идёт о том, где мы размещаем явное указание требуемого способа вывода, – факт в том, что явное указание имеет место.
Ну правильно, зачем же тебе рассматривать задачи, где могут возникнуть затруднения, когда можно продемонстрировать решение какой-то тривиальной задачки – дабы подтвердить какие-то твои тезисы? :-) Мой пример призван: 1) уточнить, чем станет твоё SomeData в случае, когда требуется сформировать данные вывода; 2) показать, что полиморфизм в таком виде тут не нужен. Насмешил :-) В твоём классе вывод юзает только одно поле данных – d (причём без какой-либо обработки со стороны класса MyClass). Как часто приходится сталкиваться с таким случаем в реальной практике?
На тебе то же самое:
Только теперь я смогу для каждого типа вывода сформировать свои данные (необходимые и понятные именно для него), а не отправлять каждый раз полный джентльменский набор на все случаи жизни. Это сообщение отредактировал(а) UnrealMan - 16.7.2006, 18:55 |
||||||||||||
|
|||||||||||||
| Fazil6 |
|
||||||||||||||||||||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
пример с потолка.
если ты не понимаешь смысл того, что я сказал - это твоя проблема.
какое это имеет значение в данном вопросе? а это вообще бред какой-то Вопрос
ответ
совсем не тоже самое.
интересно какую ерунду ты писал бы, если бы я вместо SomeData написал int... |
||||||||||||||||||||
|
|||||||||||||||||||||
| Meeer |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 20 Регистрация: 3.6.2006 Где: Ukraine Репутация: нет Всего: нет |
Господа, это лишняя трата времени и нервов. По-моему все равно каждый останется при своем мнении. Прекращайте!
|
|||
|
||||
| likehood |
|
||||||||
|
666 ![]() ![]() Профиль Группа: Участник Сообщений: 536 Регистрация: 21.12.2005 Репутация: 8 Всего: 24 |
Сейчас объясню, но для этого придется вернуться к началу беседы: Здесь имелось ввиду, что спроектированный класс (для определенности назовем его Renderer) сам производит нужные вычисления, сохраняет их результат в своих закрытых полях, и сам же выводит все это на экран. Почему Rendedrer сам делает весь вывод, хотя логичнее было бы поручитьт его другому классу (Writer'у)? Да потому что нам нельзя использовать геттеры, а как иначе Writer получит доступ к закрытым полям класса Renderer (и эти поля отнюдь не сводятся к одному единственному string'у). Ведь нельзя же нарушать инкапсуляцию! Остается единственный выход: сделать в базовом классе Renderer абстрактную ф-ю write, которую можно переопределить в классе RenderToScreen, при этом данные в Renderer'е придется сделать защищенными (protected), иначе как получить к ним доступ (здесь мы еще не знаем, что protected - это тоже плохой дизайн, Fazil6 сказал об этом лишь несколько десятков постов спустя). Отсюда и фраза:
ведь тогда надо будет в базовом классе позаботиться не только о выводе данных, но и обо всем остальном, что можно с этими данными делать. Только Fazil6 не увидел здесь никаких проблем: и в последовавшем примере продемонстрировал всю силу ОО-полиморфизма. Пример неплохой, вот только куда там делся класс Renderer? Нет, конечно суть примера была продемонстрировать как можно изменить структуру программы (при этом класс Renderer мог вообще исчезнуть), но ведь поведение программы должно остаться тем же: кто-то вычисляет исходные данные, которые где-то храняться, их кто-то потом выводит на экран. Вот только класс Write (да и его потомки) не содержат никаких данных. Значит они берут эти данные из параметра SomeData (а откуда их иначе брать), который в таком случае вполне может играть роль класса Renderer. Но не тут-то было: на вопрос "что это за SomeData такое" последовал ответ:
Ну все, приехали! Как это не имеет значения! Если цель была продемонстрировать всю силу виртуальных функций, то может и не имеет, а если как избавиться от get/set, то это принципиальный момент. Похоже, Fazil6 пошел по первому пути, отсюда и все дальнейшие непонятки. Людям то охото узнать как Writer берет чужие данные без геттеров, не нарушая при этом инкапсуляцию, а Fazil6 пытается при этом доказать что-то другое. Может то что он говорит и правильно (так оно и есть), но только разговор этот немного "не по теме". Такое впечатление, что методу write все равно откуда приходят данные, а SomeData это просто сокет, из которого идет поток байтов. Короче говоря, из этого примера я так и не понял как можно обойтись без геттеров в данном случае, но судя по тому, что
это все же можно сделать и было бы неплохо это увидеть. |
||||||||
|
|||||||||
| Fazil6 |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
Например Write в ф-ции write принимает массив строк. Его потомки умеют записать этот массив строк в файл или принтер или на экран или еще куда. Вот класс имеющий данные передает этот массив в функцию write. Параметр, передаваемый в write - это не класс, это данные. |
|||
|
||||
| likehood |
|
|||
|
666 ![]() ![]() Профиль Группа: Участник Сообщений: 536 Регистрация: 21.12.2005 Репутация: 8 Всего: 24 |
Fazil6, вы хотите сказать, что класс MyClass вместо того, чтобы открывать доступ к своим данным сам выбирает, кому он будет передавать эти данные и в каком виде? На счет того, что ф-ии write передаются данные, а не класс, то здесь наверное имеется ввиду, что это может быть и структура если данные довольно сложные, но в данном случае использование структуры с открытыми полями не будет нарушением инкапсуляции (можно было сделать ф-ю с 10-ю параметрами, но структура удобнее)? Класс MyClass отвечает за обработку и хранение данных, а если понадобиться сделать с данными что-то еще кроме вывода, можно просто добавить в MyClass еще один метод (не обязательно виртуальный) и в данном случае это не преведет к большим изменениям в программе? Я правильно понял мысль?
|
|||
|
||||
| skyboy |
|
|||
|
неОпытный ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 9820 Регистрация: 18.5.2006 Где: Днепропетровск Репутация: 1 Всего: 260 |
вот именно! только место "нельзя" я бы поставил "нежелательно". Категоричность - плохой друг и подлый враг. А касательно данных... Данные формируются в классе Render. Так? Если выводятся им же(или его потомками), то инкапсуляция ненарушена: логика и формирования изображения(первоначальная функциональность) и вывода его на принтер(добавленный метод) в пределах одного класса. Изменим один метод(его логику, структуру опериуемых данных) изменим и другой. А если будем использовать другой класс для вывода, как нам дать понять, что данные имеют другую структуру/формируются по-другому? Getter будет вместо данных возвращать надпись "проверьте свой класс"? Или писать два разных метода рендеринга(под старую логику и новую) и два новых getter'a с разными именами(под данные старой структуры и новой)? Какой вариант предпочтительнее? Когда класс и его потомки вполне самодостаточны или когда один класс "почти универсален", но универсальность эта - липовая и достигается экстенсивными методами? |
|||
|
||||
| Fazil6 |
|
||||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1653 Регистрация: 3.5.2006 Где: Минск Репутация: 35 Всего: 60 |
Это сообщение отредактировал(а) Fazil6 - 17.7.2006, 01:32 |
||||
|
|||||
| likehood |
|
|||
|
666 ![]() ![]() Профиль Группа: Участник Сообщений: 536 Регистрация: 21.12.2005 Репутация: 8 Всего: 24 |
"куда" - имеется ввиду методу с определенным прототипом. Все же если мы пишем класс "на все случаи жизни", то придется предусмотреть множество (в смысле много) операций над данными в самом базовом классе (или если хотите набор прототипов виртуальных ф-ий), что приведет к разрастанию интерфейса класса. Если этот класс библиотечный, предусмотреть все операции над данными будет очень сложно, потому то в GUI классах сложно обойтись без геттеров/сеттеров. это было сказано с долей сарказма, категоричность мне вообще не свойственна. |
|||
|
||||
| UnrealMan |
|
||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Отмазки в рассмотрение не принимаются.
Объясняю на пальцах. Пусть у нас есть классы Class1 и Class2, данные которых нужно выводить. При выводе на экран данных Class1 и Class2 не требуется никаких действий со стороны пользователя, однако при выводе данных Class1 в файл пользователю предлагается в диалоговом окне выбрать путь и имя файла для записи. В свою очередь для вывода в файл данных Class2 от пользователя никаких действий не требуется – объект класса сам предоставляет нужный путь и имя файла. Ну и что ты будешь делать со своим SomeData? А если нам ещё третий класс предстоит разработать – тоже со своими условиями, – что будет тогда? Не такая уж красивая картина получается, правда? Это не бред, а напоминание – на всякий случай (только не говори, что у тебя, кроме твоего кода, про ООП больше нигде ничего не упоминалось).
Ту часть, которая передаёт нужную информацию в объекты классов из ::Output, – да; классы для вывода в ::Output – нет. Только давай уточним насчёт «переписывать»: если у нас идёт обработка данных перед выводом, то нам всё равно придётся где-то её размещать – внутри класса или вне (например, внутри пространства имён UserClass1_::Output). Вообще же класс лучше было бы назвать LibraryClass (LibraryClass1) – дабы подчеркнуть, что каких-либо изменений в него мы уже внести не сумеем – придётся или довольствоваться тем, что есть, или же переписывать весь класс заново (и, возможно, менять все старые объявления объектов). Были бы те же самые проблемы + необходимость для каждого типа писать свой метод write (в классе Write и его наследниках) – кстати, только тогда полиморфизм и будет уместен. Что-то я не понял, как это даст получить доступ к этим данным. Приведи пример. Зависимость от реализации тут ни при чём. За классом изначально могут быть закреплены нужные (по условию задачи, которую решает данный класс) неизменные свойства, которые вполне логично задавать в виде полей. Можно было бы считать такой класс структурой данных в стиле C, но тогда определение структуры данных в стиле C пришлось бы сильно притягивать за уши, ибо внутреннее устройство этого класса может быть столь же сложным, как и в любом другом классе, где все члены-данные не являются некими хранилищами свойств, диктуемых в отношении класса условием задачи. Это сообщение отредактировал(а) UnrealMan - 17.7.2006, 12:46 |
||||
|
|||||
| Meeer |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 20 Регистрация: 3.6.2006 Где: Ukraine Репутация: нет Всего: нет |
Я всю тему прочитал с начала, и для меня, как наблюдателя (с большей стороны), более убедительная позиция выглядит со стороны "использования get-ов", т.к. получается код гибче. И это неплохая довольно-таки привычка(на мой взгляд). Правила существуют что бы их нарушать (всем известно). Можно конечно его и придерживаться (правила). Это решать каждому.
|
|||
|
||||
| UnrealMan |
|
||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 27 Всего: 32 |
Только в отношении свойств, закреплённых за классом условием задачи. Например, возьмём тот же класс строк STL basic_string и посмотрим на одну из реализаций length():
По сути length – тот же get (можно было бы назвать этот метод «Get_Len»), получающий жёстко закреплённое за строкой свойство – длину. Никакого нарушения инкапсуляции тут нет. В остальных же случаях использования get/set следует избегать. |
||||
|
|||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |