| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Как в наследнике спрятать не виртуальный метод |
| Автор: Delphist 1.4.2009, 14:49 | ||||
Есть класс:
Как мне в наследнике этого класса спрятать не виртуальный метод Foo в секцию private?, т.е.
Если написать как я привел во втором коде, Delphi выдает ошибку. |
| Автор: Frees 1.4.2009, 14:54 |
а какую ошибку, у мя скомпилил без звука D2006 |
| Автор: cemick 1.4.2009, 15:06 |
| ИМХО это не возможно |
| Автор: Delphist 1.4.2009, 15:41 | ||
Вот эту: E2065 Unsatisfied forward or external declaration: 'TInheritClass.Foo' Приведи свой пример, ты походу в TInheritClass написал код для Foo:
А я спрашивал как это сделать, без объявления procedure TInheritClass.Foo; |
| Автор: Delphist 1.4.2009, 16:26 |
А почему нельзя то, почему творцы Delphi запретили это? Интересно, а С++ тоже этого не умеет? |
| Автор: cemick 1.4.2009, 16:31 |
| Причем тут творцы, это ООП! |
| Автор: Delphist 1.4.2009, 19:10 |
C++ тоже не умеет? |
| Автор: bems 1.4.2009, 19:16 |
| сделай публичный метод с таким же именем, который при вызове будет давать исключение |
| Автор: Beltar 3.4.2009, 07:15 | ||
Я на Си++ не пишу, но насколько я помню из сравнительных разборов ООП в Си++ и Delphi, Си++ позволяет понижать видимость членам класса. Добавлено через 7 минут и 51 секунду А вообще, я честно говоря, не понял, зачем метод делать public, если в наследнике он уже не нужен и, может быть, даже опасен. |
| Автор: Delphist 3.4.2009, 09:38 | ||
Чтобы дать понять, что класс наследник был предназначен строго для решения определенных задач в нем предоставляется только метод B, а метод Foo специально спрятан, чтобы не было иллюзии, что класс наследник еще выполняет и Foo. |
| Автор: Frees 3.4.2009, 09:45 |
| зачем тогда вообще наследоваться? наследование для расширения функционала а не для его уменьшения! Добавлено через 12 минут и 57 секунд может тебе делать подругому, скрыть в родителе все функции а в потомнке открывать нужные |
| Автор: Alexeis 3.4.2009, 10:19 | ||
В С++ можно сделать Protected наследование.
В делфи можно прятать и раскрывать свойства. |
| Автор: Delphist 3.4.2009, 10:20 | ||
Да все правильно, у меня многие наследники так и делают расширяют ф-цианал предка, но вот один наследник очень специфичен и делает только одну задачу, поэтому у него должен быть один public метод, а остальные в private, чтобы подчеркнуть что этот класс решают только конкретную задачу. Вы скажете "да напиши независимый класс без наследования" я отвечу НЕТ, потому как этот единственный public метод активно использует ядро предка для выполнения задачи которые на него возлагаются. |
| Автор: CodeMonkey 3.4.2009, 10:31 | ||||
И? Проблемы не видно.
И что же там мешает вызвать метод через приведение к предку? P.S. IFWTPICPPTPICPP - "If you want to program in C++, then program in C++". |
| Автор: Alexeis 3.4.2009, 10:36 |
Когда мы так делаем, то сами берем на себя ответственность, после такого компилятор "умывает руки". Изменение области видимости помогает избежать ошибок при работе с текущим классом. Я не слышал такого чтобы в С++ можно было спрятать отдельный метод. |
| Автор: Delphist 3.4.2009, 11:15 | ||
Alexeis, полностью с тобой согласен. _________ И вот все равно не понимаю почему нельзя понизить видимость метода в наследнике. Если кто-то скажет что это ООП, и там все логично, тогда я скажу а разве мой пример не логичен? |
| Автор: Alexeis 3.4.2009, 11:20 |
| Можно сделать через Inherited, но объявить метод как inline, результат будет такой как нужно и синтаксически и бинарно. |
| Автор: CodeMonkey 3.4.2009, 11:49 | ||||
Вообще-то, как правильно уже заметили, это ошибка проектирования. Поэтому такая "фишка" - это костыль, помогающий решать эти проблемы. Может это и отлично ложиться в философию C/C++ ("мы дадим в руки программистам самые мощные инструменты, но они обязуются использовать их грамотно и во благо"), то уж точно не годится в Delphi.
Нет, не логичен. Вы допустили ошибку при создании класса TSomeClass и исправлять надо её, а не городить огород вокруг. |
| Автор: Alexeis 3.4.2009, 12:05 |
| Всего не предусмотришь, а менять реализацию уже работающих классов не лучший вариант. |
| Автор: Delphist 3.4.2009, 17:49 | ||
ну да в смысле сделать inherited |
| Автор: Alexeis 3.4.2009, 20:17 | ||
Ну так как уже говорили с объявлением того же метода в наследнике, т.е. вот так
|
| Автор: Delphist 3.4.2009, 22:22 | ||
Ааа, понял. ну что ж как вариант... |
| Автор: cemick 4.4.2009, 00:41 | ||
Лучший вариант нагородить бредового кода в который без высоких сапог и резиновых перчаток лучше не суваться? |
| Автор: Alexeis 4.4.2009, 09:38 | ||
Голословное и безосновательное утверждение попахивающее наездом. Я лично не видел кода Delphist. Вы его видели? Или судите так категорично обо всем только исходя из того что одна функция оказалась не в той области видимости? |
| Автор: cemick 4.4.2009, 13:40 | ||
Я не именно о варианте Делфиста, а вообще о подходе исправлять ошибки проективарония костылями. Мне кажется это не верный подход, который только запутывает код и многие вещи делает не очевидным. Если метод public то он он паблик, и не надо мучатся почему в n-ом предке куда то пропал этот метод. Наличие таких возможностей чревато перегибами в его использование, по тому мне кажется что лучше, что бы таких возможностей вообще не существовало. ЗЫ извини за наезд |
| Автор: CodeMonkey 5.4.2009, 19:00 |
| Поддерживаю cemick и полностью с ним согласен. Сам хотел такое написать, но опередили. |
| Автор: Alexeis 5.4.2009, 20:01 | ||
Не сомневаюсь. Хаить чужой код все мастера... Есть конкретная жизненная ситуация, для таких ситуаций должно быть эффективное решение, иначе получается что не язык для программиста, а программист для языка. Добавлено через 3 минуты и 9 секунд Точно также как в свое время ввели дополнение класс хелпелпер, позволяющий безопасно расширить существующий класс не меняя код его реализации. Реальность такова что весь софт нуждается в поддержке и доработке, даже после ухода программиста, который его писал. В этом случае безопасность модификаций имеет первостепенное значение. |
| Автор: CodeMonkey 5.4.2009, 23:51 |
| И чем же ушедший на пенсию программист мешает нам сменить видимость метода? |
| Автор: Alexeis 6.4.2009, 02:08 | ||
В теме же написано что от него поражаются и другие наследники, которые используют этот метод, если его скрыть они перестанут работать. Вот смотри есть класс TStream, который умеет читать и писать в абстрактный поток, для него есть TFileStream, который умеет реально читать и писать, но от TStream наследуется также TResourceStream, который писать не может по определению, однако ж методы записи у него унаследованы от TStream. Использовать их будет ошибкой программирования, хотя синтаксически компилятор съест. Борланд решил вопрос сгенерировав ошибку в run-time, это правильно если мы работаем с TResourceStream как с предком т.е. TStream, однако неверно при работе с TResourceStream, поскольку метод есть, компилируется верно, но при работе вызывает ошибку. Идеальным было бы скрыть в TResourceStream методы write, writeBuffer, copyfrom, поскольку они лишь путают, но не несут ни какой пользы. Но это не делается ввиду того что необходимый механизм скрытия методов попросту отсутствует. |
| Автор: CodeMonkey 6.4.2009, 08:41 | ||||||
Ага, значит метод вызывать-таки можно и скрывать его будет неверным решением. В любом случае, никто не запрещает сделать InternalFoo для близко связанных классов.
И это правильное решение. Вот создали мы TResourceStream. Окей, писать мы не можем. А потом мы передали его в процедуру Func1(AStrm: TStream), которая помимо кучи чтений (её обязательная работа) выполняет в самом конце одну запись - также обязательная часть её работы. И? Поток спокойно проглотит запись, т.к. теперь там не стоит возбуждение исключения. И это безопасно? Ладно, пусть там будет какое-то автогенерируемое исключение наподобие Abstract Error. Тогда всё равно получается, что от ситуации с возникновением исключения в run-time вы никуда не уйдёте, как было - так и осталось. Помимо путаницы (когда у нас метод невидим, когда он возбуждает исключение и т.п.) мы получаем лишние усилия разработчиков Delphi, которые можно было бы пустить в другое место. Далее, пусть в классе вы запретили вызов метода предка, а у его потомка кто-то его снова включил. В какую-то процедуру передаётся класс с "погашенным методом". Вы передаёте туда класс-потомок, у которого метод повторно включён. Хотя метод есть, но вызвать вы его не можете. Опять какая-то нелогичность и путаница. Замечу, что ситуация в целом несколько напоминает механизм абстрактных методов. Компилятор ведь не запрещает вам вызывать абстракный метод, уповая на то, что вы в run-time передадите экземпляр, где он реализован. Также и здесь. Уж если вы сделали метод public - вот любой его и сможет вызвать. А если вы его по какой-то причине не поддерживаете - ну так возбудите исключение. Это единообразно согласуется с общим поведением в Delphi. А вот это "скрытие методов" - IFWTPICPPTPICPP. Вообще в C(++) понадобавляли много чего из серии "ааа... ну как же без этого, а ВДРУГ кому-то это понадобиться???". Из-за этого язык стал одним из самых мощных, гибких, но в то же время сложным и небезопасным. Дай людям в руки возможность и они обязательно воспользуются ей неправильно. Например, там есть возможность создать класс со статическим (не виртуальным) деструктором. И что? Допускаю, что когда-то кому-то такая возможность однажды пригодилась, но противовесом стали бесчисленные множества кривых реализаций со статическим деструктором вместо виртуального. В интернете куча статей и постов в блогах для таких вот горе-разработчиков: "почему деструктор ВСЕГДА должен быть виртуальным". А вот в Delphi он виртуален по-умолчанию. И проблем нет. А если он кому-то понадобиться - это не проблема как-нибудь сэмулировать. Так же и эта вот возможность скрытия методов. Да, польза от неё может быть, но вреда намного больше. P.S. Да, и это не единственный пример с исключениями в run-time, вот ещё, к примеру:
Этот пример ловит ситуации, когда в тип переменной SomeVar мы добавили несколько новых констант, а в каком-то case в коде забыли их учесть. |
| Автор: Alexeis 6.4.2009, 09:39 | ||||
Одно другому не мешает. Я точно не помню иерархию, но допустим, что TResourceStream наследуется не напрямую от TStream, а от промежуточного TReadOnlyStream, который генерит общее для всех потомков исключение WriteError. Теперь у TResourceStream есть необходимое поведение, но есть и лишние методы в секции public. Класс TResourceStream последний в цепочке, поэтому все лишние методы следует скрыть, так как же как скрываются свойства не имеющие смысла в контексте текущего класса. Такая фича не может быть потенциально опасной, потому что ее цель увеличить степень безопасности.
Так в том то и прикол что и в С++ нет такого механизма... |
| Автор: CodeMonkey 6.4.2009, 10:12 | ||||
Ну не знаю, я в сях вообще 0
Я там не говорил о конкретно TResourceStream, но если брать пример с ним, то те мои слова остаются в силе, если предположить, что мы создаём наследника от TResourceStream у которого работают write-методы.
Анти-пример (когда "такая фича" опасна, причём не только потенциально) я уже привёл ;) А второй аргумент против (тоже уже сказал) - это "маленькое улучшение" даётся за цену увеличения количества криворуких реализаций, в которых скрытие методов будет использоваться не по назначению. Именно поэтому моё мнение таково, что хак (исправление кривости дерева наследования) должен быть хаком, а не легализоваться на уровне языка. |
| Автор: Delphist 6.4.2009, 13:28 | ||
Молодец Alexeis, удачный пример привел. |
| Автор: mes 6.4.2009, 14:28 | ||
в cpp нельзя изменить область видимости метода в наследнике, я правильно понял цитату ? а как же это :
? |
| Автор: cemick 6.4.2009, 14:58 |
| Я думаю всем понятно, чем проще код, тем он более понятен и легко модифицируем и это может является показателям профессионализма. Увеличение энтропии всегда не хорошо,а добавление механизма сокрытия статических паблик методов добавлять много неоднозначностей. Часто говорится что C++ мощный язык, но даже например перегрузка операторов собирает много критики. Вспоминается поговорка "заставь дурака богу молится дак он и лоб расшибет". Если вы не правильно спроектировали иерархию классов, можно воспользоваться средствами рефакторинга или можно натворить такого, что последующая модификация и развития проекта станет задачей архи сложной и не посильной, когда переписать все заново будет намного проще, чем что то исправить. Заметьте множественно наследование считается злом, вы предлагаете что то похожее. ООП родилось не в мгновение ока, и подменять принципы ООП не есть гуд. |
| Автор: mes 6.4.2009, 15:06 | ||||||
ага, дельфи в этом плане безопаснее, чем cpp. имхо правильней(более применима к обсуждаемому контексту ) эта поговорка звучала бы при замене слова "заставь" на "позволь"
Ага, если метод все равно можно вызвать приведя к базовому классу, то смысла от прятанья его в наследнике в приват не много. Поэтому лучше, если возможно, поступать обратно: наследоваться приватно, а нужные методы перенести в public зону.
Согласен, но не всегда возможно (по каким либо причинам) довести иерархию до идеала . |
| Автор: Alexeis 6.4.2009, 15:25 | ||||
Да ну, значит инкапсуляция теперь уже не принцип ООП? Объект предоставляет наружу, только то что допустимо к использованию, то что считает нужным и ничего лишнего. Текущий вариант как раз является нарушением принципа инкапсуляции. Не надо думать что реализация ООП в Delphi это на 100% соответствие основным принципам. Это лишь жалкое подобие. Теоретическая концепция намного глубже. Добавлено через 2 минуты и 16 секунд
Почему же, внутри функции можно сгенерировать исключение, а при прямом использовании будет ошибка этапа компиляции, что гораздо полезнее ошибки в рантайме. |
| Автор: mes 6.4.2009, 15:37 | ||||
да, но "не много" звучало в контексте того, что есть вариант получше :
|
| Автор: cemick 6.4.2009, 16:46 | ||||
Я этого и не говорил. Но если уже заговорили про это, то думаю лучше посмотреть как это реализовано в JAVA и С#.. Добавлено @ 16:52
пословицу смотреть выше Если например у нас иерархия состоит из 20 классов, то вы допускаете возможность в каждом классе менять область видимости как угодно? Или если была в одном классе видимость изменена с public на private, то в его наследниках он уже будет не видим? и вообще бред какой то, хочешь менять область видимости делай virtual |
| Автор: mes 6.4.2009, 17:31 | ||
да ?! |
| Автор: cemick 6.4.2009, 20:14 | ||
|
| Автор: Alexeis 6.4.2009, 22:36 |
| cemick, только при этом нужно в наследнике вызывать метод предка. Тоже самое делается с любым методом, виртуальность тут ни при чем. |
| Автор: mes 7.4.2009, 01:16 |
| cemick, нет слов .. |
| Автор: cemick 7.4.2009, 08:33 | ||
Да, он с "любым методом" очень не красиво так делать. |
| Автор: Alexeis 7.4.2009, 09:34 | ||
Не согласен. Суть виртуальности чтобы указатель на предка вызывал метод того класса который создан, а не то чтобы наследник вызывал метод предка. Ни какой связи.
Невнимательно читаете, я говорил финальных классах. При правильном проектировании известно какие классы являются промежуточными, какие конечными. Недавно даже специальную директиву ввели final, для того чтобы пометить финальный класс. При попытке наследоваться от такого класса будет синтаксическая ошибка. В таком классе нужно прятать все лишнее. Если посмотреть в VCL перед финальным классом всегда есть промежуточный класс, который реализует 90-100% функционала финального класса (TCustomForm, TCustomEdit и т.д.), вот у него много чего открыто, а вот в финальном TForm, TEdit основные изменение это публикования одних свойств убирание других. Хотя от TForm, TEdit и можно наследоваться, но это весьма неудобно, поэтому для наследования смело берем TCustomForm, TCustomEdit. |
| Автор: cemick 7.4.2009, 10:37 |
| ладно, уговорили ыз хотя уверен что у бывших борладовских инженеров были причины сделать так, как есть сейчас. |
| Автор: RinOSpro 7.4.2009, 15:36 |
| Можно пометить в наследнике процедуру как deprecated конечто это не скроет ее, но будет высвечиватся красивым warning-ом |
| Автор: Delphist 8.4.2009, 09:26 | ||
Вообще пришлось в наследнике просто вызывать предка:
|