Наследуем шар
Пусть кроме обычных шаров по полю будет летать несколько «хищных» шаров, которые сами ловят ловца. Тогда, увеличивая число хищных шаров, мы легко можем превратить «догонялки» в «удиралки». Это прекрасное и познавательное упражнение по наследованию, которое мы сейчас и выполним.
Очевидно, нам нужен еще один класс – 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 и Ставим_изображение_шара_на_место обоих шаров практически одинаковы, а значит можно было постараться избежать излишней писанины. Для этого можно было вернуть их в абстрактный шар и снабдить параметрами – именем массива графических полей и именем файла картинки. В этом случае параметрами пришлось бы снабжать и те процедуры, которые к ним обращаются. В общем, «шило на мыло». Зато код обоих шаров получился бы маленький и аккуратный.