Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.156] |
|
|
Правила раздела:
| Страницы: (495) « Первая ... 309 310 [311] 312 313 ... 494 495 ( Перейти к последнему сообщению ) |
Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
|
Сообщ.
#4651
,
|
|
|
|
Цитата Qraizer @ Но если придёт начальник и скажет, что заказчик передумал, и в TBC нужно разделить общий интерфейс IA так, чтобы IB и IC могли использовать разные реализации, то телодвижений для редизайна придётся сделать больше, нежели убрать virtual из базы. Редизайнить ничего не надо, клиентов это даже не коснётся. ![]() ![]() IA = Interface procedure Foo; end; IB = Interface(IA) //***// end; IC = Interface(IA) //***// end; TBC1 = class(TInterfacedObject, IB, IC) private procedure Foo; end; TBC2 = class(TInterfacedObject, IB, IC) private procedure IBFoo; procedure ICFoo; procedure IB.Foo = IBFoo; procedure IC.Foo = ICFoo; end; Если кто забыл, TBC1 и TBC2 реализуют только интерфейсы IB и IC, указанные явно в декларации, но никак не интерфейс IA, несмотря даже на то, что он является их базовым. Так вот, интересно, а что будет на абстрактных классах, если мы попытаемся получить IA из "разбитой" TBC2? Цитата scorpion @ DesweR, я на своей практике, как то не припомню задач, где бы мне потребовалось запрашивать интерфейс, неизвестный на этапе компиляции, у объекта, тоже неизвестного на этапе компиляции.... И главное зачем? Самое элементарное - фабрика. ![]() ![]() type TFactory = class private ... public procedure Reg<T>; function Get<T>: IInterface; end; var Factory: TFactory; A: IA; B: IB; begin Factory.Reg<TA>; Factory.Reg<TB>; A := Factory.Get<IA>; B := Factory.Get<IB>; end; Добавлено Цитата DesweR @ Если кто забыл, TBC1 и TBC2 реализуют только интерфейсы IB и IC, указанные явно в декларации, но никак не интерфейс IA, несмотря даже на то, что он является их базовым. C# может получить IA из TBC1, но не сможет разбить реализацию IB и IC в TBC2. |
|
Сообщ.
#4652
,
|
|
|
|
DesweR, и это называется "не надо редизайнить"? Как интересно. Это даже не дерево, это уже граф больше напоминает.
Цитата DesweR @ Кстати, всё хочу спросить и всё забываю. Как в Дельфи дженерики реаизованы? Неужто байткодом? Самое элементарное - фабрика. |
|
Сообщ.
#4653
,
|
|
|
|
Цитата Qraizer @ DesweR, и это называется "не надо редизайнить"? А где ты видишь редизайн Цитата Qraizer @ Кстати, всё хочу спросить и всё забываю. Как в Дельфи дженерики реаизованы? Неужто байткодом? А что? (если что, соглашусь псевдокод фабрики не совсем удачный) http://www.tdelphiblog.com/2009/10/c-c-delphiwin32.html |
|
Сообщ.
#4654
,
|
|
|
|
Цитата Qraizer @ Пока продолжу. Есть логгер ILog, который реализован в TA и TB. Оба логируют (или логгируют, как правильно?) свои действия. Наследования простые! Предположим, реализация ILog, используемая TA и TB, пишет в файл. Вопрос: файлов логов дожно быть два или один? Цитата Qraizer @ Ну и собственно, почему я решил что "гвоздь". Потому что от начала и до сих пор Дельфи за программиста решает, что интерфейсы всегда совмещаются, реализации всегда разделяются. И если для интерфейсов у них ещё есть отмазка, мол, интерфейсы иначе не могут, ибо тогда они и не интерфейсы вовсе, а абстрактные классы (хотя по мне это не решение, а слив, из-за терминологической подоплёки отказаться решать практическую задачу - это моветон), то в случае реализаций и такого нет. В случае необходимости иметь различные реализации одного и того же интерфейса в одном компоненте приходится перепроектировать. Хотя наверняка это даже не замечается, а просто сразу на автомате делается по-другому, потому что простое плюсовое разделение в голову просто не приходит в связи с отсутствием поддержки в языке. В случае совмещения (возможно, частичной) реалиазций на агрегированные сущности навешивается инвариант и программируется руками. Тоже на автомате простое плюсовое совмещение ... в общем по той же причине. Я просто думал, мож есть какие тайные фишки. Когда речь зашла о пропертях, я ж сразу вспомнил о всяких там контейнерах типа TPanel, подумал, может там аттрибуты координат как-то связываются на языковом уровне. Тут всё не так просто. И надо глубоко забираться в концепции. Что у нас на входе? На входе есть некий интерфейс (пусть будет IStack), и есть его реализации. На диаграммах классов всё может выглядеть очень запутанно, но в рантайме, когда всё это "взетает", клиент интерфейса всегда работает с какой-то конкретной (финальной) реализацией интерфейса. Вот тут и начинается веселье. С точки зрения клиента всё это выглядит как? Он получает нечто (некоторый объект), и кастует его к некоторому интерфейсу (тому же IStack). Если каст проходит, то объект поддерживает этот интерфейс (читать это нужно так: "объект позволяет с собой работать в соответствии с контрактом, определенным заданным интерфейсом"). Как - клиента мало интересует. Когда есть один объект и один интерфейс, то всё просто. Когда объект поддерживает несколько интерфейсов, ситуация усложняется. Если все эти интерфейсы реализуются непосредственно финальным классом, то всё более-менее нормально. Если же автор объекта воспользовался идеей наследования реализаций, и часть видимых интерфейсов объекта реализуются на одном уровне иерархии, другая часть - на другом, возникает риск, что переход клиента от одного интерфейса к другому будет неинвариантен. Примерно так: ![]() ![]() class Base1 : IDisposable { }; class Base2 : Base1, ISerializable { }; class Derived : Base2, IDisposable { }; Да. Тут тот самый ромб. Предположим, клиент, получив указатель на объект класса Derived, делает такие переходы: object (Derived*) -> IDisposable (Derived*) -> ISerializable (Base2) -> IDisposable (куда?) -> ??? Но с точки зрения логики очевидно, что во всех случаях клиент работает с финальным объектом. Он ничего не знает о его структуре. А потому переходы между интерфейсами должны быть инвариантны. Именно по этому, какая бы структура наследования не была у финального объекта, считается, что каждый интерфейс он реализует один единственный раз (в противном случае клиент очень быстро получает вынос мозга). Такая схема реализована в COM. Такая схема реализована в Delphi, С#, Java. Такая схема максимально точно реализует концепцию интерфейса как "поведенческий контракт". Именно "контракт", как "обещание вести себя строго определённым образом", и ничто другое. Добавлено Собственно, в отрыве от термина "контракт" обсуждения интерфейсов имеет мало смысла. Добавлено Вот интересно! На duck typing весь статический полиморфизм в плюсах работает. Собственно, а что ещё это может означать? Есть контракт поведенческой сущности "Стек", определяемой четырьмя операциями: "push", "pop", "top" и "isEmpty". Если два класса реализуют четыре этих метода, значит?.. Правильно - оба этих класса обладают поведением, соответствующим контракту "Стек". Если этот контракт зафиксирован в виде интерфейса, то совсем хорошо. |
|
Сообщ.
#4655
,
|
|
|
|
Цитата Qraizer @ Кстати, всё хочу спросить и всё забываю. Как в Дельфи дженерики реаизованы? Неужто байткодом? Так же как и везде, компилируются. Иначе они не нужны, есть RTTI. Цитата Qraizer @ Коли речь идёт об интерфейсах, наверное, нет. Но если придёт начальник и скажет, что заказчик передумал, и в TBC нужно разделить общий интерфейс IA так, чтобы IB и IC могли использовать разные реализации, то телодвижений для редизайна придётся сделать больше, нежели убрать virtual из базы. Зато заказчик будет будет сиять от радости, если в соответствии с его пожеланиями теперь один лог валится в виндовый журнал, а второй - в файл в %APPDATA%. Конечно я утрирую. Утрируешь. На интерфейсах давно подобное делалось, подключаемые реализации. И обычно такое пишется сразу, если есть намек на изменения. Собственно, еще в начале века написали подключение для картридеров, любых. Там даже перекомпилировать не надо было. |
|
Сообщ.
#4656
,
|
|
|
|
Цитата Flex Ferrum @ Есть контракт поведенческой сущности "Стек", определяемой четырьмя операциями: "push", "pop", "top" и "isEmpty". Если два класса реализуют четыре этих метода, значит?.. Правильно - оба этих класса обладают поведением, соответствующим контракту "Стек" Нет, неправильно. Это означает, что совпали сигнатуры методов. Никаких "поведенческих" особенностей я из названий не вижу. Али вы уже научились семантику прямо в ЯП описывать? |
|
Сообщ.
#4657
,
|
|
|
|
Цитата MyNameIsIgor @ Нет, неправильно. Это означает, что совпали сигнатуры методов. Никаких "поведенческих" особенностей я из названий не вижу. Али вы уже научились семантику прямо в ЯП описывать? С точки зрения duck typing это именно так. |
|
Сообщ.
#4658
,
|
|
|
|
Цитата Flex Ferrum @ С точки зрения duck typing это именно так. Я в курсе. Только тут обсуждается применимость уток для интерфейсов. Если решим, что применимо - тогда да, интерфейс "стек" мы реализовали. По вашим же рассуждениям "мы реализовали - значит, применимо". |
|
Сообщ.
#4659
,
|
|
|
|
Цитата MyNameIsIgor @ Нет, неправильно. Это означает, что совпали сигнатуры методов. Никаких "поведенческих" особенностей я из названий не вижу. А шаблоны ты использовал? А концепты считаешь полезными? В чем принципиальная разница в данном случае? |
|
Сообщ.
#4660
,
|
|
|
|
Цитата D_KEY @ А шаблоны ты использовал? А концепты считаешь полезными? Да, да. Цитата D_KEY @ В чем принципиальная разница в данном случае? Чтобы задать такой вопрос, надо дождаться от меня фразы об идеальности поведения шаблонов в данном случае. Т.е. из фразы Цитата Flex Ferrum @ На duck typing весь статический полиморфизм в плюсах работает я делаю только один вывод - на duck typing весь статический полиморфизм в плюсах работает А вовсе не "так надо сделать всегда". Сколько там увещевали auto_ptr в контейнеры не засовывать? И только с введением rvalue reference появился смарт, который действительно туда не засунешь. Ну, не нравится мне, что вышеописанный стек от очереди (из stl) отделяет зыбкая разница в названии методов: top - front. |
|
Сообщ.
#4661
,
|
|
|
|
Цитата MyNameIsIgor @ надо дождаться от меня фразы об идеальности поведения шаблонов в данном случае. А как бы ты хотел? |
|
Сообщ.
#4662
,
|
|
|
|
Цитата D_KEY @ А как бы ты хотел? Чёрт возьми, хороший вопрос! Я не знаю... |
|
Сообщ.
#4663
,
|
|
|
|
Цитата MyNameIsIgor @ Цитата D_KEY @ А как бы ты хотел? Чёрт возьми, хороший вопрос! Я не знаю... Ну, например, есть вариант, что для того, чтобы можно было что-либо делать с объектом, нужно обязательно указать интерфейс типа-параметра, а если интерфейс не указан, то никаких операций с типом и объектами типа делать будет нельзя. Однако и это согласуется с duck-typing интерфейсов во время выполнения. |
|
Сообщ.
#4664
,
|
|
|
|
Цитата MyNameIsIgor @ Только тут обсуждается применимость уток для интерфейсов. Если решим, что применимо - тогда да, интерфейс "стек" мы реализовали. По вашим же рассуждениям "мы реализовали - значит, применимо". Тут всё зависит от того, что считать интерфейсом. Я интерфейсом считаю фиксацию некоторого поведение. Контракт между классом и его клиентами. Поведение - это набор методов. Следовательно, если два класса предоставляют клиенту один и тот же набор методов, значит? Да. Значит, они обладают одинаковым интерфейсом. Если это семантически разные методы, об этом должно быть явным образом сообщено клиенту. |
|
Сообщ.
#4665
,
|
|
|
|
Цитата Flex Ferrum @ Если это семантически разные методы, об этом должно быть явным образом сообщено клиенту. Только вот как? |