Основы офисного программирования и язык VBA

       

Наследование интерфейсов


В Office 2000 на классах введено отношение "наследование интерфейса" . Здесь под интерфейсом понимается совокупность всех открытых (Public) свойств и методов класса. Пусть уже создан класс A, который будем называть родительским или базовым. Тогда при создании нового класса B, который будем называть классом-потомком, можно объявить, что потомок наследует интерфейс родительского класса. Заметьте, что это отношение в отличие от "классического" наследования не транзитивно, - потомок класса B не наследует интерфейс класса A - родителя класса B. Однако допускается множественное наследование интерфейсов, потомок может иметь несколько родителей, - наследовать интерфейсы нескольких классов. Говоря о родительском классе, следует отметить три возможности:

  • Родительский класс может быть полностью абстрактным классом, то есть все его открытые методы будут чистыми, не имеющими реализации.
  • Родительский класс может быть полностью определенным классом с заданной реализацией методов.
  • Зачастую, родительский класс является абстрактным классом, где часть методов, реализация которых предполагается одинаковой для большинства классов - потомков, задана, а некоторые методы являются чистыми.

Синтаксически, объявить о том, что класс наследует интерфейс другого класса, достаточно просто. Для этого достаточно в объявление класса поместить одну строчку:

Implements имя_родительского_класса

При появлении такой строки класс- потомок наследует интерфейс родительского класса. Это означает, что будут наследоваться все открытые методы, а для каждого открытого свойства, будет наследоваться пара процедур - свойств Property Get и Property Let. Сами свойства наследоваться не будут. Как только в раздел объявлений класса вставлена строка "Implements", в окне кода этого класса появится список методов родительского класса, реализацию которых предстоит написать. Для того чтобы задать реализацию этих методов, используется привычная техника первоначального создания заготовок методов с последующим наполнением их содержательным текстом.


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

Если соответствующий метод родительского класса является чистым, то понятно, что потомок обязан дать собственную реализацию метода. Собственная реализация необходима и в том случае, если потомок хочет переопределить существующий метод родителя. Во многих случаях хотелось бы просто наследовать метод родителя, но, заметьте, автоматически это не делается. Здесь есть две возможности:

  • Использовать обычную схему Copy - Paste, копируя реализацию родителя.
  • Реализовать наследование типичным для Office 2000 способом путем встраивания родительского объекта в класс - потомок. Тогда можно вызывать в нужном месте нужный метод родителя. Чуть позже мы рассмотрим проект "Люди и машины", где используем этот прием.


Еще одна важная особенность наследования интерфейса состоит в том, что интерфейс родителя не становится интерфейсом потомка. Как следствие этого факта, отсутствие транзитивности отношения "наследование интерфейсов". Синтаксически, это определяется двумя обстоятельствами:

  • Заготовки методов, строящиеся автоматически, используют спецификатор Private. Это означает, что наследуемые методы не являются открытыми методами потомка и, следовательно, не входят в его интерфейс и не передаются далее внукам родителя.
  • Еще одна особенность состоит в том, что имена наследуемых методов содержат имя родителя. Это также означает, что наследуемый метод не становится собственным методом, который можно передать далее своим потомкам


Возникает естественный вопрос, если наследуемые методы не входят в интерфейс класса, а следовательно не могут быть вызваны объектами данного класса, то какой в них толк? Толк есть, поскольку эти методы могут быть все-таки вызваны объектами, правда, принадлежащими родительскому классу.


Объект родительского класса, ссылающийся на своего потомка, может вызывать методы родителя, наследуемые потомком. Вот небольшой пример, где действую объекты трех классов Father, Son и GrandSon, связанные отношением наследования интерфейсов. Пусть определен абстрактный класс Father, интерфейс которого состоит из одного свойства, одного чистого метода и метода с заданной реализацией:

'Class Father 'Свойства класса Public MyProperty As String

'Методы класса

Public Sub MyPureMethod() 'Чистый метод End Sub



Public Sub MyRealMethod() MsgBox ("It's the Father") End Sub

Класс Son, наследующий интерфейс класса Father, по контракту должен реализовать две процедуры - свойства и два его метода. Мы дали только формальную реализацию, - в созданные автоматически заготовки добавили комментарии. Лишь в реализацию метода Father_MyPureMethod класса Son вставлена строка текста. Кроме того, в класс добавлен новый метод, определяющий собственный интерфейс этого класса. Вот описание этого класса:

