|
|||||||||||||||||||
|
Расширяемые объектыЛюди, впервые столкнувшиеся с Паскалем, считают само собой разумеющимся гибкость стандартной процедуры Writeln, позволяющей одной процедуре обрабатывать параметры нескольких различных типов: Writeln(CharVar); {выводит значение символа} Writeln(IntegerVar); {выводит целочисленное значение} Writeln(RealVar); {выводит значение с плавающей точкой} К сожалению стандартный Паскаль не предоставляет возможности для создания Ваших собственных в равной степени гибких процедур. Объектно-ориентированное программирование решает эту проблему посредством наследования: когда задается родительский тип, методы родительского типа наследуются, но при необходимости они так же могут быть перекрыты. Чтобы перекрыть родительский метод, просто задайте новый метод с тем же именем, что и родительский метод, но с другим телом и (если необходимо) с другим набором параметров. Рассмотрим простой пример. Давайте зададим тип, порожденный от Point, который рисует окружность вместо точки на экране: type Circle = object (Point) Radius : Integer; procedure Init(InitX, InitY : Integer; InitRadius : Integer); procedure Show; procedure Hide; procedure Expand(ExpandBy : Integer); procedure Contract(ContractBy : Integer); end; constructor Circle.Init(InitX, InitY : Integer; InitRadius : Integer); begin Point.Init(InitX, InitY); Radius := InitRadius; end; procedure Circle.Show; begin Visible := True; Graph.Circle(X, Y, Radius); end; procedure Circle.Hide; var TempColor : Word; begin TempColor := Graph.GetColor; Graph.SetColor(GetBkColor); Visible := False; Graph.Circle(X, Y, Radius); Graph.SetColor(TempColor); end; procedure Circle.Expand(ExpandBy : Integer); begin Hide; Radius := Radius + ExpandBy; if Radius < 0 then Radius := 0; Show; end; procedure Circle.Contract(ContractBy : Integer); begin Expand(-ContractBy); end; procedure Circle.MoveTo(NewX, NewY : Integer); begin Hide; X := NewX; Y := NewY; Show; end; Окружность, в некотором смысле, является жирной точкой: она имеет все, что имеет точка (позицию X,Y, состояние видимый/невидимый) плюс радиус. Объектный тип Circle появился для того, чтобы иметь единственное поле Radius, но не забывает о всех других полях, которые Circle наследует как наследник типа Point. Circle имеет X,Y и Visible, даже если Вы не видите их в определении типа для Circle. Так как Circle задает новое поле Radius, то его инициализация требует нового Init метода, который инициализирует Radius, так же как и наследуемые поля. Вместо прямого присваивания значений наследуемым полям, почему бы не использовать повторно Point метод инициализации (иллюстрируемый первым оператором в Circle.Init). Синтаксис для вызова наследуемого метода: Ancestor.Method, где Ancestor - это идентификатор родительского типа объекта, а Method - идентификатор метода этого типа. Заметим, что вызов перекрывающего метода является не очень хорошим стилем; вполне возможно, что Point.Init (или Location.Init для этого случая) выполняет более важную, скрытую инициализацию. Путем вызова перекрывающего метода Вы обеспечите включение в тип объекта, являющегося потомком, функциональности родителя. Вдобавок, любые изменения, сделанные в родительском методе, автоматически влияют на всех его потомков. После вызова Point.Init, Circle.Init может затем выполнить свою собственную инициализацию, которая в данном случае состоит только в присвоении Radius значения, передаваемого в InitRadius. Вместо изображения и удаления окружности по точке, можно использовать BGI Circle процедуру. Если Вы поступите так, то потребуются новые Show и Hide методы, перекрывающие такие же методы в Point. Такие переписанные заново Show и Hide методы появились в приведенном выше примере. Уточнение имени с помощью точки разрешает возможную проблему освобождения от имени типа объекта, являющегося таким же, что и имя BGI программы, рисующей тип объекта на экране. Graph.Circle - это совершенно однозначный способ сказать Turbo Pascal, что Вы ссылаетесь на Circle программу в GRAPH.TPU, а не на Circle тип объекта. Важно! В то время, как методы можно перекрывать, поле данных перекрывать нельзя. Если однажды Вы задали поле данных в иерархии объектов, порожденный тип не может задать поле данных с точно таким же идентификатором. Предыдущая страница | Следующая страница |
|
Web дизайн: Бурлаков Михаил
Web программирование: Бурлаков Михаил