"Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 28.09.04 10:59
Оценка: +1 :))
"Включение" vs "Наследование" интерфейсов

Хотелось бы узнать кто что думает по поводу замены механизма наследования интерфейсов на механизм включения. Что такое наследование интерфейсов, думаю, всем понятно. Например, если
IA = INTERFACE
  PROCEDURE f();
  PROCEDURE g();
END;
IB = INTERFACE (IA)
  PROCEDURE h();
  PROCEDURE k();
END;

то, интерфейс IB является потомком от IA и, стало быть, имеет все процедуры объявленные в IA. Переменную типа IB можно использовать вместо переменной типа IA. Другими словами, переменная типа IA совместима по присваиванию с переменной типа IB (обратное не верно). Теперь рассмотрим случай когда IA и IB не связаны друг с другом наследованием:
IA = INTERFACE
  PROCEDURE f();
  PROCEDURE g();
END;
IB = INTERFACE
  PROCEDURE f();
  PROCEDURE g();
  PROCEDURE h();
  PROCEDURE k();
END;

Хотя IA и IB не связаны друг с другом наследованием, но интерфейс IB включает в себя все процедуры объявленные в интерфейсе IA. Теоретически, любая переменная типа IB потенциально может быть использована вместо переменной типа IA (компилятор, правда, с этим будет не согласен, но это его проблемы). Будем говорить, что один (включающий) интерфейс включает в себя другой (включаемый) интерфейс, если он включает в себя все процедуры объявленные во включаемом интерфейсе. Сразу же ответим на вопрос сколько более мелких интерфейсов включает в себя один большой интерфейс имеющий N-штук процедур. Очевидно, количество включамых интерфейсов равно:

g(N) = N + N*(N-1)/2! + N*(N-1)*(N-2)/3! + ... = 2^N — 2

Например, для N = 3 имеем g(3) = 2^3 — 2 = 6, то есть
Iabc = INTERFACE
   PROCEDURE a();
   PROCEDURE b();
   PROCEDURE c();
  END;

автоматически включает в себя 6 интерфейсов:

Iab = INTERFACE
   PROCEDURE a();
   PROCEDURE b();
  END;
Iac = INTERFACE
   PROCEDURE a();
   PROCEDURE c();
  END;
Ibc = INTERFACE
   PROCEDURE b();
   PROCEDURE c();
  END;
Ia = INTERFACE
   PROCEDURE a();
  END;
Ib = INTERFACE
   PROCEDURE b();
  END;
Ic = INTERFACE
   PROCEDURE c();
  END;

Это, в некотором смысле, аналогично включению типов (type inclusion)

REAL >= SHORTREAL >= LONGINT >= INTEGER >= SHORTINT >= BYTE

Количество включаемых интерфейсов экспоненциально растет (2^N-2) по мере увеличения количества (N) процедур в интерфейсе. Интерфейс состоящий из N=10 процедур, включает в себя 2^10 — 2 = 1022 более мелких интерфейсов. Интерпретация этого явления очень проста. Вот, например, сколькими интерфейсами обладает калькулятор? Ни в жизнь не сосчитаете. Ведь калькулятором можно еще и гвозди забивать! Когда программист проектирует класс объекта, он определяет какие интерфейсы объекты этого класса будут поддерживать
TMyCalculator = CLASS (TInterfacedObject, ICalculate, IStorable, IPaintable, ...)
  //...
END;

Все интерфейсы поддерживаемые объектами этого класса жестко фиксируются в момент написания класса. Но дело в том, что если интерфейс содержит хотябы с десяток процедур, то разработчику будет не под силу указать даже малую часть всех тех 2^10 — 2 = 1022 возможных вариантов использования объектов этого класса. Программист просто утомиться их перечислять, не говоря о том чтобы еще и каждому из этих подинтерфейсов дать свое уникальное имя (и GUID...). Всвязи с этим, возникает вопрос: А не стоит ли для интерфейсов ввести такое понятие как ВКЛЮЧЕНИЕ ИНТЕРФЕЙСОВ и, соответственно, отказаться от наследования интерфейсов, поскольку включение — есть более мощный механизм нежели механизм наследования?

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

С уважением,
Сергей Губанов
Re: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 28.09.04 12:18
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>"Включение" vs "Наследование" интерфейсов


Разрешите сперва скорректировать термины.
"Включение" — агрегация (мне так удобнее).
"Наследование" — статическое наследование (именно о нём вы говорите в своём посте).
Наследование — механизма разделения ресурсов между объектами связанными отношением потомок-предок.

Итак. Агрегация и статическое наследование могут выступать в качестве различных реализаций механизма наследования в ООП, как способа разделения ресурсов между объектами связанными отношением потомок-предок. На этом сходство исчерпывается и идут отличия.
Статическое наследование по-сути есть статическое копирование реализации предка в реализацию клиента, выполняемое на этапе компиляции. На базе языковой поддержки оно присуще языкам с классической реализацией объектно-ориентированного подхода. Другой отличительной чертой этих языков (как правило), тесно связанной со статическим наследование, является статическая типизация (исключение составляет, разве что, Smalltalk).
Агрегация + делегирование — это более продвинутая реализация механизма разделения ресурсов между предком и потомком. На уровне языковой поддержки она реализована, например, в языке Self.
Агрегация и делегирования — механизмы периоды исполнения (динамический подход). Статическое наследование — механизм периода компиляции (статический подход).
Очень часто статического наследования для реализации полноценной объектно-ориентированной архитектуры в таких языках как C++, Pascal не хватает, поэтому на программном уровне оно дополняется агрегацией с делегированием (добрая половина паттернов банды четырёх решает именно такие задачи). Но смешивать эти подходы на языковом уровне не имеет смысла. Почему — чистая философия без какой либо конкретики, основанная на том принципе, что статическая типизация призвана облегчить процесс программирования и поиска ошибок, добавление агрегации — механизма, динамического по сути, может извести на нет все благие начинания.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 28.09.04 12:42
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Здравствуйте, S.Yu.Gubanov, Вы писали:


SYG>>"Включение" vs "Наследование" интерфейсов


MN>Разрешите сперва скорректировать термины.

MN>"Включение" — агрегация (мне так удобнее).
MN>"Наследование" — статическое наследование (именно о нём вы говорите в своём посте).
MN>Наследование — механизма разделения ресурсов между объектами связанными отношением потомок-предок.

Боюсь, что Вы это о чем-то о своем...

Множество {1,2,3,4} включает в себя подмножество {2,3}, но оно не агрегирует его. Агрегация — это когда автомобиль агрегирует в себе свой двигатель, сиденья, бензобак и колеса.

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

Агрегация на языке интерфейсов выражается совсем по другому, вот так

Ia = INTERFACE
  PROCEDURE f();
  PROCEDURE g();
END;

Ib = INTERFACE
  FUNCTION  GetA(): Ia; // вот она - агрегация какая
  PROCEDURE h();
  PROCEDURE k();
END;

VAR
  b: Ib;
BEGIN

  //...

  b.GetA.f();
Re: "Включение" vs "Наследование" интерфейсов
От: Quintanar Россия  
Дата: 28.09.04 13:32
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

Если я правильно понял, что вы имеете ввиду, то именно такой способ работы с объектами принят в OCaml. Там в функцию можно передать любой класс, в котором есть нужные методы с фиксированными типами параметров и именами. Т.е. в
f x = x.method 1 "1"

Можно передать любой объект с методом method типа int, string -> во что-то
Re: "Включение" vs "Наследование" интерфейсов
От: _Obelisk_ Россия http://www.ibm.com
Дата: 28.09.04 14:35
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>"Включение" vs "Наследование" интерфейсов


По этому пути уже пошла OMG в стандарте UML 2.0. Интерфейсы функционируют так, как вы описали и при этом еще поддерживается их наследование. Так что одно другому не противоречит.



Душа обязана трудиться! (с) Н.Заболоцкий.
Re: "Включение" vs "Наследование" интерфейсов
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 28.09.04 15:22
Оценка: +1
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Практически, это будет выражено в том, что когда разработчик будет писать код класса, то ему не надо будет явно перечислять все интерфейсы поддерживаемые объектами этого класса.


Идея интересная, но к сожалению, не совсем согласен.

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


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

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

А что до адаптирования, это относительно несложно решить шаблонами (если речь — о C++) или runtime-связыванием.

PS. Говорил же, меньше читайте адептов ООП.
... << RSDN@Home 1.1.3 stable >>
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[3]: "Включение" vs "Наследование" интерфейсов
От: Larm Украина  
Дата: 28.09.04 16:29
Оценка: +2
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Боюсь, что Вы это о чем-то о своем...


SYG>Множество {1,2,3,4} включает в себя подмножество {2,3}, но оно не агрегирует его. Агрегация — это когда автомобиль агрегирует в себе свой двигатель, сиденья, бензобак и колеса.


Читал и смеялся. Давайте обозначим 1=двигатель, 2=сиденья, 3=мотор... Дальше продолжать? . А вообще по изначальной теме топика — а какому это маньяку понадобятся ВСЕ возможные комбинации методов? Если уж ему нужно 1023 интерфейса, то пусть с ними возится. ИМХО так. Давайте приводить примеры из жизни. То, что послужило началом топика никакого отношения к реальности не имеет. Учим матчасть и проектирование.

P.S. Ничего личного, просто вопрос был дурацкий. ТАК никто не делает...
The God who walks is among us...
Re: "Включение" vs "Наследование" интерфейсов
От: prVovik Россия  
Дата: 28.09.04 22:05
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>"Включение" vs "Наследование" интерфейсов

SYG>...

