ПОНЯТНО О Visual Basic NET (том 3)

       

Наследование


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

Аналогия. Вообразим, что мы создали простенький класс Автомобиль, наделив его всего лишь двигателем, рулем и 4 колесами. И никаких подробностей. Полюбовавшись работой получившейся самоходной тележки, мы решили развивать проект дальше и создать еще три класса, более богатых и подробных: Амфибия, Грузовик и Автобус. У каждого из новых классов, как и у Автомобиля, есть двигатель, руль и 4 колеса, но вдобавок к ним и много подробностей, например, у Амфибии гребной винт, у Грузовика – кузов, у Автобуса – пассажирский салон.

Как создавать код каждого из этих трех классов? В нашем случае можно было бы просто скопировать в каждый из них весь код Автомобиля, добавив затем для каждого класса свои переменные, процедуры и функции. Но есть более удобный и «правильный» способ – это наследование. Мы просто объявляем, что новый класс Грузовик является наследником класса Автомобиль. При этом Грузовик неявно (невидимо) приобретает весь код своего родителя – Автомобиля. Тем самым, ничего не записав в код Грузовика, мы уже можем пользоваться им как Автомобилем. А чтобы снабдить Грузовик дополнительными чертами, мы пишем ему новые процедуры и функции. Аналогично поступаем с Амфибией и Автобусом.

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

У детей могут быть свои дети. Так, у Грузовика могут быть наследники Самосвал, Пикап и др. Все они наследуют у папаши-Грузовика кузов, а у дедушки-Автомобиля – двигатель, руль и 4 колеса. В общем, гены передаются самым отдаленным потомкам.


Рассмотрим пример. Пусть имеется класс clsПрямоугольник. Его дело – предоставлять внешнему пользователю возможность вычислять по длине и ширине прямоугольника его площадь и периметр:

Public Class clsПрямоугольник

    Public Длина As Integer

    Public Ширина As Integer

    Public Function Площадь() As Integer

        Return    Длина * Ширина

    End Function



    Public Function Периметр() As Integer

        Return    2 * Длина + 2 * Ширина

    End Function

End Class

Предположим, впоследствии у нас появилась необходимость по длине, ширине и высоте параллелепипеда вычислять его объем. Мы могли бы просто дополнить наш класс clsПрямоугольник следующим кодом:

    Public Высота As Integer

    Public Function Объем() As Integer

        Return Площадь() * Высота

    End Function

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

Поэтому создаем новый класс – clsПараллелепипед:

Public Class clsПараллелепипед

    Inherits

clsПрямоугольник

    Public Высота As Integer

    Public Function Объем() As Integer

        Return Площадь() * Высота

    End Function

End Class

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

    Inherits

clsПрямоугольник

говорит о том, что он наследует все компоненты класса clsПрямоугольник. Слово Inherits переводится «наследует». В простейшем случае наследование выглядит так, как если бы мы скопировали код родителя в тело наследника.

Обратите внимание, что не все, что есть у родителя, нужно наследнику. Я имею в виду периметр. Ну что ж, видимо, идеальных родителей не бывает.

Продолжаем пример. У родителя может быть много детей. Создадим еще один класс – clsКоробка, дело которого – вычислять полную площадь поверхности параллелепипеда. Вот он:

Public Class clsКоробка



    Inherits

clsПрямоугольник

    Public Высота As Integer

    Public Function Площадь_поверхности() As Integer

        Return 2 * Площадь() + Периметр() * Высота

    End Function

End Class

Вы видите, что каждый из наследников имеет 3 поля: длину, ширину и высоту – и умеет вычислять площадь и периметр. Проверим работу классов, нажав кнопку на форме:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim Прямоугольник As New clsПрямоугольник

        Прямоугольник.Длина = 10

        Прямоугольник.Ширина = 5

        Debug.WriteLine(Прямоугольник.Площадь)

        Debug.WriteLine(Прямоугольник.Периметр)

        Dim Параллелепипед As New clsПараллелепипед

        Параллелепипед.Длина = 4

        Параллелепипед.Ширина = 3

        Параллелепипед.Высота = 2

        Debug.WriteLine(Параллелепипед.Площадь)

        Debug.WriteLine(Параллелепипед.Периметр)

        Debug.WriteLine(Параллелепипед.Объем)

        Dim Коробка As New clsКоробка

        Коробка.Длина = 3

        Коробка.Ширина = 2

        Коробка.Высота = 100

        Debug.WriteLine(Коробка.Площадь)

        Debug.WriteLine(Коробка.Периметр)

        Debug.WriteLine(Коробка.Площадь_поверхности)

    End Sub

Вот что будет напечатано:

50

30

12

14

24

6

10

1012

Обратите внимание, что наследуются переменные величины в классах, а не их конкретные значения в объектах. Так, значение длины прямоугольника никак не влияет на значение длины коробки.

Внуки. У детей, как я уже говорил, могут быть свои дети. Так, у класса clsКоробка могут быть наследники: Гараж, Чемодан и др. Все они наследуют у родителя – класса clsКоробка – высоту и площадь поверхности, а у дедушки – класса clsПрямоугольник – длину, ширину, площадь и периметр. Факт наследования объявляется аналогично:

Public Class clsГараж

    Inherits

clsКоробка

Область видимости Protected. Если какой-нибудь компонент родительского класса объявлен Private, то у наследника не будет доступа к этому компоненту. Когда такой доступ нужен, компонент рекомендую объявлять Protected. В этом случае он будет доступен наследникам, но только им. Пример проекта:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim Наследник As New clsНаследник

        Debug.WriteLine(Наследник.Фн)

End Sub

Public Class clsРодитель

    Protected А As Integer = 100

    Private B As Integer = 200

    Protected Function Ф() As Integer

        Return   B + 1   

    End Function

End Class

Public Class clsНаследник

    Inherits clsРодитель

    Public Function Фн() As Integer

        Return   Ф + А

    End Function

End Class

Проект напечатает

301

Если бы мы объявили так:

    Private А

As Integer = 100

    Private Function Ф() As Integer

то в строке

        Return   Ф + А

были бы подчеркнуты и Ф, и А, поскольку ни переменная, ни функция были бы недоступны.


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