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&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&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>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.