В С++ эта идея уже реализована и давно используется. Техника ее применения называется "обобщенное программирование". Вводится понятие "концепции", которое можно трактовать, как некоторый запрашиваемый интерфейс, который не объявляется в классах явно. Или другими словами: концепция — это множество классов, удовлетворяющих некоторым требованиям. Таким образом, новая концепция появляется тогда, когда появляются новый набор требований, и при этом некоторые старые классы могут автоматически подпадать под новую концепцию. Также вводится понятие обобщенного алгоритма, основное отличие которого от обычного заключается в том, что обычный алгоритм работает на уровне классов, а обобщенный — на уровне концепций.

Между концепциями может возникать отношение "развития", когда одна концепция развивает другую. Например, концепция "итератор произвольного доступа" развивает концепцию "однонаправленный итератор". Таким образом, получаются иерархии концепций, наподобии иерархий классов. Но важно то, что между наследованием классов и развитием концепций не ставится никакого "versus". Одно другого дополняет.
... << RSDN@Home 1.1.4 @@subversion >>
лэт ми спик фром май харт
Re[4]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 06:12
Оценка:
Здравствуйте, Larm, Вы писали:

L>Читал и смеялся. Давайте обозначим 1=двигатель, 2=сиденья, 3=мотор... Дальше продолжать?


Попытайтесь понять смысл выражения "1=двигатель, 2=сиденья, 3=мотор", тогда посмеетесь еще больше.
Re[2]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 06:29
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>Есть один очень важный момент, про который часто забывают. Если у нас объект унаследован от нескольких интерфейсов, то просто поглядев на список наследования уже можно понять, где этот объект может быть применён и отчасти — чем он является.


1) Объект не наследует интерфейсы, а реализовывает их.

2) А чем являются и каков способ применения следующих объектов: "палка из дерева", "кусок камня", "комок ваты", "капля чернил", "калькулятор, довольно прочный на вид, так что им можно гвозди забивать" и т.д? Я же привел формулу g(N) = 2^N — 2 для количества разных вариантов использования одного и того же объекта, из которой видно, что это самое количество вариантов растет экспоненциально. Следовательно, если разработчик класса объекта будет явно прописывать возможные варианты использования этих объектов, то он сможет написать лишь ничтожно малую часть вариантов (устанет писать).
Re[4]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 06:56
Оценка: -1
Здравствуйте, Larm, Вы писали:

L>Здравствуйте, S.Yu.Gubanov, Вы писали:


SYG>>Боюсь, что Вы это о чем-то о своем...


SYG>>Множество {1,2,3,4} включает в себя подмножество {2,3}, но оно не агрегирует его. Агрегация — это когда автомобиль агрегирует в себе свой двигатель, сиденья, бензобак и колеса.


L>Читал и смеялся.


Кстати, Вы в курсе что теория множеств становится логически противоречивой когда начинают рассматривать множества множеств? То есть множество агрегирующее (содержащее) в себе другие множества — математически не совсем корректный объект. Поэтому, множество {1,2,3,4} именно включает в себя подмножества {1,2}, {2,3}, {3,4,5}, {4}, {1,4}, но оно не агрегирует их.
Re[2]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 07:16
Оценка:
Здравствуйте, prVovik, Вы писали:

V>В С++ эта идея уже реализована и давно используется.


Но Си++-совые templates нельзя скомпилировать лишь однажды и распространять в виде бинарных модулей. Как ни крути, но работают они, грубо говоря, только на уровне исходного текста программы, как мега-супер-продвинутый-макрос. Идея использования интерфейсов как раз и заключается в том. что модуль с классом объекта создается лишь однажды (сторонним производителем), а все остальные, зная интерфейс тех объектов, используют этот бинарный модуль. А я в этой ветке форума поставил вопрос, почему нельзя делать так:
I1 = INTERFACE
  PROCEDURE f();
  PROCEDURE g();
END;

I2 = INTERFACE
  PROCEDURE f();
  PROCEDURE g();
  PROCEDURE h();
  PROCEDURE k();
END;

VAR a: I1;
    b: I2;

//...

 a := b; // хочу чтобы это было разрешено! Так как "I2" включает в себя все методы "I1"
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.09.04 08:03
Оценка: +1
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Кстати, Вы в курсе что теория множеств становится логически противоречивой когда начинают рассматривать множества множеств? То есть множество агрегирующее (содержащее) в себе другие множества — математически не совсем корректный объект.

А можно пару комментариев, почему? Это очень широко применяемый объект, и мне хотелось бы знать, в чем его некорректность.
SYG>Поэтому, множество {1,2,3,4} именно включает в себя подмножества {1,2}, {2,3}, {3,4,5}, {4}, {1,4}, но оно не агрегирует их.
Почему "поэтому"? Отношения принадлежности работают между множествами и элементами, отношения включения — между множествами. Никакого противоречия здесь нет. Как, впрочем, и термина "агрегировать" по отношению к множествам.
А вот "множество всех подмножеств множества {1, 2, 3, 4}" содержит перечисленные тобой множества как элементы.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.09.04 08:03
Оценка: +1
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>2) А чем являются и каков способ применения следующих объектов: "палка из дерева", "кусок камня", "комок ваты", "капля чернил", "калькулятор, довольно прочный на вид, так что им можно гвозди забивать" и т.д? Я же привел формулу g(N) = 2^N — 2 для количества разных вариантов использования одного и того же объекта, из которой видно, что это самое количество вариантов растет экспоненциально.

Формулу-то ты привел. Но таких формул можно описать бесчисленное множество. И они никак не влияют на практику программирования.
SYG>Следовательно, если разработчик класса объекта будет явно прописывать возможные варианты использования этих объектов, то он сможет написать лишь ничтожно малую часть вариантов (устанет писать).
Вот именно. Но я могу придумать еще миллион способов утомить разработчика. И все будут столь же бессмысленными. Пока что я не вижу никакой причины бедному разработчику заниматься этой ерундой. Пока что я вижу, что ты придумал задачу, которая никогда не стоит на практике, и предлагаешь способ для ее решения.

Имхо, в программировании вполне достаточно настоящих проблем, чтобы решать еще и надуманные.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.09.04 08:03
Оценка: +1
Здравствуйте, S.Yu.Gubanov, Вы писали:
SYG>Но Си++-совые templates нельзя скомпилировать лишь однажды и распространять в виде бинарных модулей.
Можно. Ограниченно, но можно. Впрочем, к теме это отношения не имеет.
SYG>Как ни крути, но работают они, грубо говоря, только на уровне исходного текста программы, как мега-супер-продвинутый-макрос.
SYG>Идея использования интерфейсов как раз и заключается в том. что модуль с классом объекта создается лишь однажды (сторонним производителем), а все остальные, зная интерфейс тех объектов, используют этот бинарный модуль. А я в этой ветке форума поставил вопрос, почему нельзя делать так:
SYG>
SYG>I1 = INTERFACE
SYG>  PROCEDURE f();
SYG>  PROCEDURE g();
SYG>END;

SYG>I2 = INTERFACE
SYG>  PROCEDURE f();
SYG>  PROCEDURE g();
SYG>  PROCEDURE h();
SYG>  PROCEDURE k();
SYG>END;

SYG>VAR a: I1;
SYG>    b: I2;

SYG>//...

SYG> a := b; // хочу чтобы это было разрешено! Так как "I2" включает в себя все методы "I1"
SYG>


А чем это хуже I2 = interface(I1) ? Или ты полагаешь, что есть какой-то шанс на случайное совпадение сигнатур I1 и I2, которое позволит делать приведение типов осмысленным?
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 29.09.04 08:25
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Здравствуйте, prVovik, Вы писали:


V>>В С++ эта идея уже реализована и давно используется.


SYG>
SYG>I1 = INTERFACE
SYG>  PROCEDURE f();
SYG>  PROCEDURE g();
SYG>END;

SYG>I2 = INTERFACE
SYG>  PROCEDURE f();
SYG>  PROCEDURE g();
SYG>  PROCEDURE h();
SYG>  PROCEDURE k();
SYG>END;

SYG>VAR a: I1;
SYG>    b: I2;

SYG>//...

SYG> a := b; // хочу чтобы это было разрешено! Так как "I2" включает в себя все методы "I1"
SYG>


Ну если руководствоваться вашим "хочу", то возможны например такие конструкции:
/* Пример напиан по мативам книги "Как размножаются ёжики". За основу взято предложение: За окном шёл снег и рота краснармейцев. ;-) */
class Рота_Красноармейцев
{
 public:
    void Идёт();
};

class Снег
{
 public:
    void Идёт();
};

Снег obj1;
Рота_Красноармейцев obj2 = obj1; // Постойте, "рота красноармейцев" не то же самое что и "снег"!
obj2.Идёт();      // Да и ходят они по разному!!
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[4]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 09:25
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Имхо, в программировании вполне достаточно настоящих проблем, чтобы решать еще и надуманные.


Эта задача не надуманная. Вот пример:

Есть два объекта. Первый объект использует второй. Первый объект не знает конкретного типа второго объекта, а взаимодействует с ним через определенный интерфейс. Этот механизм имеет следующую реализацию:
1) Сначала пишется модуль с интерфейсом (D)
2) Потом пишутся (не важно в каком порядке) два модуля, соответственно, с классом первого (A) и классом второго (B) объектов.
3) Во время работы программы первому объекту дается второй объект и все работает, т.к. в момент написания классов этих объектов они оба знали о заблаговременно определенном интерфейсе D.
        D
 (A)----<----(B)

Эта система является гибкой, в том смысле, что вместо второго объекта можно использовать какой угодно другой лишь бы он тоже реализовывал заданный интерфейс D. Все кажется просто и лучше быть не может. Теперь рассмотрим систему покрупнее. Пусть теперь есть много разных объектов использующих друг друга.

                         D3
                  (X)----<-----(X)
                 /
                /D2
       D1      /
 (X)----<----(X)
               \
                \
                 \D4
                  \
                   \      D5
                   (X)----<------(X)

