Конференция "Начинающим" » Чем интерфейс отличается от класса?
 
  • Belkin © (04.01.17 14:17) [0]
    Собственно сабж (на примере Delphi)
  • Kerk © (04.01.17 14:22) [1]
    Интерфейс не содержит реализации.
  • stas © (04.01.17 14:23) [2]
    можно наследоваться от нескольких интерфейсов.
  • Belkin © (04.01.17 16:02) [3]

    > Kerk ©   (04.01.17 14:22) [1]
    >
    > Интерфейс не содержит реализации.


    А что это дает интерфейсу?
  • DayGaykin © (04.01.17 16:10) [4]
    Класс лучше интерфейса.
  • DVM © (04.01.17 17:04) [5]

    > Belkin ©   (04.01.17 16:02) [3]

    Интерфейсу это ничего не дает и давать не может, т.к интерфейс это лишь описание, а вот тому кто интерфейс использует или реализует при правильном применении может давать массу удобств:
    1) Подобие множественного наследования
    2) IoC
    3) Счетчик ссылок (в Delphi, не обязательно)
  • Юрий Зотов © (04.01.17 17:19) [6]
    > Belkin ©   (04.01.17 16:02) [3]

    > > Kerk ©   (04.01.17 14:22) [1]
    > > Интерфейс не содержит реализации.

    > А что это дает интерфейсу?


    interface

    type
     IMyInterface = Interface
        procedure Proc;
     end;

     TMyClass1 = class(TSomeClass, IMyInterface)
        procedure Proc;
     end;

     TMyClass2 = class(TAnotherClass, IMyInterface)
        procedure Proc;
     end;

    // TMyClass1 и TMyClass2 имеют разные ветви наследования

    procedure CallProc(intr: IMyInterface);

    implementation

    procedure TMyClass1.Proc;
    begin
     // Звонок другу
    end;

    procedure TMyClass2.Proc;
    begin
     // Печать на принтере в Австралии
    end;

    pocedure CallProc(intr: IMyInterface)
    begin
     intr.Proc // Код один и тот же, а результат - разный
    end;
  • Внук © (04.01.17 18:10) [7]
    Интерфейс - это контракт, который обязуется выполнять класс, который реализует этот интерфейс. То есть этот класс обязан предоставить реализацию методов, описанных в интерфейсе, либо делегировать реализацию этих методов другим классам.

    Это дает определенную гибкость при стыковке того кода, который использует интерфейс и того кода, который его реализует.

    Можно подойти и с другой стороны - это дает возможность заранее (до разработки кода, реализующего интерфейс) договориться о том, что от него вправе требовать тот, кто собирается использовать этот интерфейс.
  • картман © (04.01.17 19:13) [8]
    еще это дает возможность работать над одним и тем же количеством(по функциональности) кода в пять раз большему количеству программистов. Т.о. интерфейсы обеспечивают рабочими местами ит-шников. Справедливости ради, надо сказать, что интерфейсы в этом обеспечении лишь вершина айсберга.
  • Belkin © (04.01.17 20:02) [9]
    Спасибо всем за ответы, вроде стало понятно.


    > картман ©   (04.01.17 19:13) [8]
    >
    >  Справедливости ради, надо сказать, что интерфейсы в этом
    > обеспечении лишь вершина айсберга.


    А что еще?
  • Кто б сомневался © (05.01.17 00:34) [10]
    Основной его плюс - это то, что можно сделать закрытый код (например DLL, dcu или другой пакет), а интерфейс отдать другому программисту.
    И он будет знать как работать с этим классом\dll.


    > DVM ©   (04.01.17 17:04) [5]
    >
    > 3) Счетчик ссылок (в Delphi, не обязательно)


    Уже обязательно. Причем на моб. компиляторах счетчик ссылок уже на обычных классах (TObject). Т.е. TObject.Free на моб. компиляторах уже не работает, нужно или обнулять или FreeAndNil или DisposeOf (этот метод всегда 100% уничтожает класс, независимо от ref count).
  • Кто б сомневался © (05.01.17 00:42) [11]

    >  Т.е. TObject.Free на моб. компиляторах уже не работает


    Точнее он работает, только деструктор не вызывается, а просто обнуляется переменная и ref count уменьшается.  = FreeAndNil http://docwiki.embarcadero.com/RADStudio/Seattle/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers
  • DVM © (05.01.17 00:46) [12]

    > Кто б сомневался ©


    > Точнее он работает, только деструктор не вызывается

    и как же деструктор вызвать?
  • Кто б сомневался © (05.01.17 00:57) [13]
    DVM ©   (05.01.17 00:46) [12]
    DisposeOf
  • KSergey © (05.01.17 15:28) [14]
    > Юрий Зотов ©   (04.01.17 17:19) [6]

    Сколько читаю про интерфейсы - никак не могу взять в толк вот что: что изменится от того, если в вашем примере IMyInterface = Interface заменить на класс с виртуальным методом?  и сделать 2 класса, его наследующих?
    Ну ведь тоже самое абсолютно получится, так зачем интерфейсы приплетают? что они дают?
  • Внук © (05.01.17 17:11) [15]

    > Ну ведь тоже самое абсолютно получится, так зачем интерфейсы
    > приплетают? что они дают?


    Можно сказать, что интерфейс ничем не отличается от абстрактного класса в стиле С++. Но это будет не совсем правда по причинам, частично отмеченным выше:
    1. Возможность "наследовать" несколько интерфейсов одновременно
    2. Автоматическое управление временем жизни переменных, основанное на подсчете ссылок. С этим надо аккуратно. Как ни странно, вводимый для уменьшения ошибок работы с памятью подсчет ссылок их не сильно уменьшил, скорее заменил одни ошибки другими.
    И тем более, слово interface не всегда означает включение механизма подсчета этих самых ссылок.
    3. Возможность сопоставить с интерфейсом GUID позволяет уникально идентифицировать этот интерфейс по всему мировому программному коду, а также задействовать возможность получения через интерфейсную переменную другого интерфейса, реализуемого тем же классом.
    Ну и COM, конечно, который в том числе базируется на пунктах 2 и 3.
  • kilkennycat © (05.01.17 19:50) [16]
    Я тож непонимаю необходимости интерфейсов, учитывая, что я работаю один, какие-то заумные наследования в конечном продукте не предусматриваются в виду скромности этого продукта (работаю ведь один). А вышеприведенный пример с длл вообще мне непонятен, так как предпочитаю в длл засовывать функции.
  • Игорь Шевченко © (05.01.17 21:09) [17]

    > Ну ведь тоже самое абсолютно получится, так зачем интерфейсы
    > приплетают? что они дают?


    Удобно, знаете ли. А вообще в справке все написано, кто ее ленится читать, тому и на форуме не ответить.
  • stas © (05.01.17 21:18) [18]
    Возможно пример далекий, но наглядный.
    Возьмем телевизор у которого есть вход scart и rgb.
    Так вот телевизор это класс TTelevizor, а  входы это интерфейсы IScart, IRgb, которые содержат описание какой контакт где находится и форма самого разъема.
    Так вот класс TTelevizor наследуется от TTelevizorBase и реализует интерфейсы IScart и IRgb.
    Как он их реализует это его дело, но есть договоренность что в итоге он должен из полученного сигнала показать изображение.
    При этом все понимают что если у него есть RGB вход значит, на этот вход можно подключить видеоплеер и получить картинку.
  • stas © (05.01.17 21:21) [19]
    А телевизор, который на 100 баксов дешевле реализует всего 1 интерфейс, а наследуется от того же базового класса.
  • KSergey © (06.01.17 09:45) [20]
    > Игорь Шевченко ©   (05.01.17 21:09) [17]
    > А вообще в справке все написано, кто
    > ее ленится читать, тому и на форуме не ответить.

    Умник Игорь, который прочитал все справки.
    Скажи, что в них написано за пределами пунктов 1..3 поста, предшествующего твоему очень полезному?

    > Удобно, знаете ли

    Вот бы ты сумел привести при мер удобства.
    Но не трудись, я знаю, что ты не сумеешь.
  • Rouse_ © (06.01.17 10:11) [21]

    > KSergey ©   (05.01.17 15:28) [14]
    > Ну ведь тоже самое абсолютно получится, так зачем интерфейсы
    > приплетают? что они дают?

    К примеру, не знаю, почему никто не упомянул - реализация кода может быть совершенно не в твоей программе, к которой ты можешь обращаться через интерфейс.
    IShellXXX и иже с ним. Тут уже абстрактными классами не обойтись.
    В своем софте мы используем (опять-же как пример) для разруливания горизонтальных связей между несвязанными библиотеками и как расширение функционала базовых модулей (очень удобно кстати получается).
  • Rouse_ © (06.01.17 10:31) [22]
    Я опробую еще немного шире обьяснить.
    Вот смотри есть у нас некие наборы данных - они пошифрованы, их декрипт происходит одинаково, не важно INI файл, XML или еще что-то наше внутреннее.

    Потом есть к примеру два класса которые читают XML - один читает как есть, а второй должен читать его из криптоконтейнета.

    Второму мы объявляем интерфейс, к примеру IDecrypt и на перекрытом методе DoLoad просто вызываем метод Decrypt интерфейса.

    Таким образом у нас нет лишнего копипаста кода - а работать начинает уже с двумя наборами данных на одном и том-же механизме.
  • Rouse_ © (06.01.17 10:31) [23]
    И опять-же тут уже абстрактный класс никак не поможет.
  • Игорь Шевченко © (06.01.17 10:39) [24]
    KSergey ©   (06.01.17 09:45) [20]

    Это все платные услуги, дружище.
  • ухты_х © (06.01.17 10:46) [25]

    > К примеру, не знаю, почему никто не упомянул - реализация
    > кода может быть совершенно не в твоей программе,
    про СОМ упомянули, к примеру )
  • Rouse_ © (06.01.17 10:58) [26]

    > ухты_х ©   (06.01.17 10:46) [25]
    > про СОМ упомянули, к примеру )

    Да, действительно - немножко пропустил читая, сори :)
  • KSergey © (06.01.17 11:20) [27]
    > Внук ©   (05.01.17 17:11) [15]
    > 2. Автоматическое управление временем жизни переменных,
    > основанное на подсчете ссылок.
    .....
    > И тем более, слово interface не всегда означает включение
    > механизма подсчета этих самых ссылок.

    Вот-вот. В первых строках все пишут про подсчет ссылок. Но дальнейшее чтение сотен страниц столько нюансов внезапно выявляет - что "нафик-нафик" это ваше волшебство.
    Это так, не к вам лично. Впечатление от всех эти интерфейсов.

    Хорошо, взаимодействие нескольких DLL одного процесса.
    Может тут в самом деле удастся найти много пользы?

    Вот есть такого плана проблема: имеем DLL, она должна вернуть список структур. Список - переменной длины. (т.е. грубо говоря нужно вернуть массив переменной длины, каждый элемент - некая структура.)
    Если делать обычным интерфейсом "на функциях", то получается довольно коряво одним из 2-х вариантов:
    1.
    а) Делаем функцию, возвращающую количество элементов, которые хотим вернуть
    б) Далее вызывающий модуль выделяет память и передаёт указатель на выделенный блок памяти во вторую функцию, которая уже заполняет переданный ей буфер и возвращает количество фактически заполненных элементов.
    Пункты а) и б) можно как-то объединять в одну функцию, но это уже детали. Основная проблема в том, что фактически приходится одно и тоже действие (иногда сложное) выполнять фактически дважды, чтобы выходной буфер сформировать.
    2.
    Делаем функцию, которая внутри себя выделяет нужный размер памяти, заполняет и возвращает указатель на неё.
    И делаем функцию, которая освобождает выделенную память в надежде, что пользователь нашей DLL не забудет вызвать освобождение памяти.

    Вот как бы это всё сделать более удобно и волшебно в использовании?

    Да, это всё не про Delphi. В том смысле, что нужен универсальный интерфейс, от того и разговор про буферы памяти, а не динамические массивы.
  • KSergey © (06.01.17 11:23) [28]
    > Игорь Шевченко ©   (06.01.17 10:39) [24]
    > Это все платные услуги, дружище.

    И поэтому бесплатно ты умничаешь и всё? зачем? чешется?
    Молодец. Продолжай в том же духе, всё одно проку с тебя ноль.
  • Rouse_ © (06.01.17 11:25) [29]

    > б) Далее вызывающий модуль выделяет память и передаёт указатель
    > на выделенный блок памяти во вторую функцию

    Ну это один из вариантов, к примеру NetAPI - там память выделяет уже вызываемая библиотека, только я тут не вижу никакой связи с интерфейсами
  • KSergey © (06.01.17 11:26) [30]
    > ухты_х ©   (06.01.17 10:46) [25]
    > про СОМ упомянули, к примеру )

    Про СОМ понятно.
    Когда я хочу задействовать этот механизм по каким-то причинам - то вариантов нет: интерфейсы и всё такое.
    Но интерфейсы существуют и вне СОМ механизма, вот и пытаюсь понять куда их приткнуть. Не зря же Тенцер пишет такие толстые книжки, наверное, вот только так и не нашел я куда всё это приткнуть за несколько лет по прочтении.
  • KSergey © (06.01.17 11:27) [31]
    > Rouse_ ©   (06.01.17 11:25) [29]
    > > б) Далее вызывающий модуль выделяет память и передаёт
    > указатель
    > > на выделенный блок памяти во вторую функцию
    >
    > Ну это один из вариантов, к примеру NetAPI - там память
    > выделяет уже вызываемая библиотека, только я тут не вижу никакой связи с интерфейсами

    Скорее всего связи и нет, просто надежда: вдруг умные слова как-то помогут.
  • KSergey © (06.01.17 11:28) [32]
    >Rouse_ ©   (06.01.17 10:11) [21]
    >Rouse_ ©   (06.01.17 10:31) [22]


    Спасибо, попробую поразмыслить.
  • Внук © (06.01.17 20:06) [33]

    > KSergey ©

    Если говорить о пользе, она, конечно, для всех разная. И когда кто-то пишет, что ему интерфейсы нафиг не нужны, я верю.

    Что касается той пользы, которую увидел лично я (если не брать в расчет разработку кода в коллективе, где интерфейсы служат именно что контрактами), то я назову тестирование. Точнее, написание автоматизированных тестов. Интерфейсы позволяют писать тесты ДО написания тестируемого кода, не прибегая к заглушкам. Не говоря о том, что такой подход к тестам является православным, это на ранних стадиях позволяет увидеть косяки архитектуры и дешево их исправить.
  • Германн © (07.01.17 02:11) [34]

    > KSergey ©   (06.01.17 09:45) [20]
    >
    > > Игорь Шевченко ©   (05.01.17 21:09) [17]
    > > А вообще в справке все написано, кто
    > > ее ленится читать, тому и на форуме не ответить.

    Тут есть сложность с определениями что такое форум по Дельфи, чем он может быть полезен, и нафига он вообще нужен?
    ИШ об этом молчит.
  • Юрий Зотов © (07.01.17 11:28) [35]
    > KSergey ©   (05.01.17 15:28) [14]

    > Юрий Зотов ©   (04.01.17 17:19) [6]
    >
    > Сколько читаю про интерфейсы - никак не могу взять в толк
    > вот что: что изменится от того, если в вашем примере IMyInterface
    > = Interface заменить на класс с виртуальным методом?  и
    > сделать 2 класса, его наследующих?
    > Ну ведь тоже самое абсолютно получится, так зачем интерфейсы
    > приплетают? что они дают?


    Вся фишка в том, что эти два класса могут иметь совершенно разные ветви наследования. Например:

    TMyClass1 = class(TGraphicControl, IMyInterface)
    TMyClass2 = class(TDataset, IMyInterface)

    С интерфейсами все просто - в интерфейсе декларируем метод Proc и реализуем этот метод в обоих классах.

    А без интерфейсов возникает проблема - где взять общего предка для этих двух классов, чтобы в этом предке объявить абстрактный метод Proc ?
  • KSergey © (10.01.17 13:44) [36]
    Юрий Зотов ©   (07.01.17 11:28) [35]
    Внук ©   (06.01.17 20:06) [33]

    Спасибо, интересны в самом деле именно реальные примеры.
    Буду думать куда бы всё же приткнуть хоть раз в жизни, а то завидно.
  • stas © (10.01.17 14:12) [37]

    > Юрий Зотов ©   (07.01.17 11:28) [35]


    Немного добавлю.
    И есть еще
    TMySuperClass = class (TObject)



    у него метод:
    procedure MySuperProcedure (obj:IMyInterface)
    begin
       obj.Proc()
    end



    Этому классу ничего не нужно знать о TGraphicControl, TDataset, а только о интерфейсе.
    Но в метод примет и правильно обработает классы TMyClass1, TMyClass2
  • KSergey © (11.01.17 14:36) [38]
    > stas ©   (10.01.17 14:12) [37]

    А вот это толково, кстати.
    В самом деле: наследования вынуждают все пихать в единое дерево с общими предками, что бывает противоестественно, а тут всё как надо делается.

    Спасибо. Проникся.
  • DVM © (11.01.17 15:04) [39]

    > KSergey ©   (11.01.17 14:36) [38]
    > > stas ©   (10.01.17 14:12) [37]
    >
    > А вот это толково, кстати.

    Это и есть IoC отчасти. https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8F_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F
 
Конференция "Начинающим" » Чем интерфейс отличается от класса?
Есть новые Нет новых   [118232   +39][b:0.001][p:0.002]