Пример 5.1.

(html, txt)

Заметьте, что, конечно, можно в наследуемых методах изменить спецификатор Private на Public, и тогда эти методы войдут в интерфейс класса - потомка. Но лучше этого не делать, хотя бы потому, что наследование таких методов приведет к ошибке, - наследование наследуемых методов не допускается, как мы уже говорили. Правильный путь состоит в создании собственных Public методов, в которых при необходимости вызываются Private методы.

Приведем теперь описание класса GrandSon - потомка классов Son и Father. Заметьте, класс явно определяет обоих своих родителей. Опять-таки мы ограничились формальной реализацией, определив лишь реализацию метода Father_MyRealMethod:

Пример 5.2.

(html, txt)

Приведем теперь пример процедуры из стандартного модуля, где действуют объекты всех трех классов семейства:

Пример 5.3.

(html, txt)

В этом примере объект Grand класса Father связывается поочередно с объектами класса Father, Son, GrandSon и всякий раз вызываются методы родительского класса, унаследованные потомками.Обратите внимание на конструкцию TypeOf - Is, позволяющую определить текущий тип объекта. В процессе работы этой процедуры будут открываться диалоговые окна, уведомляющие, что мы встретились с объектами Father, Son, Son of his Father, GrandSon. При печати свойств лишь однажды будет напечатано значение Flat.

Уже этот пример показывает, что наследование интерфейсов мало что дает с точки зрения наследования свойств и методов, поскольку ни свойства напрямую не наследуются, ни реализация не наследуется. Наследование интерфейсов представляет несомненный интерес и весьма полезно по другой причине, - оно позволяет реализовать в полной мере полиморфизм семейства классов VBA. К подробному рассмотрению этого вопроса мы сейчас и переходим.



Опять- таки мы ограничились формальной реализацией, определив лишь реализацию метода Father_MyRealMethod:

Option Explicit

Implements Father Implements Son

Private Property Let Father_MyProperty(ByVal RHS As String) 'Реализация отложена

End Property

Private Property Get Father_MyProperty() As String 'Реализация отложена

End Property

Private Sub Father_MyPureMethod() 'Реализация отложена

End Sub

Private Sub Father_MyRealMethod() 'Реализация отложена MsgBox ("It's the GrandSon") End Sub

Private Sub Son_SonNewMethod() 'Реализация отложена

End Sub

Пример 5.2.

Приведем теперь пример процедуры из стандартного модуля, где действуют объекты всех трех классов семейства:

Public Sub Family()

Dim F As New Father, S As New Son, GS As New GrandSon Dim Grand As Father, GrandS As Son

Set Grand = F Grand.MyProperty = "Flat" Grand.MyRealMethod Grand.MyPureMethod Debug.Print Grand.MyProperty Set Grand = S Grand.MyProperty = "Flat" Grand.MyRealMethod Grand.MyPureMethod If TypeOf Grand Is Son Then Set GrandS = Grand: GrandS.SonNewMethod End If Debug.Print Grand.MyProperty

Set Grand = GS If TypeOf Grand Is GrandSon Then Set GrandS = Grand: GrandS.SonNewMethod End If Grand.MyProperty = "Flat" Grand.MyRealMethod Grand.MyPureMethod Debug.Print Grand.MyProperty

End Sub

Пример 5.3.

В этом примере объект Grand класса Father связывается поочередно с объектами класса Father, Son, GrandSon и всякий раз вызываются методы родительского класса, унаследованные потомками. Обратите внимание на конструкцию TypeOf - Is, позволяющую определить текущий тип объекта. В процессе работы этой процедуры будут открываться диалоговые окна, уведомляющие, что мы встретились с объектами Father, Son, Son of his Father, GrandSon. При печати свойств лишь однажды будет напечатано значение Flat.

Уже этот пример показывает, что наследование интерфейсов мало что дает с точки зрения наследования свойств и методов, поскольку ни свойства напрямую не наследуются, ни реализация не наследуется.Наследование интерфейсов представляет несомненный интерес и весьма полезно по другой причине, - оно позволяет реализовать в полной мере полиморфизм семейства классов VBA. К подробному рассмотрению этого вопроса мы сейчас и переходим.


Содержание раздела