Эта система задается графом, дуги которого есть интерфейсы, а вершины этого графа есть какие угодно объекты реализовывающие интерфейсы соответствующие входящим и исходящим дугам из этих вершин. Зададимся вопросом, на сколько такая система гибка? Да, мы по прежнему можем вместо каждого конкретного класса (X) объектов использовать какой-то другой класс, лишь бы только интерфейсы подходили. Но видители в чем дело. Когда мы, два-три года назад, писали классы объектов, мы и не думали что сегодня захотим объединить их именно в такую схему как показано выше, мы объединяли их немного в другую систему чуточку с другой топологией и чуточку с другими интерфейсами D1', D2', D3',... Чтобы сейчас объединить те уже написанные классы объектов в новую систему взаимодействия нам надо, пусть не много, но изменить исходный код каждого класса, как минимум добавив в заголовке каждого класса, что объекты этих классов теперь могут быть использованы еще и по новым интерфейсам D1, D2, D3, ... И так будет каждый раз когда мы захотим использовать старые уже написанные классы объектов для построения других систем. Почему нам пришлось вносить изменения в исходный код классов? Да потому что есть такое дурацкое требование в определении каждого класса явно прописывать все способы использования объектов этого класса. Какова альтернатива? Альтернативой является системы собираемые также как в детском конструкторе. Есть элементарные компоненты, а уже их сочетание может дать массу всяких систем без внесения изменений в сами компоненты


                 (X)-........-(X)
                 /
                .
               /
 (X)-.......-(X)
               \
                .
                 .
                  .
                   \      
                   (X)-........-(X)
Re[4]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 09:31
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А чем это хуже I2 = interface(I1)?


Тем, что в случае N=4, g(4) = 2^4 — 2 = 14, будешь наследоваться от 14 интерфейсов? Кстати, у интерфейсов множественного наследования нету.


S>Или ты полагаешь, что есть какой-то шанс на случайное совпадение сигнатур I1 и I2, которое позволит делать приведение типов осмысленным?


А что делать? Предложи что-то более безопасное, но тем не менее, более гибкое чем есть?
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.09.04 10:02
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:
SYG>Тем, что в случае N=4, g(4) = 2^4 — 2 = 14, будешь наследоваться от 14 интерфейсов? Кстати, у интерфейсов множественного наследования нету.
Это в Delphi нету. Используй полноценный ООЯП и наследуйся от 4х интерфейсов
S>>Или ты полагаешь, что есть какой-то шанс на случайное совпадение сигнатур I1 и I2, которое позволит делать приведение типов осмысленным?
SYG>А что делать? Предложи что-то более безопасное, но тем не менее, более гибкое чем есть?
public interface IList : ICollection, IEnumerable
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.09.04 10:02
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:
SYG>Эта задача не надуманная. Вот пример:

SYG>Есть два объекта. Первый объект использует второй. Первый объект не знает конкретного типа второго объекта, а взаимодействует с ним через определенный интерфейс.

SYG>Эта система является гибкой, в том смысле, что вместо второго объекта можно использовать какой угодно другой лишь бы он тоже реализовывал заданный интерфейс D. Все кажется просто и лучше быть не может.
Именно.
SYG>Теперь рассмотрим систему покрупнее. Пусть теперь есть много разных объектов использующих друг друга.
SYG>

SYG>                         D3
SYG>                  (X)----<-----(X)
SYG>                 /
SYG>                /D2
SYG>       D1      /
SYG> (X)----<----(X)
SYG>               \
SYG>                \
SYG>                 \D4
SYG>                  \
SYG>                   \      D5
SYG>                   (X)----<------(X)
SYG>

SYG>Эта система задается графом, дуги которого есть интерфейсы, а вершины этого графа есть какие угодно объекты реализовывающие интерфейсы соответствующие входящим и исходящим дугам из этих вершин. Зададимся вопросом, на сколько такая система гибка? Да, мы по прежнему можем вместо каждого конкретного класса (X) объектов использовать какой-то другой класс, лишь бы только интерфейсы подходили. Но видители в чем дело. Когда мы, два-три года назад, писали классы объектов, мы и не думали что сегодня захотим объединить их именно в такую схему как показано выше, мы объединяли их немного в другую систему чуточку с другой топологией и чуточку с другими интерфейсами D1', D2', D3',...
Очень интересно. Вот у тебя есть два класса, связанные по интерфейсу D3. Так? Зашибись. Это означает, что клиенту нужна совершенно определенная функциональность от сервера этого интерфейса. И сервер, соответственно обязан эту функциональность предоставить. И, естественно, об этом рассказать при помощи включения в список реализованных интерфейсов D3.
SYG>Чтобы сейчас объединить те уже написанные классы объектов в новую систему взаимодействия нам надо, пусть не много, но изменить исходный код каждого класса,
Ну естественно. Потому, что объединение — это процесс двусторонний.
SYG>Почему нам пришлось вносить изменения в исходный код классов? Да потому что есть такое дурацкое требование в определении каждого класса явно прописывать все способы использования объектов этого класса.
Нет! Потому, что теперь клиенты требуют от серверов другой функциональности. Ты что, всерьез полагаешь, что эта функциональность совершенно случайно оказалась уже реализованной в старых классах? Не смеши мои тапочки.
SYG>Какова альтернатива? Альтернативой является системы собираемые также как в детском конструкторе. Есть элементарные компоненты, а уже их сочетание может дать массу всяких систем без внесения изменений в сами компоненты
Если ты мне предложишь хоть один пример такой системы, который никак не выражается стандартным подходом, я задумаюсь над реальностью задачи. Вот только не надо иксов и дэ с номерами. Расскажи, как можно собрать объекты новым, не предусмотренным изначально способом, без их переделки.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 10:04
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN> За окном шёл снег и рота краснармейцев. */


Придется давать методам длинные осмысленные имена обозначающие что-то очень конкретное. Тогда вероятность совпадения сигнатур двух методов выполняющих разные по смыслу действия будет минимальна.
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 29.09.04 10:14
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Здравствуйте, Mr. None, Вы писали:


MN>> За окном шёл снег и рота краснармейцев. */


SYG>Придется давать методам длинные осмысленные имена обозначающие что-то очень конкретное. Тогда вероятность совпадения сигнатур двух методов выполняющих разные по смыслу действия будет минимальна.


Нет. Совпадение сигнатур ещё не означает, что объекты состоят в отношении потомок-предок и подобное преобразование ошибочно! Кстати, даже отношение тип-подтип, которое достигается статическим наследованием (см. выше), не всегда является отношением предок-потомок, но это так — размышления.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[6]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 10:21
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А можно пару комментариев, почему?


Возьмем множество всех множеств которые не включают себя как свой элемент. И зададим вопрос Включает ли это множество само себя? Исходя их определения если оно себя включает, то оно себя не включает и наоборот.

http://www.yandex.ru/yandsearch?rpt=rad&amp;text=%EF%E0%F0%E0%E4%EE%EA%F1%FB+%F2%E5%EE%F0%E8%E8+%EC%ED%EE%E6%E5%F1%F2%E2
Re[6]: "Включение" vs "Наследование" интерфейсов
От: Quintanar Россия  
Дата: 29.09.04 10:22
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Нет. Совпадение сигнатур ещё не означает, что объекты состоят в отношении потомок-предок и подобное преобразование ошибочно! Кстати, даже отношение тип-подтип, которое достигается статическим наследованием (см. выше), не всегда является отношением предок-потомок, но это так — размышления.


А если мы хотим написать функцию, где бы все — не важно кто — шагали бы два раза вперед и раз назад? Сейчас надо для это объявлять специальный интерфейс, который все заинтересованные классы должны реализовывать. А спрашивается зачем? А если понадобятся расширения типа поворачивать, летать, прыгать. Мы можем написать кучу функций, которые будут делать что-то полезное на основе базовых навыков движения, но для каждой из них будет нужен свой набор движений и для каждой из них объявлять по интерфейсу?
Re[7]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 29.09.04 10:35
Оценка:
Здравствуйте, Quintanar, Вы писали:

Q>Здравствуйте, Mr. None, Вы писали:


MN>>Нет. Совпадение сигнатур ещё не означает, что объекты состоят в отношении потомок-предок и подобное преобразование ошибочно! Кстати, даже отношение тип-подтип, которое достигается статическим наследованием (см. выше), не всегда является отношением предок-потомок, но это так — размышления.


Q>А если мы хотим написать функцию, где бы все — не важно кто — шагали бы два раза вперед и раз назад? Сейчас надо для это объявлять специальный интерфейс, который все заинтересованные классы должны реализовывать. А спрашивается зачем? А если понадобятся расширения типа поворачивать, летать, прыгать. Мы можем написать кучу функций, которые будут делать что-то полезное на основе базовых навыков движения, но для каждой из них будет нужен свой набор движений и для каждой из них объявлять по интерфейсу?


И что? Следовательно разрешать преобразовывать объект "снег" к объекту "рота красноармейцев" только на том основании, что и то и другое может идти? И при этом совершенно не важно, что эти объекты ну никак не являются родственными? Так что ли?

Могу привести другой пример бездумного использования наследования:
класс очереди оконных сообщений, унаследованный публичным образом от класса список на том лишь основании, что очередь сообщений использует функционал списка. В этом случае тоже возможны подобные преобразования: объект очередь оконных сообщений в объект список — это же bred of sive ckable!
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[7]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.09.04 11:00
Оценка: 1 (1)
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Возьмем множество всех множеств которые не включают себя как свой элемент.

