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

       

Наследуем шар


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

Очевидно, нам нужен еще один класс – clsХищный_шар. Чем отличается поведение хищного шара от поведения обычного? Только действиями при столкновении с ловцом. Если обычный шар при столкновении исчезает, то при столкновении хищного шара с ловцом игра должна останавливаться и должно выдаваться сообщение «Ловца поймали, вы проиграли!»

Кроме поведения есть еще и внешний вид. Фигурка у хищного шара другая.

Исходя из вышесказанного, спросим себя: Чем будет отличаться код класса clsХищный_шар от кода класса clsШар? Процедурой Выход_шара_из_игры, отвечающей за действия при столкновении, и процедурой New, отвечающей за создание изображения шара. Все! Все остальное (за небольшим исключением, о котором позже) будет одинаковым.

Раз так, то имеется прямой смысл использовать наследование. Но какое? Сделать ли, чтобы хищный шар был наследником обычного или оба они должны быть наследниками абстрактного шара? Выбираю второе, и вот почему.

Для изображений хищных шаров нам нужен новый массив графических полей, назовем его pictХищный_шар. Если мы сделаем хищный шар наследником обычного, то при выполнении конструктора New хищного шара должен автоматически выполняться конструктор New обычного шара, который будет работать с массивом графических полей pictШар, что при создании хищного шара не только излишне, но и является ошибкой.

Вот как с учетом вышесказанного дополнится наш проект (многоточием обозначены фрагменты, оставшиеся без изменений):

Форма:

    Private Const Число_хищных_шаров As Integer = 2

    Private Хищный_шар(Число_хищных_шаров) As clsХищный_шар

    Public pictХищный_шар(Число_хищных_шаров) As PictureBox

    ………………

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load




       ………………

        For i = 1 To Число_хищных_шаров

            Хищный_шар(i) = New clsХищный_шар

        Next i

       ………………

    End Sub

    Private Sub Начальная_установка()

       ………………

        For i = 1 To Число_хищных_шаров

            Хищный_шар(i).Начальная_установка()

        Next i

    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

       ………………

        For i = 1 To Число_хищных_шаров

            Хищный_шар(i).Действие()

        Next i

       ………………

    End Sub

Абстрактный шар. Я думаю, что не нужно приводить тут полностью код этого класса, лучше сказать словами, как он получается. Возьмите бывший код класса clsШар из 22.12.6  и вычтите из него строки, которые вы видите пониже в коде нового обычного шара, после чего добавьте пять нижеследующих строк):

Public MustInherit Class clsШар_абстрактный

    Protected

Const Размер_шара As Double = 12

    Protected

x, y As Double

    Protected

dx, dy As Double 

      ………………

    Protected MustOverride Sub Ставим_изображение_шара_на_место()

      ………………

    Protected

MustOverride Sub Выход_шара_из_игры()

End Class

Обычный шар:

Public Class clsШар

    Inherits

clsШар_абстрактный

    Private Shared Число_созданных_шаров As Integer = 0

    Private Номер_шара As Integer

    Public Sub New()

        Число_созданных_шаров = Число_созданных_шаров + 1

        Номер_шара = Число_созданных_шаров

        Форма.pictШар(Номер_шара) = New PictureBox

        Форма.pictШар(Номер_шара).Width = Размер_шара

        Форма.pictШар(Номер_шара).Height = Размер_шара

        Форма.pictШар(Номер_шара).SizeMode = PictureBoxSizeMode.StretchImage

        Форма.pictШар(Номер_шара).BackColor = Color.White

        Форма.pictШар(Номер_шара).Image = Image.FromFile("MOON06.ICO")

        Форма.Controls.Add(Форма.pictШар(Номер_шара))    

        Форма.pictШар(Номер_шара).BringToFront()  

    End Sub



    Protected

Overrides Sub Ставим_изображение_шара_на_место()

        Форма.pictШар(Номер_шара).Left = x

        Форма.pictШар(Номер_шара).Top = y

    End Sub

    Protected

Overrides Sub Выход_шара_из_игры()

        x = -10000 : y = -10000

        dx = 0 : dy = 0

        Форма.Число_непойманных_шаров = Форма.Число_непойманных_шаров - 1

        Форма.Счетчик_непойманных_шаров.Text = Форма.Число_непойманных_шаров

    End Sub

End Class

Хищный шар:

Public Class clsХищный_шар

    Inherits clsШар_абстрактный

    Private Shared Число_созданных_шаров As Integer = 0

    Private Номер_шара As Integer

    Public Sub New()

        Число_созданных_шаров = Число_созданных_шаров + 1

        Номер_шара = Число_созданных_шаров

        Форма.pictХищный_шар(Номер_шара) = New PictureBox

        Форма.pictХищный_шар(Номер_шара).Width = Размер_шара

        Форма.pictХищный_шар(Номер_шара).Height = Размер_шара

        Форма.pictХищный_шар(Номер_шара).SizeMode = PictureBoxSizeMode.StretchImage

        Форма.pictХищный_шар(Номер_шара).BackColor = Color.White

        Форма.pictХищный_шар(Номер_шара).Image = Image.FromFile("MOON02.ICO")

        Форма.Controls.Add(Форма.pictХищный_шар(Номер_шара))

        Форма.pictХищный_шар(Номер_шара).BringToFront()

    End Sub

    Protected

Overrides Sub Ставим_изображение_шара_на_место()

        Форма.pictХищный_шар(Номер_шара).Left = x

        Форма.pictХищный_шар(Номер_шара).Top = y

    End Sub

    Protected Overrides Sub Выход_шара_из_игры()

        Форма.Timer1.Enabled = False                                  'Конец игры

        MsgBox("Ловца поймали, вы проиграли!")

    End Sub

End Class

Пояснения: Хищные и обычные шары равноправны, поэтому в коде формы мы для хищных шаров дописываем все те строки, что писали и для обычных. Переменные Число_созданных_шаров и Номер_шара я перенес из абстрактного шара в каждый из наследников, так как у каждого массива своя нумерация. Поскольку Номер_шара ушел из абстрактного шара, пришлось перенести из него в наследников и процедуру Ставим_изображение_шара_на_место, которая обращается к этой переменной. У обоих шаров процедуры Ставим_изображение_шара_на_место и Выход_шара_из_игры пришлось объявить, как Protected, так как именно таким образом объявлены эти процедуры у родителя. А у родителя они так объявлены потому, что иначе к ним не было бы доступа от наследников.

Недостатки нашего способа наследования:

Смысл процедуры Выход_шара_из_игры класса clsХищный_шар не соответствует ее названию.

Процедуры New и Ставим_изображение_шара_на_место обоих шаров практически одинаковы, а значит можно было постараться избежать излишней писанины. Для этого можно было вернуть их в абстрактный шар и снабдить параметрами – именем массива графических полей и именем файла картинки. В этом случае параметрами пришлось бы снабжать и те процедуры, которые к ним обращаются. В общем, «шило на мыло». Зато код обоих шаров получился бы маленький и аккуратный.


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