Рефакторинг. Зачем? - страница 8

Шрифт
Интервал

стр.

и LineLength трогать не надо. По крайней мере, если вы не планируете менять внутренней логики класса.

Свойства

Для того, чтобы можно было каким–то образом реагировать на изменения значений полей классов, были придуманы свойства (property). Они так же могут перекликаться с понятиями getter и setter.

Сначала я приведу пример кода, а потом поясню что происходит. Думаю так будет понятнее:

>type

>TLine = class(TObject)

>private

>LengthCalculated: Boolean;

>LineLength: Double;

>FX2: Integer;

>FY2: Integer;

>FX1: Integer;

>FY1: Integer;

>procedure SetX1(const Value: Integer);

>procedure SetX2(const Value: Integer);

>procedure SetY1(const Value: Integer);

>procedure SetY2(const Value: Integer);

>public

>property X1: Integer read FX1 write SetX1;

>property Y1: Integer read FY1 write SetY1;

>property X2: Integer read FX2 write SetX2;

>property Y2: Integer read FY2 write SetY2;

>function CalculateLineLength: Double;

>end;


>function TLine. CalculateLineLength: Double;

>begin

>if not LengthCalculated then

>begin

>LineLength:= Sqrt(Sqr(X2 — X1) + Sqr(Y2 — Y1));

>LengthCalculated:= True;

>end;

>Result:= LineLength;

>end;


>procedure TLine. SetX1(const Value: Integer);

>begin

>LengthCalculated:= False;

>FX1:= Value;

>end;


>procedure TLine. SetX2(const Value: Integer);

>begin

>LengthCalculated:= False;

>FX2:= Value;

>end;


>procedure TLine. SetY1(const Value: Integer);

>begin

>LengthCalculated:= False;

>FY1:= Value;

>end;


>procedure TLine. SetY2(const Value: Integer);

>begin

>LengthCalculated:= False;

>FY2:= Value;

>end;

И так, что мы сделали. Мы добавили полям X1, Y1, X2, Y2 префик «F» и перенесли в private секцию. Префикс «F» — это стандартный, исторически сложившийся префикс для обозначения приватного поля (сокращение от Field, поле). После этого напрямую менять значения координат снаружи класса стало невозможно.

Параллельно, также в private секции мы создали набор функций SetX1, SetY1, SetX2, SetY2 для корректной установки соответствующих значений. Помимо своих непосредственных обязанностей, они также сбрасывают флаг LengthCalculated, чтобы при последующем обращении некорректная уже длина пересчиталась заново.

Для доступа к приватным полям снаружи — предусмотрены свойства. Строка «property X1: Integer read FX1 write SetX1;” означает, что мы свойство X1, при чтении которого будет возвращаться значение FX1 (то, что после ключевого слова read), а при записи — будет вызываться функция SetX1 (то, что после ключевого слова write).

Таким образом, запись: «X:= Line. X1» эквивалентна записи «X:= Line. FX1», а запись «Line. X1:= X» эквивалентна записи «Line. SetX1(X)».

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

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


>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;


стр.

Похожие книги