Этот парадокс построен исключительно на возможности определять множество декларативно. Он показывает не слабость теории множеств, а ограниченность декларативных определений. Вообще, декларативные определения подобного рода встречаются сплошь и рядом. Они обходятся путем наложения ограничений на предикаты, служащие генераторами. Например, подобный же парадокс возможен при выполнении запроса в SQL-99. К счастью, существует простой алгоритм проверки, содержит ли подобное противоречие SQL-запрос.

Кстати, данный парадокс не имеет абсолютно никакого отношения к предмету беседы. Потому в этой ветке я постить больше не буду.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: "Включение" vs "Наследование" интерфейсов
От: Quintanar Россия  
Дата: 29.09.04 11:07
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>И что? Следовательно разрешать преобразовывать объект "снег" к объекту "рота красноармейцев" только на том основании, что и то и другое может идти? И при этом совершенно не важно, что эти объекты ну никак не являются родственными? Так что ли?


Нет, преобразовывать объект в объект нельзя. Но можно преобразовывать вот так:
interface F {
  void f();
}

class A {
  f, g
}

class B {
  f, h
}

void f(F f) {
  f->f();
}

void main() {
  A a;
  B b;
  f((F)A); f((F)B);
}

Т.е. иметь возможность преобразовывать объекты к интерфейсам, от которых они не наследовались и чтобы возможность такого приведения определялась только наличием у объекта нужных функций. Обратное преобразование, естественно, разрешать нельзя.
Re[9]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 29.09.04 11:16
Оценка:
Здравствуйте, Quintanar, Вы писали:

Q>Здравствуйте, Mr. None, Вы писали:


MN>>И что? Следовательно разрешать преобразовывать объект "снег" к объекту "рота красноармейцев" только на том основании, что и то и другое может идти? И при этом совершенно не важно, что эти объекты ну никак не являются родственными? Так что ли?


Q>Нет, преобразовывать объект в объект нельзя. Но можно преобразовывать вот так:

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

Ага, возвращаясь к нашим ёжикам и красноармейцам, имеем:

interface IБоеваяЕдиница
{
 public:
    void Идёт();
};

void ИзменитьДислокацию(IБоеваяЕдиница obj)
{
    obj.Идёт();
}

Снег снег;
ИзменитьДислокацию((IБоеваяЕдиница)снег); // Н-да... вы уверены, что именно это имели в виду?
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[10]: "Включение" vs "Наследование" интерфейсов
От: Quintanar Россия  
Дата: 29.09.04 11:27
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Ага, возвращаясь к нашим ёжикам и красноармейцам, имеем:


MN>
MN>interface IБоеваяЕдиница
MN>{
MN> public:
MN>    void Идёт();
MN>};

MN>void ИзменитьДислокацию(IБоеваяЕдиница obj)
MN>{
MN>    obj.Идёт();
MN>}

MN>Снег снег;
MN>ИзменитьДислокацию((IБоеваяЕдиница)снег); // Н-да... вы уверены, что именно это имели в виду?
MN>



Здесь налицо неправильный подход к определению функции. Поскольку она не делает ничего боеваяединицаспеципичного, то нефиг в нее передавать такой интерфейс. И сам интерфейс к боевым единицам отношения не имеет, он определяет только нечто, что ходит. Ты сам написал, что можно намудить с обычным наследованием, так и здесь можно намудить при желании какую-нибудь чушь.
Re[3]: "Включение" vs "Наследование" интерфейсов
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 29.09.04 12:02
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Здравствуйте, Геннадий Васильев, Вы писали:


ГВ>>Есть один очень важный момент, про который часто забывают. Если у нас объект унаследован от нескольких интерфейсов, то просто поглядев на список наследования уже можно понять, где этот объект может быть применён и отчасти — чем он является.


SYG>1) Объект не наследует интерфейсы, а реализовывает их.


Да, ты прав. Наследование — это только метод.

SYG>2) А чем являются и каков способ применения следующих объектов: "палка из дерева", "кусок камня", "комок ваты", "капля чернил", "калькулятор, довольно прочный на вид, так что им можно гвозди забивать" и т.д?


На этот вопрос невозможно ответить, не задав дополнительного: "А в каком контексте?"

SYG>Я же привел формулу g(N) = 2^N — 2 для количества разных вариантов использования одного и того же объекта, из которой видно, что это самое количество вариантов растет экспоненциально. Следовательно, если разработчик класса объекта будет явно прописывать возможные варианты использования этих объектов, то он сможет написать лишь ничтожно малую часть вариантов (устанет писать).


В ожеговском словаре русского языка 56 г. издания около 56000 слов. И поверь, далеко не все тексты, сотавленные из этих слов имеют хоть какой-то смысл. Какой смысл у комбинации "к, в, на"? Подсказка: психоделические мотивы не привлекать.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 29.09.04 12:05
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

MN>> За окном шёл снег и рота краснармейцев. */


SYG>Придется давать методам длинные осмысленные имена обозначающие что-то очень конкретное. Тогда вероятность совпадения сигнатур двух методов выполняющих разные по смыслу действия будет минимальна.


Угу, уже тепло.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 29.09.04 12:15
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

На самом деле идея весьма здравая. Такой подход позволит полностью выкинуть интерфейсы из списка наследования классов, что ИМХО есть очень хорошо. В конце концов что такое интерфейс? — место где встречаются и взаимодействуют независимые системы. В идеале м-ду обьектом и интерфейсом должны осуществлятся отношения "использования", т.е. интерфейс использует обьект. Когда обьект реализует интерфейс или наследуется от него то это уже костыли и грабли. Почему грабли и костыли? Потому что списочек фиксирован:
class Foo : public IFoo, public IBar

И свободных мест нет (особенно в thrid party классах );

Уважаемый S.Yu.Gubanov фактически предложил полностью отделить интенрфейс от реализации, т.е. сделать связь интерфейс-обьект очень слабой (наследование это очень сильная связь). Так и должно быть в иделае.

Примечательно что у себя в проекте мне недавно приходилось делать нечто подобное, правда ручками все. Т.е. примерно вот так:

    // some Node class
    class Node {
    public:
        void    get( int &value, const char *key );
        void    set( int value, const char *key );
        // ... etc
    };

    // VTFable for INode
    struct INodeVFT {
        bool (*int_get)(void *node, int *v, const char *key, ErrorInfo *e);
        bool (*int_set)(void *node, int v, const char *key, ErrorInfo *e);
        // ... etc
    };

    // отделенный обертывающий интерфейс
    struct INode {
        void            *node; // некий узел не обязательно Node
        const INodeVFT    *vft;  // самодельная в-тейбл.

    public:
        int int_get( const char *key ) // функция хелпер для удобства
        {
            int i;
            ErrorInfo e;
            if ( (vft->int_get)( node, &i, key, &e ) )
                return i;
            throw Error(e);
        }
    };

    // поскольку нет наследования то "оператор" приведения типа прилагается
    void wrap( Node *n, INode &node )
    {
        node.node = n;
        node.vft  = &NodeVFT;
    }

    // a это оборачиватель для ThirdParty Node
    void wrap( ThirdPartyNode *n, INode &node )
    {
        node.node = n;
        node.vft  = &ThirdPartyNodeVFT;
    }

// ==================================================================
// юзалось это так:
// ==================================================================

    void acceptMyNode( Node *n )
    {
        INode node;
        wrap( n, node );
        doNodeWork( node );
    }

    void acceptThirdPartyNode( ThirdPartyNode *n )
    {
        INode node;
        wrap( n, node );
        doNodeWork( node );
    }

    void doNodeWork( INode node )
    {
        int x = node.int_get("FooBar");
    }
Re[11]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 29.09.04 12:19
Оценка:
Здравствуйте, Quintanar, Вы писали:

Q>Здравствуйте, Mr. None, Вы писали:


Q>Здесь налицо неправильный подход к определению функции. Поскольку она не делает ничего боеваяединицаспеципичного, то нефиг в нее передавать такой интерфейс.


Что значит неправильный подход к определению функции? А какой правильный? У меня есть интерфейс, который что-то определяет, мне нужна функция, которая будет каким-то образом использовать функционал объектов, предоставляемый через этот интерфейс. Что более надо? И тем более, что данный пример как раз более чем боеваяединицаспеципичный: функция, изменяющая дислокацию боевой единицы, она использует единственный метод интерфейса — идти.

Q>И сам интерфейс к боевым единицам отношения не имеет, он определяет только нечто, что ходит.


А что, боевая единица не умеет ходить? А может у меня "бевая единица на параде" — она только ходит и больше ничего не делает...
Не нравится пример с боевыми единицами и снегом, пожалуйста более жизненный пример:
interface IWavPlayer // Интерфейс проигрывателя wav-файлов
{
 public:
    void Play();
};
class MainGameObject // Основной объект игры
{
 public:
    void Play();
};

PlayMusic(IWavPlayer player) // некий метод, который проигрывает музыку в фоне.
{
    // запуск нового потока
    player.Play();
}
MainGameObject game;
PlayMusic((IWavPlayer)game); // И?!!


Q>Ты сам написал, что можно намудить с обычным наследованием, так и здесь можно намудить при желании какую-нибудь чушь.


