![]() |
Модераторы: Poseidon, Snowy, bems, MetalFan |
![]() ![]() ![]() |
|
Poseidon |
|
||||||||||||||||||||||||||||||
![]() Delphi developer ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 5273 Регистрация: 4.2.2005 Где: Гомель, Беларусь Репутация: 53 Всего: 133 |
Часть 1: Объявления, реализация и разрешение имен методов
Я впервые написал об интерфейсах в апрельском выпуске Delphi Informant Magazine за 1998 год. Почти за два года произошло несколько важных событий. Первое - в Delphi 4 предоставлен новый и важный способ реализации интерфейса в классе. Второе, и возможно более важное - интерфейсы быстро выходят за рамки своего первоначального назначения: естественной поддержки COM (Компонентная Объектная Модель фирмы Майкрософт). В результате, видимо, пришло время для пересмотра этой важной темы. Но только, что является интерфейсом и почему он так важен? Интерфейс - это определение методов и свойств, которые могут быть реализованы классом. Интерфейс предусматривает совместимость по назначению (часто называемую полиморфизмом) между различными объектами, которые реализуют общий интерфейс. Еще важнее, что интерфейсы обеспечивают полиморфизм без ограничений, обычно связываемых с полиморфизмом через наследование. Действительно, хотя два класса не наследуют методы от общего предка, к ним можно обращаться полиморфно в отношении метода, если этот метод определен для интерфейса обоих классов реализации. С другой стороны, интерфейсы позволяют определять свои методы и свойства абсолютно независимо от объекта реализации. Эта статья начинается с обзора, почему интерфейсы важны в объектном Паскале, и продолжается детальным обсуждением характеристик интерфейсов. Это обсуждение включает декларацию интерфейсов, требования к их реализации, и разрешение имен методов. Во второй части этой статьи будет продолжение с подробным примером использования интерфейсов, включая использование интерфейса делегированием, предоставленного в Delphi 4. Почему в Delphi добавлены интерфейсы? Легкий ответ - чтобы обеспечить естественную поддержку COM. Однако, поддержка COM, предусмотренная в интерфейсах - побочный продукт их функциональности. Действительно, интерфейсы обеспечивают элегантный механизм для полиморфного обращения к объектам, даже когда у них нет общих предков. Эту концепцию трудно описать, но пример поможет ее проиллюстрировать. Рассмотрим ситуацию, когда Вы хотите создать группу компонент пользовательского интерфейса (UI), которые разделяют общие способности, такие как способность загрузки строковых данных из файла ресурсов. Кроме того, Вы можете захотеть предоставить новый метод, названный LoadStrings, в каждом из Ваших новых UI компонентов, и в реализации этого метода загрузить строки этих компонент из ресурсного файла, возможно основываясь на свойстве компонента Tag. Пока все хорошо. Однако, другой аспект этой структуры - возможность инициирования процесса загрузки для каждого из Ваших компонент общей командой. В Delphi 1 и 2, где интерфейсы еще не были предоставлены, это было почти невозможно. Рассмотрим псевдокод на рисунке 1 (который будет компилироваться).
Хотя этот код компилируется, невозможно вызвать метод LoadStrings этих объектов полиморфным способом. Например, следующий метод не компилируется:
Проблема в том, что хотя LoadStrings может быть законным методом определенного экземпляра TControl, к которому Вы применяете DoLoadStrings, это не метод класса TControl. Только, если LoadStrings будет публичным или опубликованным методом класса TControl, этот код будет правильно компилироваться. Подход со множественным наследованием Если бы Delphi поддерживала множественное наследование, эта проблема была бы решена довольно легко. Действительно, Вы могли бы создать другой класс (назовем его TLoadable) который включал бы абстрактный виртуальный метод LoadStrings. Тогда, при объявлении каждого из Ваших UI компонент, Вы могли бы объявить их наследниками от обоих естественных предков (TNewButton от TButton и TNewLabel от TLabel) и TLoadable. Псевдокод, показанный на рисунке 2 демонстрирует как это могло бы выглядеть.
Если бы Delphi поддерживал множественное наследование, Вы могли бы создать другой класс (TLoadable), который включал бы абстрактный виртуальный метод LoadStrings. Теперь все, что Вы должны были бы сделать - изменить описание DoLoadStrings, чтобы оно было похоже на следующее:
Поскольку этот измененный метод получает объект TLoadable как параметр, и поскольку объекты TLoadable имеют видимый метод LoadStrings, все должно работать прекрасно. Единственная проблема состоит в том, что Delphi не поддерживает множественное наследование, и, следовательно, этот код нельзя откомпилировать. Решение: Интерфейсы Оно стало возможным когда появились интерфейсы. Интерфейс это объявление, мало чем отличающееся от абстрактного виртуального класса Loadable, показанного на рисунке 2. Кроме того, интерфейс может использоваться двумя и более классами, чтобы сделать эти классы совместимыми по назначению, таким же образом, которым наследование предусматривает полиморфизм. Рассмотрим пример кода, показанный на рисунке 3. Этот код включает одно объявление интерфейса, названного ILoadable, и объявления двух классов. Каждый из этих классов реализует интерфейс ILoadable.
Этот код включает одно объявление интерфейса и объявления двух классов. На языке интерфейсов мы говорим, что оба класса TNewButton и TNewLabel реализуют интерфейс ILoadable. Кроме того, поскольку оба этих класса реализуют общий интерфейс, они совместимы по назначению с интерфейсной ссылкой. Поэтому, мы можем заставить эту структуру работать обобщенно, изменив тип параметра в нашем методе DoLoadStrings на ILoadable. Следующий сегмент кода демонстрирует как выглядит законченный метод:
Поскольку DoLoadStrings может получать ILoadable объект, следующий фрагмент кода полностью законен:
Декларация интерфейсов в объектном Паскале Как Вы можете видеть в коде предыдущего примера, интерфейс объявленный в декларации типов, очень похож на класс. Однако декларация интерфейса отличается от декларации класса в нескольких важных вещах. Например, интерфейс состоит только из объявлений методов и свойств. В интерфейсе нет полей члена класса. Поля члена класса используются для хранения данных в экземпляре класса. Интерфейсы, в отличие от классов, никогда не могут быть экземплярами. Следовательно, интерфейс не может содержать данных, и из-за этого не может иметь объявления полей. Так как интерфейсы не имеют полей, существуют ограничения, в объявлении их свойств. Действительно, в то время как класс может осуществлять чтение и запись свойств, используя прямой подступ, интерфейсы могут использовать только методы. Определенно, когда свойство объявлено в интерфейсе, чтение свойства описывается с использованием метода-функции, а запись свойства - с использованием метода-процедуры. Следующий пример - простая декларация интерфейса, которая содержит метод ShowMessage и свойство MessageText. Два других метода GetMessageText и SetMessageText, также рассматривается как методы интерфейса:
Для сравнения, декларация класса может описывать поля членов в частях read и write. Это называется прямым доступом, потому что объект, читая свойство, будет читать непосредственно из поля, и любой объект, пишущий в свойство, будет писать непосредственно в поле. Все методы в декларации интерфейса рассматриваются как виртуальные и абстрактные по определению. Следовательно, в декларации интерфейса никогда не используются директивы virtual и abstract. Кроме того, в декларации интерфейса нет идентификаторов видимости (public, published и т. д.). Со всеми методам и свойствами, объявленными в интерфейсе, обращаются как с public, хотя класс, реализующий методы, может использовать идентификаторы видимости для управления их видимостью в классе. Класс реализует интерфейс включением имени интерфейса в круглые скобки, которые следуют за ключевым словом class в декларации его типа. Имя интерфейса отделяется от имени предка класса запятой. Кроме того, один класс может реализовывать два и более интерфейсов. Когда реализуется больше, чем один интерфейс, Вы перечисляете эти интерфейсы в списке, разделяя их запятой, после имени класса предка. Реализация методов интерфейса Класс, который реализует интерфейс, нужен, чтобы обеспечить реализацией каждый метод, объявленный в интерфейсе. Реализация может быть обеспечена или явным объявлением или в соответствии с наследованием от предка класса. Например, если класс TNewLabel объявлен для реализации интерфейса ILoadable и в этом интерфейсе объявлен один метод, названный LoadStrings, то класс TNewLabel должен или наследовать метод с соответствующим описанием по имени LoadStrings, или этот метод должен быть явно объявлен в классе и реализован. Кроме того, если унаследованный метод LoadStrings - абстрактный, то должна быть предусмотрена реализация этого метода в реализации класса TNewLabel. Хотя класс, который реализует интерфейс, должен реализовать все методы, объявленные в этом интерфейсе, не требуется, чтобы он реализовал все свойства интерфейса, или хотя бы одно из этих свойств. Например, в интерфейсе IShowMessage, описанном ранее, класс, реализующий IShowMessage, не обязан иметь свойство MessageText. Это место может смущать, но имеет большой смысл. Свойство интерфейса принадлежит интерфейсу. Содержит ли реализующий объект свойство не имеет значения. Фактически, (чтобы усугубить вопрос) объект, который реализует интерфейс, может иметь свойство, которое имеет такое же название как и свойство, объявленное в интерфейсе, и все же свойство интерфейса и свойство объекта могут быть абсолютно несвязанны. Компилятор может сообщить к которому из свойств обращаются, основываясь на том, является ли квалификатор свойства интерфейсной ссылкой (в этом случае вызываются доступные методы интерфейса) или объектной ссылкой (в этом случае, независимо от механизма используемого объектом для реализации свойства, используется объект). Иерархия интерфейсов Интерфейсы организованы в иерархию, очень похожую на иерархию классов Delphi. В Delphi, все интерфейсы, за исключением IUnknown, наследуются от существующего интерфейса. IUnknown- интерфейс самого высокого уровня, это значит, что все интерфейсы обязательно наследуются от него. Когда Вы видите объявление интерфейса, в котором не определен предок (подобно интерфейсу IShowMessage, объявленному в коде предыдущего примера), интерфейс наследуется от IUnknown. Интерфейсы, которые наследуются от другого интерфейса, чем IUnknown, содержат название интерфейса предка в своем объявлении типа. Это демонстрируется в следующем объявлении интерфейса: type INewInterface = interface(IDispatch) ... Когда класс реализует интерфейс, он ответственен за реализацию не только всех методов этого интерфейса, но и всех методов всех предков интерфейса. Рассмотрим следующее объявление, которое содержит объявление интерфейса IUnknown в объектном Паскале.
Зная, что IShowMessage наследуется от IUnknown, любой класс, который реализует IShowMessage, должен реализовать не только три метода IShowMessage, но также и три метода IUnknown. К счастью, как описано ранее в этой статье, эта реализация может быть унаследована. Например, класс TComponent реализует методы QueryInterface, _AddRef, and _Release. В результате, любой объект, который наследуется от TComponent, может реализовать IShowMessage, объявляя и реализуя только три метода IShowMessage. Реализация методов IUnknown обеспечивается наследованием. Интерфейсные ссылки Интерфейсная ссылка указывает на экземпляр объекта, который реализует интерфейс. Интерфейсная ссылка может быть переменной, формальным параметром, свойством объекта, и даже значением, возвращаемым из функции. В процедуре DoLoadStrings, описанной ранее, интерфейсная ссылка была формальным параметром (LoadableObject). Интерфейсная ссылка используется для вызова методов интерфейса, а также для чтения и/или записи свойств интерфейса. Во время выполнения, вызывается конкретный метод (или метод доступа к свойству) объекта, на который указывает ссылка. Например, передача объекта TNewButton в DoLoadStrings, приводит к вызову конкретной реализации LoadStrings, определенной в классе TNewLabel. Для сравнения, передача TNewLabel в DoLoadStrings приводит к поведению, определенному в TNewLabel. Однако, интерфейсная ссылка ограничена работой только с теми свойствами и методами, которые определены в интерфейсе. К любым методам, свойствам или полям объекта, которые не являются частью определения интерфейса, нельзя обращаться через интерфейсную ссылку. Снова обсудим метод DoLoadStrings. Хотя Вы можете передавать в этот метод любые объекты, реализующие ILoadable, Вы можете использовать формальный параметр LoadableObject только для вызова методов ILoadable. Действительно, даже притом, что Вы можете передавать экземпляр TNewButton в DoLoadStrings, компилятор не разрешит обращаться к свойству Enabled класса TNewButton из LoadableObject - или к любому другому свойству этого класса. Могут быть вызваны только методы LoadStrings, QueryInterface, _AddRef и _Release, потому что только они являются методами интерфейса ILoadable. (Помните, что ILoadable наследуется от IUnknown по определению). Еще одно дополнение относительно интерфейсных ссылок состоит в правиле: Вы не можете преобразовывать интерфейсную ссылку в объект. Например, Вы не можете преобразовать LoadableObject в TNewButton. Разрешение имен методов интерфейса Как Вы уже знаете, любому объекту, который реализует интерфейс, необходимо реализовывать методы, определенные в этом интерфейсе. Это может составить проблему, если в интерфейсе объявлен метод, чье имя уже используется объектом, который должен реализовать интерфейс. Например, представим, что есть класс по имени TMessageObject, который должен реализовать интерфейс IShowMessage, но он наследует метод ShowMessage от предка. Проблема, если унаследованный метод концептуально отличается от интерфейсного метода с тем же именем. В этом случае, необходимо реализовать метод, соответствующий методу интерфейса, и он должен отличаться от унаследованного метода. Delphi обеспечивает отображение методов интерфейса на реализующие методы с другим именем. Например, рассмотрим следующее объявление TMessageObject:
Это объявление описывает, что TMessageObject наследуется от TParentMessageObject, и реализует IShowMessage. Поскольку TMessageObject уже имеет метод ShowMessage (наследованием), есть необходимость отобразить метод IShowMessage.ShowMessage на метод с другим именем, которое в этом случае является DisplayMessage. При использовании разрешения метода интерфейса, чтобы отобразить метод интерфейса на новое имя метода - метод интерфейса и метод на который он отображается должны иметь одинаковый список параметров и одинаковое возвращаемое значение. Учитывая предшествующее объявление типа, если вызывается метод ShowMessage экземпляра TMessageObject, с использованием объектной ссылки с типом TMessageObject, выполнится наследуемый метод ShowMessage. Однако, если экземпляр TMessageObject назначен переменной IShowMessage, и вызывается метод ShowMessage, выполнится метод DisplayMessage. Заключение Обсуждение интерфейсов будет продолжено во второй части этой серии. Вторая часть начнется с рассмотрения того, как сильно отличается использование объектов с интерфейсными ссылками от стандартного использования объектов. Вы также найдете детальный обзор реализации интерфейсов делегированием, представленным в Delphi 4. Cary Jensen is President of Jensen Data Systems, Inc., a Houston-based database development company. He is co-author of 17 books, including Oracle JDeveloper [Oracle Press, 1998], JBuilder Essentials [Osborne/McGraw-Hill, 1998], and Delphi in Depth [Osborne/McGraw-Hill, 1996]. He is a Contributing Editor of Delphi Informant Magazine, and an internationally respected trainer of Delphi and Java. For more information, visit http://www.delphizine.com/include/Click_Re...atasystems.com/, or e-mail Cary at mailto:[email protected]. By Cary Jensen, Ph.D. Перевел Мотов Олег mailto:[email protected] http://olegmotov.h1.ru --------------------------------------------------------------------------------- Ревизия интерфейсов Часть 2: Интерфейсные ссылки против объектных ссылок Как Вы помните из первого выпуска этой серии статей, интерфейсы - это объявления методов и свойств, которые могут быть реализованы в классе. Основное достоинство интерфейсов состоит в том, что они допускают совместимость по назначению между двумя и более объектами, которые реализуют общий интерфейс. Другими словами, интерфейсы разрешают двум объектам быть обработанными полиморфно в отношении методов и свойств интерфейса, даже притом, что эти объекты не наследуют методы и свойства от общего предка. В этом отношении, интерфейсы обеспечивают ключевую (критическую) поддержку полиморфизма в языках типа Object Pascal, которые не поддерживают множественное наследование. Кроме того, с точки зрения объектно-ориентированного дизайна, интерфейсы обеспечивают элегантный способ определить поведение объекта независимо от его реализации. В первой части я описал основные правила декларации интерфейсов и реализации классов. В этой части мы рассмотрим использование объектов через интерфейсные ссылки, и покажем как это использование отличается от обычного использования объектов. Я также опишу дополнительную поддержку реализации интерфейсов, которая была добавлена в Delphi 4. Эта поддержка, называемая реализация интерфейсов делегированием, завершает модель интерфейсов Delphi. Это статья заканчивается обсуждением двух типов реализации интерфейсов делегированием, где я докажу, что только одна форма реализации интерфейсов делегированием безопасна в использовании, тогда как другой свойственна опасность. Интерфейсные ссылки против объектных ссылок В использовании интерфейсных и объектных ссылок есть существенные различия. Самое большое связано с управлением жизненным циклом объектов. Действительно, обращение к объектам через интерфейс происходит с подсчетом ссылок. Когда Вы присваиваете объект интерфейсной ссылке, неявно вызывается метод _AddRef. Точно так же, когда интерфейсная ссылка освобождается, вызывается метод объекта _Release. Все объекты, реализующие интерфейс, должны реализовывать методы _AddRef и _Release. Это необходимо, потому что все интерфейсы в конечном счете наследуются от IUnknown, и (как обсуждалось в первой части), любой объект, который реализует интерфейс, должен также реализовать все методы, объявленные, в предках интерфейса. В реализации метода _AddRef, объект должен увеличить внутренний счетчик ссылок. В методе _Release, этот счетчик ссылок уменьшается. Кроме того, в методе _Release объект должен явно уничтожаться, если счетчик ссылок уменьшится до нуля. Пример реализации такого типа показан на методах интерфейса IUnknown, которые реализованы в классе TInterfacedObject, показан на рисунке 1.
Рисунок 1: Методы IUnknown, реализованные классом TInterfacedObject. Компилятор автоматически генерирует необходимый вызов QueryInterface при назначении объекта, реализующего интерфейс, интерфейсной ссылке. Как показано на рисунке 1, TInterfacedObject.QueryInterface вызывает GetInterface, который является методом класса TObject. GetInterface вызывает метод _AddRef объекта, у которого запрашивается интерфейс. Компилятор также генерирует вызовы _Release. Есть три ситуации, в которых компилятор генерирует обращения к _Release: · Интерфейсной ссылке присваивается значение nil. · Интерфейсной ссылке переназначается другой объект, реализующий интерфейс. · Интерфейсная ссылка выходит из области видимости. Как Вы можете видеть на рисунке 1, когда вызывается метод _Release и счетчик ссылок уменьшается до нуля, автоматически вызывается деструктор объекта. Такое поведение, которое происходит только при использовании интерфейсных ссылок, существенно отличается от поведения, наблюдаемого при использовании объектных ссылок. При использовании объектных ссылок, жизненный цикл объектов управляется одним из двух способов: или Вы полагаетесь на сборку "мусора" класса TComponent, в котором Владелец(Owner) объекта (назначенный в методе TComonent.Create) уничтожает все существующие объекты, которыми он владеет (то есть те объекты, на которые ссылается его свойство-массив Components) как часть своего собственного процесса уничтожения; или Вы явно вызываете метод Free (или Release для экземпляра TForm). Если объектная ссылка не потомок TComponent (или если у объекта нет собственника пр. пер.), то Вы должны явно вызвать метод Free для освобождения объекта. Различия в управлении жизненным циклом приводят к существенно разному коду, в зависимости от того, используете ли Вы интерфейсные или объектные ссылки. Эти различия можно найти в примере приложения DEMOINT (доступного для загрузки, для подробностей смотрите конец статьи). Возможно Вы захотите загрузить этот файл и рассмотреть весь модуль главной формы. Для краткости, я в данное время сосредоточусь только на части этого файла. Сначала рассмотрим декларацию одного интерфейса и двух классов, которые реализуют этот интерфейс. Для ясности, я сохранил декларацию этого интерфейса и классы его реализации очень простой, как показано на Рисунке 2.
Рисунок 2: Декларация интерфейса и двух классов, которые его реализуют. Первая вещь, на которую Вы обратите внимание, состоит в том, что интерфейс IShowMessage связан с большим числом. На это число ссылаются как на глобальный уникальный идентификатор интерфейса, или сокращенно GUID (произносится как гуид). Это 128-битный число, сгенерированное вызовом API-функции Windows, которая гарантирует, что это число абсолютно уникально, даже среди различных компьютеров. Цель GUID - уникально идентифицировать интерфейс, когда он зарегистрирован для использования COM (объектная модель программных компонентов) в системном реестре Windows. Object Pascal не требует, чтобы Вы генерировали и регистрировали GUID для интерфейсов, которые вы создаете. Однако, в том случае, если Вы решите использовать интерфейс как интерфейс COM когда-нибудь в будущем, не повредит назначить интерфейсу GUID. И редактор Delphi делает генерацию GUID очень легким. Чтобы вставить GUID в исходный код, просто нажмите [Ctrl][Shift][G]. Остальная часть декларации интерфейса довольно проста. В ней объявляется одно свойство, названное MessageText, и три метода. Два из этих методов - методы доступа к свойству. Как Вы знаете из первой части этой серии, интерфейсы не реализуют методы. Это ответственность классов, которые реализуют интерфейс. На рисунке 2, классы TYesDefault и TNoDefault реализуют интерфейс IShowMessage. Поскольку ни TYesDefault ни TNoDefault не наследуют методы интерфейса IShowMessage, в этих классах должны быть явно объявлены и реализованы эти методы. Обратите внимание, что свойство MessageText не объявлено ни в TYesDefault ни в TNoDefault. Как упоминалось в первой части, классы, реализующие интерфейс, не требуют явного объявления свойств интерфейса. Это действие абсолютно необязательно, и я посчитал его необязательным и в нашем случае. Однако, поскольку эти классы должны реализовать методы доступа к свойству GetMessageText и SetMessageText, и эти методы требуют сохранения текста сообщения, было необходимым объявить поле, названное FMessageText, чтобы хранить содержимое сообщения в объектах класса. Хотя эти два класса выглядят почти одинаково, они различны. Различие можно найти в реализациях метода ShowMessage. Рисунок 3 показывает реализацию этих классов. Я также перекрыл деструкторы этих классов. Это было сделано для того, чтобы можно было увидеть результат управления жизненным циклом и сравнить как оно отличается между интерфейсными и объектными ссылками.
Рисунок 3: Реализация TYesDefault и TNoDefault. Как упоминалось ранее, единственное реальное различие между реализациями этих классов может быть обнаружено в методе ShowMessage. Когда вызывается метод TYesDefault.ShowMessage, отображается диалоговое окно с заданной по умолчанию кнопкой OK. Для сравнения, когда вызывается TNoDefault.ShowMessage, заданная по умолчанию - кнопка Cancel. Это единственное различие позволяет нам показать, какая из реализаций интерфейса вызывается, отметив, которая из кнопок на отображенном диалоговом окне является основной. Давайте теперь обратим наше внимание на использование этих двух объектов. Основная форма DEMOINT содержит четыре кнопки (см. рисунок 4). Начнем с первых двух - помеченных Use Objects и Use Interfaces. На рисунке 5 - обработчик события OnClick, связанный с кнопкой Use Objects. Основная форма проекта DEMOINT.
Рисунок 5: Обработчик события OnClick для кнопки Use Objects. Здесь мы видим традиционный способ использования экземпляров класса. Вызывается конструктор класса, вызываются методы экземпляра класса, и затем он освобождается. Обратите внимание, что мы используем методы доступа, чтобы установить текст сообщения диалогового окна. Мы не можем использовать свойство MessageText в этом случае, потому что оно не является свойством классов TYesDefault или TNoDefault. Давайте сравним этот код с кодом, связанным с кнопкой помеченной Use Interfaces. На рисунке 6 показан код обработчика события OnClick, связанного с этой кнопкой.
Рисунок 6: Обработчик события OnClick для кнопки Use Interfaces. Здесь мы используем переменную типа IShowMessage - интерфейсную ссылку. Кроме того, мы можем использовать свойство MessageText, чтобы установить текст сообщения диалогового окна, потому что у интерфейса IShowMessage есть это свойство. Кроме этих двух различий, есть два других важных различия между этой и предыдущей частями кода. Первое - для ссылки на два экземпляра объектов используется одна переменная типа IShowMessage. Другими словами, экземпляры классов TYesDefault и TNoDefault совместимы по назначению со ссылкой IShowMessage. Второе важное отличие состоит в том, что эти объекты не освобождаются явно. Вместо этого, их жизненный цикл управляется интерфейсной ссылкой. Вы это увидите, если загрузите и запустите этот проект. Когда Вы нажмете на кнопку Use Interfaces, вначале увидите диалоговое окно YesDefault. Как только Вы нажмете на одну из кнопок диалогового окна, Вы увидите сообщение, показанное деструктором экземпляра YesDefault. Этот деструктор вызывается когда переменной IShowMessage назначается экземпляр TNoDefault. Другими словами, когда интерфейсная ссылка направляется на другой объект, реализующий интерфейс, первая ссылка освобождается и в результате этого вызывается деструктор. После сообщения деструктора, показывается диалоговое окно экземпляра NoDefault. Снова, после нажатия на кнопку этого диалогового окна, Вы увидите сообщение, показанное деструктором объекта NoDefault. В данном случае, это происходит, потому что переменная IShowMessage вышла из области видимости. Реализация интерфейсов делегированием Начиная с Delphi 4, был предоставлен новый важный механизм реализации интерфейсов. Вместо того, чтобы явно объявлять и реализовывать каждый метод интерфейса, Вы можете объявить свойство типа класс или интерфейс и делегировать реализацию интерфейса этому свойству. Когда объект, реализующий интерфейс, назначается интерфейсной ссылке, этот объект, назначается свойству интерфейса, которое снабжается интерфейсной ссылкой. На первый взгляд это дополнение поддержки интерфейсов может показаться необязательным, но в действительности ("в действительности" устаревшее выражение, на самом деле современные люди употребляют выражение "на самом деле" пр. пер.) оно очень полезно. На примере продемонстрируем почему. Представим, что у Вас есть 20 различных объектов, которые должны реализовать один интерфейс. Допустим, что эти объекты не наследуют методы интерфейса. В Delphi 3 было необходимо для каждого из реализующих классов объявить и реализовать методы интерфейса. Допустим, что все 20 классов, реализуют интерфейс абсолютно одинаково и Вы имеете 20 различных копий одного кода. Если Вы решите, что общая реализация должна быть изменена, Вам надо будет изменить код в 20 различных классах. Реализация интерфейсов делегированием решает эту проблему. Вместо объявления и реализации интерфейса в каждом из 20 классов, Вы можете создать оди -------------------- Если хочешь, что бы что-то работало - используй написанное, если хочешь что-то понять - пиши сам... |
||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
![]() ![]() ![]() |
Правила форума "Delphi: Общие вопросы" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |