Рефакторинг. Зачем? [DarkGoodWIN] (fb2) читать постранично, страница - 8

- Рефакторинг. Зачем? 121 Кб, 22с. скачать: (fb2) - (исправленную)  читать: (полностью) - (постранично) - DarkGoodWIN

 [Настройки текста]  [Cбросить фильтры]

самое можно сказать и о квадрате. Что это значит с точки зрения программирования? То, что класс TCircle, наследник класса TShape можно использовать везде, где можно использовать класс TShape. Более того, все переменные и методы класса TShape (кроме private) будут также доступны в классе TCircle.

Не буду сильно углубляться в теорию, всё–таки я предпочитаю объяснять на примерах, поэтому сразу перейдём к тому как изменится наша функция с попмощью этого нехитрого преобразования:


var

Shapes: array of TShape;


function HitTest(X, Y: Integer): Boolean;

var

I: Integer;

begin

Result:= False;

for I:= 0 to Length(Shapes) — 1 do

begin

if Shapes[I] is TCircle then

Result:= (Shapes[I] as TCircle).HitTest(X, Y)

else if Shapes[I] is TRectangle then

Result:= (Shapes[I] as TRectangle).HitTest(X, Y)

if Result then

Exit;

end;

end;

На самом деле тоже не очень красиво. Приходится для каждого примитива делать проверку, поддерживает–ли он нужный нам тип (оператор is) и осуществлять приведение типов (оператор as). Операторы is и as предназначены для работы только с объектами и не работают с простыми типами. Подробнее о них можно прочитать в документации.

Чтобы оценить мощь наследования нам остался всего один шаг. В класс TShape добавим строку «function HitTest(X, Y: Integer): Boolean; virtual; abstract;”, а в классы TCircle и TRectangle добавим после аналогичных строчек ключевое слово override:


type

TShape = class(TObject)

public

function HitTest(X, Y: Integer): Boolean; virtual; abstract;

end;


TCircle = class(TShape)

public

 …..

function HitTest(X, Y: Integer): Boolean; override;

end;


TRectangle = class(TShape)

public

…..

function HitTest(X, Y: Integer): Boolean; override;

end;

Что это означает? Мы как бы говорим, что класс TShape в принципе может проверить, попали в него координаты мыши или нет, но конкретная реализация зависит от того, какой именно примитив используется. То есть абстрактно функциональность есть, но её реализация должна быть переопределена в классах потомках.

Нашу многострадальную функцию теперь можно переписать так:


var

Shapes: array of TShape;


function HitTest(X, Y: Integer): Boolean;

var

I: Integer;

begin

Result:= False;

for I:= 0 to Length(Shapes) — 1 do

begin

Result:= Shapes[I].HitTest(X, Y);

if Result then

Exit;

end;

end;

При этом, в случаю кругов, в реальности будет вызываться функция TCircle. HitTest, а в случае прямоугольников — TRectangle. HitTest.

Понятно, что в случае с одной абстрактной функцией выигрышь не совсем очевиден, но ведь можно расширить базовый класс, добавив в него функции:

TShape. Move(dx, dy: Integer); virtual; abstract;

для перемещения примитива,

TShape. Rotate(x, y: Integer; angel: Double); virtual; abstract;

для поворота вокруг точки,

TShape. Flip(Line: TLine); virtual; abstract;

для зеркального отображения вокруг прямой.

Реализация данных методов уникальна для каждого из классов наследников, однако сама функциональность применима ко всем графическим примитивам.