Не намудрить, а неправильно использовать понятия: использовать отношение тип-подтип там, где нужно потомок-предок. Статическое наследование используется и для одного и для другого — оно имеет право жить, главное правильно его использовать. К сожалению эти правила носят чисто деклоративную семантику и их нельзя закрепить в коде — это факт. Но если система спроектирована правильно и все эти правила соблюдены, то вам не удасться нарушить правила типизации компилятора и преобразовать объект "снег" к объекту "рота красноармейцев", даже если они имеют идентичный список методов. Для того типы и были введены — дабы избежать некорректного использования объектов в выражении. И для того было введено наследование, чтобы разрешить группам разных объектов, но со схожими свойствами использоваться в одних и тех же выражениях. Так зачем же добавлять механизм, который нарушает все эти старания?
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re: "Включение" vs "Наследование" интерфейсов
От: Gaperton http://gaperton.livejournal.com
Дата: 29.09.04 12:31
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Всвязи с этим, возникает вопрос: А не стоит ли для интерфейсов ввести такое понятие как ВКЛЮЧЕНИЕ ИНТЕРФЕЙСОВ и, соответственно, отказаться от наследования интерфейсов, поскольку включение — есть более мощный механизм нежели механизм наследования?


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


Все крайне просто. Если рассматривать только наследование интерфейсов (без наследования реализации), то по выразительной силе это совершенно эквивалентно динамической диспетчеризации вызова. В самом деле:
Запихнем каждую функцию в свой интерфейс. Перед вызовом функции запрашиваем у объекта соотв. интерфейс. Что получается? Динамическая типизация. Начинаем группировать несколько функций в один интерфейс. Суть остается та же, но количество проверок типов уменьшается. Эта техника в сочетании с "наследованием" (расширением) интерфейсов позволяет внести динамическую семантику вызовов в строго типизированный язык.

То что ты предлагаешь, это еще один шаг, приближающий интерфейсы по внешнему виду к простой и честной динамической типизации.
Re[6]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 29.09.04 12:33
Оценка: +1
Здравствуйте, Kluev, Вы писали:

K>Здравствуйте, S.Yu.Gubanov, Вы писали:


K>На самом деле идея весьма здравая. Такой подход позволит полностью выкинуть интерфейсы из списка наследования классов, что ИМХО есть очень хорошо. В конце концов что такое интерфейс? — место где встречаются и взаимодействуют независимые системы.


Интерфейс — это контракт между объектом и его пользователем, как он реализован — это уже дело десятое, наследование при этом совсем не обязательно.

K>В идеале м-ду обьектом и интерфейсом должны осуществлятся отношения "использования", т.е. интерфейс использует обьект.


А вот это, извините, полная чушь. Как контракт может использовать своего владельца ("И мы имеем наших женщин, которые имеют наши магазины, которые имеют нас как хотят" (C) Жванецкий). Интерфейс всегда (подчёркиваю — всегда) реализуется объектом. Он может быть декларативным, динамически формируемым, статически типизированым, но реализуется он всегда объектом, а не использует свой объект.

K>Когда обьект реализует интерфейс или наследуется от него то это уже костыли и грабли. Почему грабли и костыли? Потому что списочек фиксирован:

K>
K>class Foo : public IFoo, public IBar
K>

K>И свободных мест нет (особенно в thrid party классах );

K>Уважаемый S.Yu.Gubanov фактически предложил полностью отделить интенрфейс от реализации, т.е. сделать связь интерфейс-обьект очень слабой (наследование это очень сильная связь). Так и должно быть в иделае.


Связь между объектом и интерфейсом не может быть слабой. Она есть, и она сильна — и всё тут. Связь между объектом и его типами может быть слабой — это уже языки с динамической типизацией (SmallTalk). То есть вы посылаете любое сообщение объекту и он его либо обрабатывает, если сообщение соотноситься с его интерфейсом, либо нет. Но при этом список обрабатываемых объектом сообщений всегда постоянен — это и есть интерфейс данного объекта. А, простите, что это за херомантия получается, если связь между объектом и интерфейсом слабая, сегодня объект обрабатывает данное сообщение, а завтра нет... Это только в российской действительности ответственная сторона может менять соглашение по своему усмотрению, извинте за лирическое отступление.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[12]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 12:47
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>К сожалению эти правила носят чисто деклоративную семантику и их нельзя закрепить в коде — это факт.


Но может быть все-таки можно как-нибудь обмозговать это дело и решить проблему?...

MN> Так зачем же добавлять механизм, который нарушает все эти старания?


В принципе я с Вами согласен. Просто взять и сказать что интерфейсам теперь разрешено включать друг друга принесет больше вреда чем пользы. Будем думать дальше....
Re[7]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 29.09.04 12:51
Оценка:
Здравствуйте, Mr. None, Вы писали:

K>>В идеале м-ду обьектом и интерфейсом должны осуществлятся отношения "использования", т.е. интерфейс использует обьект.


MN>А вот это, извините, полная чушь. Как контракт может использовать своего владельца ("И мы имеем наших женщин, которые имеют наши магазины, которые имеют нас как хотят" (C) Жванецкий).

Когда мы спустимся с академических высот на низкий уровень реализации то это имеет место быть. Посмотрите кодец котороый я привел. Класс который выполняет функцию интерфейса исползьует класс реализацию. Т.е. обьект "интерфейс" используеет обьект "обьект"

Интерфейс всегда (подчёркиваю — всегда) реализуется объектом. Он может быть декларативным, динамически формируемым, статически типизированым, но реализуется он всегда объектом, а не использует свой объект.

Я имел ввиду уже физический уровень реализации, ну не буду же я все время писать "обьект интерфейсного класса"? такое и читать то будет невозможно. Смотрите в подтекст и все будет ясно и непротиворечиво.
Re[5]: "Включение" vs "Наследование" интерфейсов
От: Larm Украина  
Дата: 29.09.04 12:55
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

L>>Читал и смеялся. Давайте обозначим 1=двигатель, 2=сиденья, 3=мотор... Дальше продолжать?


SYG>Попытайтесь понять смысл выражения "1=двигатель, 2=сиденья, 3=мотор", тогда посмеетесь еще больше.


а что не так со смыслом? С "3=мотор" был не прав. Пусть будет "3=бензобак" Итого, ввели алиас для 1, алиас для 2, алиас для 3... В чем проблема? Получим что "машина" = {1,2,3,4} = {двигатерль, сиденья, бензобак, колеса} .
The God who walks is among us...
Re[6]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 29.09.04 13:02
Оценка:
Здравствуйте, Larm, Вы писали:

L>а что не так со смыслом? С "3=мотор" был не прав. Пусть будет "3=бензобак"


"3" — это число, то есть абстракция. "бензобак" — эта такая фиговина куда бензин наливают. Спрашивается, как понимать выражение "3=бензобак"?
Re[8]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 30.09.04 04:43
Оценка:
Здравствуйте, Kluev, Вы писали:

K>Когда мы спустимся с академических высот на низкий уровень реализации то это имеет место быть.


Извольте — давайте спустимся, и увидим отсутствие объектов и интерфейсов, а только биты, байты, регистры и команды процессора. Так что же теперь назвать языки высокого уровня стезёй академиков и начать перевирать основные принципы их построения, аппелируя к принципам низкого уровня, причём допуская ошибки в своих утверждениях.

K>Посмотрите кодец котороый я привел. Класс который выполняет функцию интерфейса исползьует класс реализацию. Т.е. обьект "интерфейс" используеет обьект "обьект"


Насколько я смог понят, вы привели ручную реализацию динамической типизации. И то, что вы называете объектом "интерфейс" интерфейсом как таковым не является. Интерфейс — это как раз список сигнатур функций, реализуемых тем или иным объектом Node. Если объект сохранённый в переменной *node не реализует функции get и set (не поддерживает определённый интерфейс), то ничего у вас не склеиться.

MN>>Интерфейс всегда (подчёркиваю — всегда) реализуется объектом. Он может быть декларативным, динамически формируемым, статически типизированым, но реализуется он всегда объектом, а не использует свой объект.


K>Я имел ввиду уже физический уровень реализации, ну не буду же я все время писать "обьект интерфейсного класса"? такое и читать то будет невозможно. Смотрите в подтекст и все будет ясно и непротиворечиво.


На физическом уровне реализации вообще никаких интерфейсов нет см. выше про биты и байты. А такого понятия как "объект интерфейсного класса" вообще не существует. Есть понятие объекта определённого класса, реализующего определённый интерфей. У интерфейса вообще не может быть объекта, потому как это декларация и она не может быть инстанцирована.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[13]: "Включение" vs "Наследование" интерфейсов
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 30.09.04 04:56
Оценка: 10 (1)
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Но может быть все-таки можно как-нибудь обмозговать это дело и решить проблему?...


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

Разные языки исповедуют разные принципы и подходы:
одни добавляют дополнительные возможности раннего (на стадии компиляции) выявления ошибок за счёт введение жёсткой статической типизации и, как следствие, добавления излишнего кодирования всяких деклараций;
другие предоставляют средства быстрого прототипирования и быстрого начала групповой разработки приложения, за счёт динамической типизации или полного отказа от типов; эти допущения снижают число деклараций и делают различные части приложения менее связанными друг с другом, что облегчает групповую разработку, по крайней мере на ранних стадиях, но увеличивает число ошибок периода исполнения.
И смешивать эти подходы не имеет смысла, потому что мы будем больше терять, чем находить.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[14]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 30.09.04 06:15
Оценка:
Здравствуйте, Mr. None, Вы писали:

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


Да, это удобная вещь. Я в Delphi такое сделал и повсеместно использую.
http://www.rsdn.ru/Forum/Message.aspx?mid=822951&amp;only=1
Автор: S.Yu.Gubanov
Дата: 24.09.04
Re[14]: "Включение" vs "Наследование" интерфейсов
От: kaa_t Россия  
Дата: 30.09.04 06:18
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

http://www.rsdn.ru/Forum/Message.aspx?mid=829403&amp;only=1
Автор: S.Yu.Gubanov
Дата: 29.09.04

по описанию очень смахивает на "семантическую сеть".

