MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« : 17-05-2005 13:24 » |
|
Задам немного ламерский вопрос : Объясните мне суть применения аксессоров и мутаторов (get и set) в я зыке C# ? Я понимаю синтаксис написания, также понимаю как можно это применить в коде, но я не совсем понимаю для чего это использовать Объясните мне плизз. Также не совсем понимаю назначение интерфейсов(interface), если кому не лень, разъясните пожалуйста наглядно
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
Alf
Гость
|
|
« Ответ #1 : 17-05-2005 13:42 » |
|
С помощью акцессоров и мутаторов возможно реализовывать так называемые свойства объектов, которые довольно похожи на свойства объектов COM. В мутатор, например, можно встроить код для проверки целостности объекта. Например, если у тебя объект хранит информацию о человеке, ты можешь сделать его возраст свойством вроде: private date m_BirthDate; ... public date BirthDate { get { return m_BirthDate; } set { if ((value > today()) // дата рождения больше текущей || ((today() - value) > 150)) // столько не живут throw new Bad_Age(value); else m_BirthDate = value; } }
В этом фрагменте при задании возраста ты гарантируешь, что он более-менее правдоподобен. Можно также добавить свойство, которое динамически вычисляет возраст: public int Age { get { return (int)(today() - m_BirthDate); } }
Само собой, в последнем случае можно с равным успехом применить функцию-член вместо свойства; в Java, где свойства отсутствуют, это единственный способ, в C# можешь выбирать тот или иной стиль. P.S. Тип data и функция today(), разумеется, фиктивные, взяты только для краткости примера; в реальном коде следует заменить их реальными.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #2 : 17-05-2005 13:44 » |
|
По поводу интерфейсов попробую рассказать вечером, если успею купить сегодня монитор взамен почившего вчера в бозе старого...
|
|
|
Записан
|
|
|
|
xelos
Гость
|
|
« Ответ #3 : 17-05-2005 13:53 » |
|
get и set - imho, в основном введены для удобства пользования объектами. Кстати, по-русски, вроде, свойство называется. через свойства, ты можешь изменять состояние объекта. Разница с членом при вызове свойства и члена-функции - в синтаксисе (при обращении к свойству не указываешь скобки). Функциональность та же самая, как если бы ты изменял состояние объекта через функцию. Только вместо 2-х функций SetValue и GetValue, ты пользуешься опертором присваивания напрямую.
интерфейсы широко пользуются при наследовании. интерфейс - это контракт на реализацию какой-то функции (если не ошибаюсь, интерфейс соответствует чисто виртуальной функции в С++ - без базового определения). Т.е. в классе, объявляешь интерфейс и все его потомки обязяны его реализовать, иначе не скомпилится код. Есть важное свойство у интерфейсов. в С# не поддерживается множественное наследование, если это не интерфейсы. Т.е. множествнное наследование от нескольких интерфейсов разрешено.
если надо примеры - говори какие именно.
|
|
|
Записан
|
|
|
|
xelos
Гость
|
|
« Ответ #4 : 17-05-2005 13:55 » |
|
о, Alf, уже дал примеры для get и set
|
|
|
Записан
|
|
|
|
npak
|
|
« Ответ #5 : 17-05-2005 13:59 » |
|
МОРОЗ, в большинстве распространённых языков программирования присваивание поля объекта сводится к прямой записи результата в память. Чтение поля объека эквивалентно чтению из памяти объекта, и программист-разработчик класса не может этим управлять. Поэтому уже давно пришли к выводу, что предоставлять прямой доступ к полям объека для чтения/присваивания нельзя. О некоторых обоснованиях см. https://forum.shelek.ru/index.php/topic,6303.0.htmlАльтернатнива доступу к полям -- предоставление в интерфейсе get-методов для чтения состояния объекта и set-методов для изменения состояния. Скажем, для поля name типа String создать метод String getName() и метод void setName(String). Практика (прежде всего, язык Java) показала, что при этом получаются очень длинные строки или надо пользоваться временными переменными: TopManager.getTopManager().getResourcePool().getResource(IconID).setSize(Icon.SMALL) Благодаря тому, что вызовы get и set методов автоматически вызываются вместо чтения поля или присвоения поля, можно написать что-то вроде (смесь С# и Java) TopManager.topManager.resourcePool.resource[IconID].size = Icon.SMALL И писанины меньше, и нагляднее.
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #6 : 17-05-2005 18:55 » |
|
Ну начёт аксессоров и мутаторов всё более менее понятно. Попрактикуюсь в написании чтобы понять полностью. Теперь жду про интерфейы
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
Alf
Гость
|
|
« Ответ #7 : 17-05-2005 20:54 » |
|
Все, траблы с монитором успешно разрулены, продолжаю рассказ про интерфейсы. Теорию разводить не буду, ты ее и сам наверняка начитался, раз успел запутаться. Расскажу историю из реального проекта. Некая программа может работать как в диалоге, так и в пакетном режиме. Если командная строка пустая, она выводит юзеру диалоговое окно, в котором он может задать настройки (сервер базы данных и учетную запись, имя лог-файла и кучу других). Если же командная строка непуста, программа парсит ее и пытается извлечь значения параметров из опций (этот режим используется при запуске из планировщика, когда оператора нет в принципе). Я создал класс CProgramSettings, который инкапсулировал все возможные параметры. Основную программу не волнует, откуда они взялись - из диалога или командной строки. Если ей, к примеру, нужно выяснить имя лог-файла, она может найти его в соответствующем классе. Однако хотелось обезопасить параметры от случайной порчи во время выполнения программы, когда они не должны меняться в принципе. Когда ваяешь программу в спешке, вполне можно ошибиться и перепутать переменные, а потом ищи их... Я поступил следующим образом. 1. Создал интерфейс для чтения параметров настройки: class IGetProgramSettings { public: virtual CString LogFileSpec(void) = 0; // имя лог-файла virtual bool DumpFlag(void) = 0; // флаг: выводить файл дампа ... // и так далее };
2. Создал аналогичный интерфейс для записи: class ISetProgramSettings { public: virtual void SetLogFileSpec(const CString &lfs) = 0; virtual void SetDumpFlag(const bool df) = 0; ... // и так далее };
Все члены интерфейсных классов являются чисто виртуальными функциями. 3. Создал собственно класс, реализующий эти интерфейсы: class CProgramSettings: public IGetProgramSettings, public ISetProgramSettings { public: // Реализация IGetProgramSettings virtual CString LogFileSpec(void); virtual bool DumpFlag(void);
// Реализация ISetProgramSettings virtual void SetLogFileSpec(const CString &lfs); virtual void SetDumpFlag(const bool df); ... // и так далее
private: CString m_LogFileSpec; bool m_DumpFlag; ... };
CString CProgramSettings::LogFileSpec(void) { return m_LogFileSpec; }
void CProgramSettings::SetLogFileSpec(const CString &lfs) { m_LogFileSpec = lfs; }
...
4. Теперь я могу создать переменную типа CProgramSettings: CProgramSettings ps;
Например, программа разбора командной строки должна записывать результат в ps, поэтому она получает ее как ISetProgramSettings: void ParseCommandString(ISetProgramSettings *sps {...} ... ParseCommandString(&ps); // разобрать командную строку и записать параметры в ps
А вот основная программа-обработчик должна лишь считывать параметры, поэтому она получает лишь указатель на IGetProgramSettings: void Process(IGetProgramSettings *gps) {...} ... Process(&ps); // выполнить обсчет данных
Надеюсь, основная идея понятна, хоть я и использовал C++ для иллюстрации. Для C# картина будет полностью аналогичная. В данном случае один объект имеет два интерфейса для разных нужд. В каждом контексте используется наиболее подходящий. Например, функция Process в принципе не может изменить программные настройки, поскольку получает лишь интерфейс, способный считывать данные. Пост ополучился объемистый, не обессудь. Я и так постарался оставить лишь необходимое для понимания сути. Набирал прямо в форуме, компилировать не пробовал. Поэтому не обессудь, если впопыхах где ошибся.
|
|
|
Записан
|
|
|
|
xelos
Гость
|
|
« Ответ #8 : 17-05-2005 21:19 » |
|
Alf, а насколько обосновано в данном случае применение интерфейсов? имхо, если хотелось только защитить класс от записи в отдельно взятой функции, не самое оптимальное решение.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #9 : 17-05-2005 22:21 » |
|
Цель применения интерфейсов я описал выше - объект имеет несколько ипостасей и предоставляет клиенту лишь тот сервис, который ему необходим. Так совпало, что в данном примере один из них в качестве побочного действия предоставляет защиту от записи.
Основная идея - отнюдь не в том, чтобы защищаться от записи подобным образом. Суть совсем в другом: клиент не видит истинного типа объекта, а получает необходимый сервис лишь через указатель на интерфейс. Если по какой-то причине мне понадобится полностью заменить реализацию, клиента даже не придется перекомпилировать - пока класс-сервер реализует неизменный интерфейс, клиент не заметит подмены (кстати, в этом же и состоит основная идея COM). Таким образом, применение интерфейсов ослабляет взаимозависимость между классами и позволяет строить логику программы более четко.
Кстати (это уже к Морозу), помнится, недавно в нашей беседе промелькнули паттерны проектирования. Так вот, эти самые паттерны базируются в основном на интерфейсах.
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #10 : 18-05-2005 04:35 » |
|
Кстати (это уже к Морозу), помнится, недавно в нашей беседе промелькнули паттерны проектирования. Так вот, эти самые паттерны базируются в основном на интерфейсах. Отсюда и возник мой вопрос про интерфейсы Потому что разных определений и теории я прочёл много, но вот понимать начинаю нормально только тогда, когда начинаю это использовать Спасибо за подробные разъяснения. Попробую втянуться в это дело
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
Alf
Гость
|
|
« Ответ #11 : 18-05-2005 08:25 » |
|
Лучше всего ты это сможешь оценить, если работаешь над проектом не в одиночку.
Представь себе, что мы пишем программу из моего примера вдвоем. Ты пишешь ее основную часть, а мне достались диалоги с пользователем и прочие вспомогательные вещи.
Первым делом мы с тобой заключаем контракт, какой сервис тебе нужен от моего модуля. К примеру, ты говоришь: "Мне необходимо знать имя файла для обработки. По ходу обработки я буду вести лог в файле, который задает пользователь. По желанию оператора программа может выдавать данные в дамп и/или базу данных, поэтому мне понадобятся соответствующие флаги..." и так далее. Мы приходим к соглашению, что тебе нужна от меня строка имя-входного-файла, строка имя-лог-файла, булевские переменные флаг-дампа и флаг-базы- данных и так далее.
В результате появляется класс, состоящий из чисто виртуальных функций, каждая из которых обеспечивает какую-то часть нужного для работы твоей части сервиса. Это и есть интерфейс. Если я каким-то образом передам тебе указатель на этот интерфейс, ты сможешь вызвать любую его функцию, и тебя совершенно не волнует, что на самом деле объект типа интерфейса не может быть создан.
Со своей стороны, когда я займусь реализацией интерфейса, я должен буду создать класс, производный от этого интерфейса. При этом я обязан реализовать все функции интерфейса. Если я забуду хотя бы про одну, созданный мной класс останется абстрактным, и нельзя будет создать его экземпляр. Реализация интерфейса от тебя полностью скрыта. Ты не знаешь, например. храню ли я параметры в реестре или в INI-файле, получаю их значения из диалога или в командной строке и т.д. Главное, чтобы соблюдались условия контракта, закрепленные в спецификации интерфейса. Это позволяет достичь большей степени абстракции, а следовательно, независимости частей программы друг от друга. Что бы я ни делал в своей части, твоя не потребует даже перекомпиляции, пока интерфейс остается неизменным.
Конечно, такая стратегия годится не только для разработки "всем колхозом". Ей можно пользоваться и самостоятельно, разбивая программы на относительно независимые части и строя взаимодействие между ними исключительно посредством интерфейсов.
Если хочешь, можешь представить части программы отделенными друг от друга широкой пропастью. Тогда интерфейс - это мостик, соединяющий два ее края. Контролировать этот мостик гораздо проще, чем большой участок границы, через который любой может шмыгать когда захочет.
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #12 : 18-05-2005 09:12 » |
|
Понятно теперь Щас как раз заканчиваю с абстрактными классами и возьмусь за интерфейсы
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
xelos
Гость
|
|
« Ответ #13 : 18-05-2005 09:57 » |
|
Если хочешь, можешь представить части программы отделенными друг от друга широкой пропастью. Тогда интерфейс - это мостик, соединяющий два ее края. Контролировать этот мостик гораздо проще, чем большой участок границы, через который любой может шмыгать когда захочет.
за что люблю посты альфа, так это за доходчивость объяснения с интересными аналогиями
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #14 : 18-05-2005 11:13 » |
|
Во, с абстрактными классами разобрался Ща попробую подобное с интерфейсами
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
Alf
Гость
|
|
« Ответ #15 : 18-05-2005 11:34 » |
|
Мороз, а чем сия картина маслом написана? Каким инструментом?
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #16 : 18-05-2005 12:00 » |
|
Alf - это стандартный тулз в студии 2005 - называется "View Class Diagramm" и атачится к прожекту как файл с расширением cd
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
Alf
Гость
|
|
« Ответ #17 : 18-05-2005 12:18 » |
|
С виду похоже на диаграмму классов UML, с двумя "но".
Во-первых, по стилю. Если рисуешь диаграмму классов, общепринято рисовать иерархию в виде дерева, когда производные классы изображаются ниже базовых. Может показаться мелочной придиркой, но когда объектная структура достаточно сложная, аккуратная диаграмма читается гораздо легче, чем нарисованная как попало.
Во-вторых, у тебя действительно класс Program является производным от DrawingEngine, или это просто стрелки Generalization и Uses в этом инструменте выглядят настолько похожими?
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #18 : 18-05-2005 13:28 » |
|
у тебя действительно класс Program является производным от DrawingEngine, или это просто стрелки Generalization и Uses в этом инструменте выглядят настолько похожими? Да, действительно класс Program является производным от DrawingEngine (но это не обязательно) С интерфейсами тоже всё уже более менее ясно. Спасибо за помощь!
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
Alf
Гость
|
|
« Ответ #19 : 18-05-2005 13:43 » |
|
Я, собственно, почему спросил-то...
Обычно если класс B является производным от класса A, это подразумевает, что B является некоей разновидностью A. Например, Круг является частным случаем Фигуры, ЛегковойАвтомобиль - частным случаем ТранспортногоСредства и т.д.
А вот является ли Программа разновидностью ГрафическогоДвижка? Мне такое наследование представляется сомнительным. Тут как раз тот случай, когда агрегация гораздо уместнее наследования. Разумеется, это IMHO; вполне возможно, что у тебя были веские причины именно для такого решения,которые я не уловил поверхностным взглядом.
|
|
|
Записан
|
|
|
|
MOPO3
Ай да дэдушка! Вах...
Команда клуба
Offline
Пол:
Холадна аднака!
|
|
« Ответ #20 : 18-05-2005 13:53 » |
|
А вот является ли Программа разновидностью ГрафическогоДвижка? Мне такое наследование представляется сомнительным. Тут как раз тот случай, когда агрегация гораздо уместнее наследования. Разумеется, это IMHO; вполне возможно, что у тебя были веские причины именно для такого решения,которые я не уловил поверхностным взглядом.
Да похоже я просто ступил здесь Конечно не надо наследоваться от графического движка, всё и так работает Спасибо ещё раз
|
|
|
Записан
|
MCP, MCAD, MCTS:Win, MCTS:Web
|
|
|
nikedeforest
|
|
« Ответ #21 : 05-07-2005 17:58 » |
|
Простите дурака, но хотелось бы уточнить правильно-ли я понял. Как я понял, интерфейсом являются методы классов (или сам класс), которые позволяют получить значение определенной переменной или изменить значение опред. переменной через данные методы. Я прав? Если да, то это по сути является аналогом property в Дельфи, так ли это? После ответа на этот пост, скорее всего появятся новые вопросы, извините уж.
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #22 : 05-07-2005 20:09 » |
|
Не совсем правильно. То, о чем ты говоришь, называется свойствами и присутствует в C# и Delphi в явном виде, а в C++ и Java эмулируется через пару методов аксессор-мутатор. Это наиболее корректный с точки зрения ООП подход к реализации класса, когда переменные состояния объекта скрыты от непосредственного обращения и доступны лишь косвенно через вызовы соответствующих методов.
Интерфейс - это совокупность описаний методов (и свойств в случае, если язык позволяет их использовать). При этом реализация этих методов определяется полностью автором класса, реализующего данный интерфейс. Другими словами, интерфейс - одна из абстракций, на которых базируется ООП. Понятие интерфейса встроено в C# и Java, в С++ их приходится эмулировать чистыми виртуальными классами. Насчет Delphi не помню, пусть знатоки подскажут, как там дело с интерфейсами обстоит.
Особенностью интерфейса является то, что нельзя создать объект типа интерфейса. Обязательно должен быть класс, реализующий интерфейс. Зато ссылки на интерфейс - вполне обычное явление в практике программирование. Разумеется, на самом деле они указывают на один из объектов, реализующих интерфейс, но за счет полиморфизма программа работает корректно.
Ну и в заключение - не ставь знак тождества между интерфейсом и классом. Один и тот же интерфейс могут реализовывать множество классов, а один класс в свою очередь может реализовывать множество интерфейсов.
|
|
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #23 : 06-07-2005 12:45 » |
|
Думаю что понял, только не знаю на 100% процентов, так ли я понял. Наверное виднее будет когда с этим столкнешься.
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #24 : 06-07-2005 12:54 » |
|
Если хочешь аналогию из мира железа, то представь себе интерфейс как съемную панель автомагнитолы. Сама по себе панель играть не будет, ты должен прицепить ее на место. Зато когда она стоит на месте, она определяет внешний вид магнитолы, без нее там глянуть не на что.
Если еще предположить, что эта панель подходила бы к нескольким разным моделям магнитол, аналогия была бы полной. Все они для потребителя выглядели бы одинаково, и он не искал бы, какой кнопкой переключить радиостанцию, а какой убавить громкость. Хотя при этом у каждой магнитолы своя начинка, от дешевой мыльницы до супераппарата, и одна может кое-как дребезжать, а другая давать чистый звук. Но главное - одинаковое управление одинаковыми функциями, которые реализованы внутри совсем по-разному. Это и есть - один интерфейс для нескольких классов.
|
|
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #25 : 06-07-2005 18:50 » |
|
Alf, спасибо.
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
|