Долго решался, прежде чем начать эту тему, обычно она достаточно сложна для понимания. Не уверен, что мне удасться уложиться в одну главу, но не беда. Главное быть последовательным. Давайте не уходить далеко от геометрических примитивов. Создадим два класса. Класс, описывающий круг и класс, описывающий прямоугольник:
>type
>TCircle = class(TObject)
>public
>X: Integer;
>Y: Integer;
>D: Integer;
>end;
>TRectangle = class(TObject)
>public
>X1: Integer;
>Y1: Integer;
>X2: Integer;
>Y2: Integer;
>end;
Круг вполне описывается координатами центра (X, Y) и диаметром (D), а прямоугольник — двумя точками (X1, Y1 и X2, Y2).
Допустим у нас есть программа для рисования кругов и прямоугольников. Каждый раз, когда мы рисуем новый круг, он кладётся в массив Circles: array of TCircle, а когда рисуем новый прямоугольник, он кладётся в массив Rectangles: array of TRectangle.
Далее нам нужен код, который определит, находится–ли заданная координата внутри одного из нарисованных примитивов. Например, пользователь совершил клик мыши и нам нужно выделить приметив, если кликнули именно на него.
Заведём для этого в каждом классе функцию HitTest, которая будет возвращать True в случае, если наша точка находится внутри графического примитива и False в противном случае:
>type
>TCircle = class(TObject)
>public
>X: Integer;
>Y: Integer;
>D: Integer;
>function HitTest(aX, aY: Integer): Boolean;
>end;
>type
>TRectangle = class(TObject)
>public
>X1: Integer;
>Y1: Integer;
>X2: Integer;
>Y2: Integer;
>function HitTest(X, Y: Integer): Boolean;
>end;
>function TCircle. HitTest(aX, aY: Integer): Boolean;
>begin
>Result:= Sqrt(Sqr(X — aX) + Sqr(Y — aY)) <= D;
>end;
>function TRectangle. HitTest(X, Y: Integer): Boolean;
>begin
>Result:= (X1 <= X) and (X <= X2) and (Y1 <= Y) and (Y <= Y2);
>end;
>Это была реализация классов, а тут реализация базовой функции HitTest, которая должна проверить все наши объекты:
>var
>Circles: array of TCircle;
>Rectangles: array of TRectangle;
>function HitTest(X, Y: Integer): Boolean;
>var
>I: Integer;
>begin
>Result:= False;
>for I:= 0 to Length(Rectangles) — 1 do
>begin
>Result:= Rectangles[I].HitTest(X, Y);
>if Result then
>Exit;
>end;
>for I:= 0 to Length(Circles) — 1 do
>begin
>Result:= Circles[I].HitTest(X, Y);
>if Result then
>Exit;
>end;
>end;
Не слишком компактно получилось. А теперь представим, что разных типов примитивов у нас десятки и даже сотни. Получается для каждого нужен свой массив, и свой цикл для функции HitTest? Есть способ сделать проще, можно применить наследование.
В чём тут смысл? Вместо нескольких массивов для каждого графического примитива отдельно, мы заводим класс, который описывает графический примитив, назовём его, например, TShape. Далее мы хотим хранить все наши примитивы, независимо от их типа в массиве «Shapes: array of TShape». Но так просто у нас это не получится, компилятор ругнётся, что типы не совместимы.
Для того, чтобы мы смогли положить наши прямоугольники и круги в массив TShape, нам надо поменять их наследование. Заменим TObject на TShape в объявлении класса:
>type
>TShape = class(TObject)
>end;
>TCircle = class(TShape)
>public
>…..
>end;
>TRectangle = class(TShape)