пример семантической сети.


Обычно данную систему построения модели используют для хранения информации. Основная программа через объекты связи видет поиск данных. Ты же хочешь распределить саму программу , в принципе наверное можно реализовать передачу интерфейса. Но непонятно зачем? Чисто теоретически, к примеру объекту "армения" предоставляется интерфейс "опек" и что дальше? Я чесно говоря не представляю как должна будет работать такая программа,так как все узлы евляются обсалютно равноправными. Приведи пример на данной конкретной модели.
Re[15]: "Включение" vs "Наследование" интерфейсов
От: S.Yu.Gubanov Россия http://sergey-gubanov.livejournal.com/
Дата: 30.09.04 07:19
Оценка:
Здравствуйте, kaa_t, Вы писали:


_> по описанию очень смахивает на "семантическую сеть".


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


Правильно что не представляете, ведь мой пример не имеет к этому никакого отношения.
Re[9]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 30.09.04 07:41
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Здравствуйте, Kluev, Вы писали:


K>>Когда мы спустимся с академических высот на низкий уровень реализации то это имеет место быть.


MN>Извольте — давайте спустимся, и увидим отсутствие объектов и интерфейсов, а только биты, байты, регистры и команды процессора. Так что же теперь назвать языки высокого уровня стезёй академиков и начать перевирать основные принципы их построения, аппелируя к принципам низкого уровня, причём допуская ошибки в своих утверждениях.


Если так делать то мы на всю жизнь увяжем в бесполезной полемике. Я выразил мысль достаточно ясно для понимания. Дальше имхо перетирать нет смысла.

P.S. Кстати, "перевирание основных принципов" и "ошибки в утверждениях" не мешают мне писать работаюшие и эффективные программы. В этом отношении я согласен с Б.Страуструпом который действовал по принципу: если теория мешает практике, то ну ее в ж. такую теорию.
Re[16]: "Включение" vs "Наследование" интерфейсов
От: kaa_t Россия  
Дата: 30.09.04 08:42
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>Здравствуйте, kaa_t, Вы писали:



_>> по описанию очень смахивает на "семантическую сеть".


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


SYG>Правильно что не представляете, ведь мой пример не имеет к этому никакого отношения.


цитата:
Есть два объекта. Первый объект использует второй. Первый объект не знает конкретного типа второго объекта, а взаимодействует с ним через определенный интерфейс. Этот механизм имеет следующую реализацию:
1) Сначала пишется модуль с интерфейсом (D)
2) Потом пишутся (не важно в каком порядке) два модуля, соответственно, с классом первого (A) и классом второго (B) объектов.
3) Во время работы программы первому объекту дается второй объект и все работает, т.к. в момент написания классов этих объектов они оба знали о заблаговременно определенном интерфейсе D.


        D
 (A)----<----(B)




Имеется два объекта обмен между которыми осуществляется через промежуточный объект отношение
Имено так строется семантическая сеть(за исключением выделеного участка у вас право управлять объектом B предоставляется объекту A т е система принятия решения расползается по разным объектам. Как они должны взаимодействовать?).
Re[6]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.09.04 08:51
Оценка:
Здравствуйте, Kluev, Вы писали:
K>Примечательно что у себя в проекте мне недавно приходилось делать нечто подобное, правда ручками все. Т.е. примерно вот так:
Да, довольно-таки неустойчивая реализация паттерна "декоратор". Правда, неясно, какое отношение она имеет к отказу от интерфейсов, исповедуемому Gubanov. Если я правильно понял, его беспокоит необходимость вписывать в декларацию ThirdPartyNode реализации интерфейса INode (при этом он почему-то полагает, что ThirdPartyNode магическим способом будет таки иметь необходимые методы, в данном случае get и set). А ты предлагаешь вместо вписывания одного слова в декларацию класса писать полноценный класс-враппер. Кстати, есть более устойчивый способ реализации данного паттерна на С++, чем то, что ты написал. А вообще подход, конечно, более правильный. Потому как если у нас сервер не выполняет контракт, требуемый клиентом, то согласовать их можно только вручную. А делать это в компайл-тайме или в ран-тайме — не так уж важно.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: "Включение" vs "Наследование" интерфейсов
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 30.09.04 10:34
Оценка:
Здравствуйте, kaa_t, Вы писали:

_>Имеется два объекта обмен между которыми осуществляется через промежуточный объект отношение


Ну что Вы в самом деле... Нету никакого промежуточного объекта.
Re[7]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 30.09.04 11:29
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Да, довольно-таки неустойчивая реализация паттерна "декоратор". Правда, неясно, какое отношение она имеет к отказу от интерфейсов, исповедуемому Gubanov. Если я правильно понял, его беспокоит необходимость вписывать в декларацию ThirdPartyNode реализации интерфейса INode (при этом он почему-то полагает, что ThirdPartyNode магическим способом будет таки иметь необходимые методы, в данном случае get и set). А ты предлагаешь вместо вписывания одного слова в декларацию класса писать полноценный класс-враппер. Кстати, есть более устойчивый способ реализации данного паттерна на С++, чем то, что ты написал.


Это какой? В моей реализации мне еще пришлось учитывать специфические требования такие как вызовы из модулей скомпиленных разными компилерами. Т.к. 100% С++ совместимости как я понял нет даже на уровне VFTable проишлость все чисто С-шным способом своими руками. + небольшая автоматизация через templates. А вообще, работать с врапперами оказалось настолько удобнее, что я пожалуй напишу свой IDL транслятор.

S>А вообще подход, конечно, более правильный. Потому как если у нас сервер не выполняет контракт, требуемый клиентом, то согласовать их можно только вручную. А делать это в компайл-тайме или в ран-тайме — не так уж важно.


ИМХО это скорее исключение из правил когда third party классы поддерживает интерфейсы нужные клиенту. Обчно все наооборот. Было бы очень не плохо если бы язык имел встроенную поддержку "оберточных" интерфейсов. Имхо стыковать компонеты было бы гораздо проще.
Re[16]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.09.04 12:21
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:
SYG>Правильно что не представляете, ведь мой пример не имеет к этому никакого отношения.
Так ты пример-то привел бы? А то что-то так и неясно, что и зачем ты предлагаешь. Вот прямо так:

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

А?
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.09.04 12:21
Оценка:
Здравствуйте, Kluev, Вы писали:

K>Это какой?

Это когда вместо привязки VMT вручную ты делаешь нормальный вызов вложенного объекта. Ты в своем коде не привел определений NodeVFT и ThirdPartyNodeVFT, а ведь предполагается, что они будут указывать на соответствующим образом оформленные методы. То есть собственно код трансляции вызова из конвенции клиента в конвенцию сервера ты почему-то оставил за кадром.
Я бы сделал примерно так:
struct INode 
{
    int get(const char *key);
    void set(int value, const char *key);
};

class NodeWrapper : public INode
{
    private:
        const Node &wrapped;
    public: 
        // type conversion operator
        NodeWrapper(Node &toWrap) : wrapped(toWrap){};
    public: 
      // INode implementation
      int get( const char *key )
        {
          int value;
            wrapped.get(value, key);
            return value;
        }
        void set(int value, const char *key)
        {
          wrapped.set(value, key);
        }
}

...
void acceptMyNode(Node &n)
    {
        doNodeWork(n);
    }

    void acceptThirdPartyNode( ThirdPartyNode &n )
    {
        doNodeWork(n);
    }

    void doNodeWork(INode node )
    {
        int x = node.get("FooBar");
    }

Преимущества:
1. вместо указателей — ссылки. Т.е. гарантия присвоенности. В твоем случае можно забыть вызвать wrap. Получим AV. В моем — нет. Автоматически подставится правильная версия.
2. Строгая типизация. Твой INode запросто позволит присвоить в node ThirdPartyNode, a в vft — NodeVFT. Что мы получим?
3. Весь врапающий код — в одном месте. Все прозрачно. В твоем случае приведенный фрагмент кода не показывает, как именно void Node::get( int &value, const char *key ) превращается в bool (*int_get)(void *node, int *v, const char *key, ErrorInfo *e);.
4. Компилер при удачном раскладе (как в примере) может даже выполнить инлайнинг, и производительность будет лучше, чем при прямых вызовах.

K>ИМХО это скорее исключение из правил когда third party классы поддерживает интерфейсы нужные клиенту. Обчно все наооборот. Было бы очень не плохо если бы язык имел встроенную поддержку "оберточных" интерфейсов. Имхо стыковать компонеты было бы гораздо проще.

Я совершенно согласен. Вот только Gubanov эту проблему никак не решает. Даже если у нас совершенно случайно наш ThirdPartyNode не поддерживает INode, но у него есть совадающие по сигнатурам методы set и get, то чем придумывать какие-то кошмары с 2^N подынтерфейсами проще написать вот так:
class ThirdPartyNodeWrapper : public ThirdPartyNode, public INode
{
  public ThirdPartyNodeWrapper(&ThirdPartyNode node): ThirdPartyNode(node){};
}

Кстати, как ты себе представляешь встроенную в компилятор поддержку оберточных интерфейсов? Я пока не вижу ничего такого, что мог бы сделать сам компилятор.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 30.09.04 13:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

K>>Это какой?

S>Это когда вместо привязки VMT вручную ты делаешь нормальный вызов вложенного объекта. Ты в своем коде не привел определений NodeVFT и ThirdPartyNodeVFT, а ведь предполагается, что они будут указывать на соответствующим образом оформленные методы. То есть собственно код трансляции вызова из конвенции клиента в конвенцию сервера ты почему-то оставил за кадром.
S>Я бы сделал примерно так:
S>
S>struct INode 
S>{
S>    int get(const char *key);
S>    void set(int value, const char *key);
S>};

S>class NodeWrapper : public INode
S>{
// дальше поскипано.
S>

Все понял. Но мне это не подошло т.к. нет гарнтии что INode будет правильно понят разными компилерами.
В моем случае INode может уйти в функцию которая сидит в dll сделанной неизвестным компилером.

А реализацию я сделал вот такую: Конечно же писанины получилось больше:
// вспомогательные классцы
struct ErrInfo {
    // поскипан
};

struct Error {
    Error( ErrInfo& ) {}
};

// class MyNode - его будем враппить
struct MyNode {
    int get( const char *key ) { return (int)key; }
};

// ВФ-Тейбл
struct INodeVFT {
    bool    (*int_get)(void*, const char*, int*, ErrInfo*);
    bool    (*int_set)(void*, const char *, int, ErrInfo*);
};

// W-интерфейс
struct INode {
    void              *xp;
    const INodeVFT    *vt;

public:
// хелперы:
    int int_get( const char *key )
    {
        ErrInfo e; int i;
        if ( (*vt->int_get)(xp, key, &i, &e) )
            return i;
        throw Error(e);
    }

    void int_set( const char *key, int value )
    {
        ErrInfo e;
        if ( (*vt->int_set)(xp, key, value, &e) )
            return;
        throw Error(e);
    }
};

// INode wrapper base
template <class This, class T>
struct WNodeImp {
    static const INodeVFT    vt;

// методы по дефлоту:
    static bool    int_get(void*, const char*, int*, ErrInfo*) { return false; }
    static bool    int_set(void*, const char *, int, ErrInfo*) { return false; }

    friend void wrap( T *node, INode &inode )
    {
        inode.xp = node;
        inode.vt = &vt;
    }
};

template <class This, class T>
const INodeVFT WNodeImp<This,T>::vt =
{
    This::int_get,
    This::int_set
};

// INode wrapper for MyNode
struct WMyNode : WNodeImp<WMyNode,MyNode>
{
    static bool int_get( void *x, const char *k, int *rv, ErrInfo* )
    {
        MyNode *n = (MyNode*)x;
        *rv = n->get(k);
        return true;
    }
};

// testing
void work_do( INode node )
{
    int x = node.int_get("ZZz");
    try {
        node.int_set("dgdf", 10); // not implemented
    } catch( Error& )
    {
    }
}

void learn()
{
    MyNode    node;
    INode        inode;
    wrap( &node, inode );

    work_do( inode );

    return;
}

Здесь компилер тоже все проверит, т.к. все небезопасные приведения закрыты.

K>>ИМХО это скорее исключение из правил когда third party классы поддерживает интерфейсы нужные клиенту. Обчно все наооборот. Было бы очень не плохо если бы язык имел встроенную поддержку "оберточных" интерфейсов. Имхо стыковать компонеты было бы гораздо проще.

S>Я совершенно согласен. Вот только Gubanov эту проблему никак не решает. Даже если у нас совершенно случайно наш ThirdPartyNode не поддерживает INode, но у него есть совадающие по сигнатурам методы set и get, то чем придумывать какие-то кошмары с 2^N подынтерфейсами проще написать вот так:
S>
S>class ThirdPartyNodeWrapper : public ThirdPartyNode, public INode
S>{
S>  public ThirdPartyNodeWrapper(&ThirdPartyNode node): ThirdPartyNode(node){};
S>}
S>

S>Кстати, как ты себе представляешь встроенную в компилятор поддержку оберточных интерфейсов? Я пока не вижу ничего такого, что мог бы сделать сам компилятор.

Пока никак. Однако преимущества я уже оценил. Думаю сделать что-то вроде IDL. Генерить обертки.
Re[17]: "Включение" vs "Наследование" интерфейсов
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 30.09.04 13:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Так ты пример-то привел бы?



Есть два (бинарных) модуля M1 и M2 (от разных производителей) со следующими интерфейсами:

UNIT M1;

  TYPE
    IDocument = INTERFACE
      PROCEDURE OpenDocument();
      PROCEDURE CheckDocument();
      PROCEDURE CloseDocument();
    END;

    IDocuments = INTERFACE
      PROCEDURE IncludeDocument(doc: IDocument);
      PROCEDURE ExcludeDocument(doc: IDocument);
      PROCEDURE CheckDocuments();
    END;

  FUNCTION NewDocuments(): IDocuments;

END.




UNIT M2;

  TYPE
    ISuperDocument = INTERFACE
      PROCEDURE OpenDocument();
      PROCEDURE CheckDocument();
      PROCEDURE CloseDocument();
      PROCEDURE GetWriter(OUT Writer: IWriter);
      PROCEDURE GetReader(OUT Reader: IReader);
    END;

  FUNCTION NewSuperDocument(): ISuperDocument;

END.



В первом модуле описан некий интерфейс документов и контейнера документов, а во втором модуле описан интерфейс какого-то специфичного супер-документа. Так сложилось, что по смыслу этот супер-документ можно использовать вместо документа из модуля M1, и у него даже имена методов совпадают OpenDocument, CheckDocument, CloseDocument. То есть интерфейс M2.ISuperDocument включает в себя интерфейс M1.IDocument

M2.ISuperDocument >= M1.IDocument;

Но вот компилятор не разрешит засунуть супер-документы в контейнер M1.IDocuments потому что M2.ISuperDocument и M1.IDocument не связаны отношением наследования. Однако, если бы работал принцип включения (поглощения) одного интерфейса другим, то можно было бы использовать эти два компонента совместно.
Re[18]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 30.09.04 13:55
Оценка: 18 (2)
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Здравствуйте, Sinclair, Вы писали:


S>>Так ты пример-то привел бы?


СГ>Есть два (бинарных) модуля M1 и M2 (от разных производителей) со следующими интерфейсами:


СГ>
СГ>UNIT M1;
СГ>UNIT M2;
СГ>


СГ>В первом модуле описан некий интерфейс документов и контейнера документов, а во втором модуле описан интерфейс какого-то специфичного супер-документа. Так сложилось, что по смыслу этот супер-документ можно использовать вместо документа из модуля M1, и у него даже имена методов совпадают OpenDocument, CheckDocument, CloseDocument. То есть интерфейс M2.ISuperDocument включает в себя интерфейс M1.IDocument

СГ>

СГ>M2.ISuperDocument >= M1.IDocument;

СГ>Но вот компилятор не разрешит засунуть супер-документы в контейнер M1.IDocuments потому что M2.ISuperDocument и M1.IDocument не связаны отношением наследования. Однако, если бы работал принцип включения (поглощения) одного интерфейса другим, то можно было бы использовать эти два компонента совместно.

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

Имхо надо копать в сотрону оберток, либо напрямую поддерживаемых языком:
// что-то типа
interface IFoo {
   int  foo();
   void bar();
};

class Some {
   int  dummy();
   void mummy();
};

wrapper Some : IFoo {
   foo = dummy;
   bar = mummy;
};

void test( Some &s )
{
   IFoo &foo = s; // компилер заюзает то что написано во wrapper Some
   foo.foo();
}


Либо оборачивать все через IDL.
Re[19]: "Включение" vs "Наследование" интерфейсов
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 30.09.04 14:50
Оценка:
Здравствуйте, Kluev, Вы писали:

K>
wrapper Some : IFoo {
   foo = dummy;
   bar = mummy;
};


Да, надо что-то такое!
Re[18]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.09.04 14:55
Оценка: +1
Здравствуйте, Сергей Губанов, Вы писали:

СГ>В первом модуле описан некий интерфейс документов и контейнера документов, а во втором модуле описан интерфейс какого-то специфичного супер-документа. Так сложилось, что по смыслу этот супер-документ можно использовать вместо документа из модуля M1, и у него даже имена методов совпадают OpenDocument, CheckDocument, CloseDocument. То есть интерфейс M2.ISuperDocument включает в себя интерфейс M1.IDocument

СГ>

СГ>M2.ISuperDocument >= M1.IDocument;

Ну братан, ну ты даешь! Давно я так не смеялся. Шанс на случайное объятие даже простейшего интерфейса равен нулю с хорошей точностью. А имена методов не совпадают вообще практически никогда! У одного Check, у другого Verify. И это для методов без параметров, для которых шанс совпасть сигнатурой минимален. Ну ты честно признайся, что случайных совпадений такого рода не бывает! С тем же успехом ты мог бы вызывать произвольную функцию из библиотеки в надежде, что это случайно окажется синус.
А если совпадение не случайно, то одно из двух:
1. Разработчик интерфейса IDocument намеренно урезал ISuperDocument потому, что не хотел реализовывать суперфункциональность. А автор ISuperDocument нарушил правило атомарности интерфейса. Отлично, это не проблема. Поскольку зависимый интерфейс IDocuments у него в руках, он просто добавляет в него возможность работать и с ISuperDocument
2. Разработчик интерфейса ISuperDocument был в курсе существования пары IDocument/IDocuments. Тем не менее он просто скопировал часть определения, но не унаследовался. Либо он просто дятел, и его надо уволить, либо он намеренно пошел на это, понимая, что на самом деле использовать ISuperDocument в качестве IDocument нельзя.

Таким образом, ты предлагаешь способ решения проблемы, которая не встречается в природе. Я не думаю, что ты сможешь привести реальный пример такого совпадения. При этом это значяительное изменение поведения компилятора никак не поможет в случае хоть малейшего отступления от протокола! Я привел пример с Check/Verify. Все, твоя технология уехала в трэш.
Зато способ с генерацией враппера подходит всегда, т.к. в нем программист руками описывает отображение методов объемлемого интерфейса на методы объемлющего.

Так что не занимайся ерундой.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[19]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.09.04 14:55
Оценка:
Здравствуйте, Kluev, Вы писали:


K>Имхо надо копать в сотрону оберток, либо напрямую поддерживаемых языком:

K>
K>// что-то типа
K>interface IFoo {
K>   int  foo();
K>   void bar();
K>};

K>class Some {
K>   int  dummy();
K>   void mummy();
K>};

K>wrapper Some : IFoo {
K>   foo = dummy;
K>   bar = mummy;
K>};

K>void test( Some &s )
K>{
K>   IFoo &foo = s; // компилер заюзает то что написано во wrapper Some
K>   foo.foo();
K>}

K>

Хм. Неплохая идея. Вполне может прижиться, учитывая зоопарк компонентов. Приходилось мне заниматься подобной ерундой, где как правило при использовании любой сторонней библиотеки первым делом писались врапперы для основных классов.
Вот такие прокси, как мы с тобой пообсуждали вполне можно генерить автоматически. Вот только как сделать нормальный язык описания маппинга? Тут ты привел пример совпадения сигнатур. А если они не совпадают, как в примере с Node / INode ?
Node.get(a, b) <-> a=INode.get(b)

... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: "Включение" vs "Наследование" интерфейсов
От: borisman2 Киргизия  
Дата: 01.10.04 04:14
Оценка: 20 (2)
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>"Включение" vs "Наследование" интерфейсов


SYG>Хотелось бы узнать кто что думает по поводу замены механизма наследования интерфейсов на механизм включения. Что такое наследование интерфейсов, думаю, всем понятно.


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

Однако, обратимся к самой ИДЕЕ интерфеса, то есть ПРОТОКОЛА между двумя объектами. Протокол — это, во-первых, некий язык, а во-вторых еще и дополнительные правила применения этого языка.

Я поясню свою мысль на примере протокола HTTP.
Языком в данном случае являются команды(запросы) серверу и ответы сервера. "GET", "POST", "HEAD", .... коды ответов 200, 404, 401, ... составляют словарь языка. Формат подачи этих команд, например
GET /index.htm HTTP.1/1
Это синтаксис языка.
Надеюсь, пока что я излагаю все понятно.

Кроме языка имеется также те самые дополнительные правила разговора, которые я упомянул выше. Например, невозможно сказать GET и затем сразу POST, не дожидаясь ответа сервера. Нужно сначала сказать GET, затем получить ответ, затем уже говорить POST. Если вы будете делать иначе, сервер вас "не поймет".

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

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

В более сложном случае объект, предоставляющий интерфейс ИМЕЕТ состояние. Тогда, как правило, нельзя вызывать функции беспорядочно, есть некий порядок, отражающий переходы состояний объекта. Примером може служить Интернет Эксплорер с его интерфейсами IWebBrowser. Нельзя, например, сначала попытаться отправить форму, а потом только загрузить страничку с этой формой. (Вернее, можно, но из этого ничего не выйдет). Еще более наглядный пример — IDirect3D. Вследствие того, что DirectX обрабатывает графические данные при помощи graphic pipeline, которую мы можем рассматривать как объект с очень сложным внутренним состоянием, имеется совершенно четкая схема работы с интерфесами DirectX.


ВЫВОДЫ.

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

С Уважением,
Пасько Борис,
программист
Программа Микро и Малого Финансирования ЕБРР
Re[20]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 01.10.04 08:10
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

S>Вот такие прокси, как мы с тобой пообсуждали вполне можно генерить автоматически. Вот только как сделать нормальный язык описания маппинга? Тут ты привел пример совпадения сигнатур. А если они не совпадают, как в примере с Node / INode ?
S>
S>Node.get(a, b) <-> a=INode.get(b)
S>

S>

Думаю что нормальный язык мапинга не сделать никак. Как нипример отмаппировать вызов функци где параметры передаются через структуру как в ВыньАПИ?
TVINSERTSTRUCT args;
args.hParent = ...;
args.hInsertAfter = ...;
args.item.mask = TVIF_IMAGE | TVIF_PARAM ...;
// еще куча всякой инициализации
TreeView_InsertItem(hwnd,&args);

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

// сигнатура в интерфейсе:
ITreeItem ITree::insert( string name, ITreeItem parent, ITreeItem after );

// сгенеренная враппер функция:
bool MyTreeWrp::insert( MyTree &This, ITreeItem *retval, string name, ITreeItem parent, ITreeItem after, ErrorInfo *e )
{
}
Re[21]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.10.04 09:24
Оценка:
Здравствуйте, Kluev, Вы писали:

K>Думаю что нормальный язык мапинга не сделать никак. Как нипример отмаппировать вызов функци где параметры передаются через структуру как в ВыньАПИ?

K>Язык мапинга может оказатся сложнеее чем ручной маппинг.
K>Думаю если генератор обертки будет генерить пустые функции то этого будет вполне достаточно:

K>
K>// сигнатура в интерфейсе:
K>ITreeItem ITree::insert( string name, ITreeItem parent, ITreeItem after );

K>// сгенеренная враппер функция:
K>bool MyTreeWrp::insert( MyTree &This, ITreeItem *retval, string name, ITreeItem parent, ITreeItem after, ErrorInfo *e )
K>{
K>}

K>

Я правильно понял, что ты описываешь дизайн-тайм маппинг? То есть всего лишь примитивный тул, легко реализуемый современными средствами (были бы в платформе механизмы интроспекции)?
Тогда можно просто сесть его и написать
Правда, непонятно, к чему ты упоминал "прямую поддержку языком". Пока что я не вижу ничего, что можно было бы так поддержать.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: "Включение" vs "Наследование" интерфейсов
От: Павел Леонов Россия icq: 138726397
Дата: 01.10.04 09:35
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>"Включение" vs "Наследование" интерфейсов


Используй статические методы, это позволит разгрузить типы. Допустим есть Тип XXX, к нему по крайней необходимости был добавлен метод ToInt(), ToArray(), Save().... и прочее. Сути, подразумеваемой под этим типом не изменилось, разве что добавился сервисный метод, однако формально это стал другой тип и пошло, поехало...

PS. Если это использовать, то выясниться, что не так уж много типов вообще нужно. ООП после процедурных языков — вещь полезная, только имхо по инерции произошел перекос.
Re[22]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 01.10.04 09:40
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Я правильно понял, что ты описываешь дизайн-тайм маппинг? То есть всего лишь примитивный тул, легко реализуемый современными средствами (были бы в платформе механизмы интроспекции)?

ИМХО и интроспекция не нужна. Что-то типа IDL будет в самый раз.

S>Тогда можно просто сесть его и написать

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

Например оператор wrapper_cast т.е. делаешь переходник для класса, а язык это дело отслеживает и возвращает нужный враппер (типа dynamic_cast).
Re[23]: "Включение" vs "Наследование" интерфейсов
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.10.04 10:16
Оценка:
Здравствуйте, Kluev, Вы писали:
K>Например оператор wrapper_cast т.е. делаешь переходник для класса, а язык это дело отслеживает и возвращает нужный враппер (типа dynamic_cast).
Ну, в плюсах, например, этого не надо. Т.к. мы и так иожем сделать правильный каст, если сгенеренный класс задекларирует соответствующий неявный конструктор.
Интересно, что борланд пытался сделать что-то подобное в D8 при помощи хелпер классов.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[24]: "Включение" vs "Наследование" интерфейсов
От: Kluev  
Дата: 01.10.04 10:36
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Kluev, Вы писали:

K>>Например оператор wrapper_cast т.е. делаешь переходник для класса, а язык это дело отслеживает и возвращает нужный враппер (типа dynamic_cast).
S>Ну, в плюсах, например, этого не надо. Т.к. мы и так иожем сделать правильный каст, если сгенеренный класс задекларирует соответствующий неявный конструктор.

В компайл тайме да. А рантайме уже нет.
т.е. вот такая фича:
void foo(INode n)
{
   ICoolNode cn = wrapper_cast<ICoolNode>(n);
}

Хотя наверное это и не очень нужно. Можно самому ручками прикуртить, только тады прийдется как-то регистрировать врапперы. Сам класс-то о них ничего не знает. Хотя я здесь не вижу никаких особых сложностей. Более того весь вспомогательный код может как раз сгенерить WDL-translator (wrapper description language . Кстатит он мне видится двухрежимным. В первом режиме принимает описание враппера и генерит интерфейсный хедер. Во втором интерфейс+конкретный класс — генерит оболочку для конкретно взятого класса.

S>Интересно, что борланд пытался сделать что-то подобное в D8 при помощи хелпер классов.

Может быть. Идея то здравая. Для плюсов еще хороша тем что можно обеспечить кросскомпилерную совместимость. Т.к. vtable не используюьтся и исключения кидаются в клиетском коде.
Re[19]: "Включение" vs "Наследование" интерфейсов
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 01.10.04 11:45
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну братан, ну ты даешь!


Да, я понял в чем тут слабое место...
Re[2]: "Включение" vs "Наследование" интерфейсов
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 01.10.04 11:47
Оценка:
Здравствуйте, borisman2, Вы писали:

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


Согласен с Вами. Думаю, на этом можно считать вопрос исчерпанным.
Re[7]: "Включение" vs "Наследование" интерфейсов
От: Larm Украина  
Дата: 01.10.04 13:48
Оценка:
Здравствуйте, S.Yu.Gubanov, Вы писали:

SYG>"3" — это число, то есть абстракция. "бензобак" — эта такая фиговина куда бензин наливают. Спрашивается, как понимать выражение "3=бензобак"?


Не хотелось бы переходить на личности, но математику в университете изучали? Или еще в школу ходим? Вопрос задан идиотский.
The God who walks is among us...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.