Мир программирования

 


Найти: на:


Меню
Партнеры
Счетчики
Реклама

Процедура или метод?


 Предыдущая страница     |     Следующая страница  
Добавить в избанное Обсудить в форуме Написать автору сайта

           Главная цель   разработки   модуля   Figures.PAS   состоит   в предоставлении пользователям возможности расширять  типы  объектов, определенные в   модуле  и  еще  использовать все свойства модулей. Интересная задача состоит в том,  чтобы создать несколько  способов изображения произвольной   графической  фигуры на экране в ответ на ввод пользователя.

     Есть два способа реализации этой цели.  Способ, который первый приходит в голову традиционному программисту на Паскале - заставить FIGURES.PAS экспортировать процедуру,   которая  берет  полиморфный объект в  качестве параметра var,  а затем изображает данный объект на экране. Такая процедура приведена ниже:

      procedure DragIt (var AnyFigure : Point;  DragBy : integer);

     var

        DeltaX, DeltaY : Integer;

        FigureX, FigureY : Integer;

     begin

        AnyFigure.Show {чтобы  изображаемая  фигура  была  видна на         экране}

        FigureX := AnyFigure.GetX; {получаем начальные значения X и

        FigureY := AnyFigure.GetY;  Y для данной фигуры }

      {Ниже приведен цикл, в котором чертится фигура}

         while GetDelta (DeltaX, DeltaY) do

        begin

          {применим Delta к X,Y}

            FigureX := FigureX + (DeltaX + DragBy);

           FigureY := FigureY + (DeltaY + DragBy);

           {затем передвигаем фигуру}

        end;

     end;

      DragIt вызывает  дополнительную  процедуру  GetDelta,  которая получает от пользователя изменения X и Y в некоторой  форме.   Ввод можно осуществлять   с помощью клавиатуры,  манипулятора "мышь" или джойстика. Для  простоты в  нашем  примере  ввод  осуществляется  с помощью клавиш-стрелок на клавиатуре.

     Что касается DragIt,  то нужно отметить, что любой объект типа Point или объект любого типа,  порожденного Point, можно передать в var  параметр  AnyFigure.  Экземпляры  типа  Point или Circle,  или экземпляры любого порожденного Point или Circle типа, который будет определен в будущем, могут передаваться без компиляции в AnyFigure.

     Как код  DragIt узнает,  какой тип объекта фактически передан? DragIt не знает тип и это хорошо.  DragIt только делает  ссылку  на идентификаторы, определенные в типе Point.  Благодаря наследованию, эти идентификаторы определены так же  в  любом  типе,   порожденном Point. Методы GetX,  GetY,  Show и MoveTo фактически присутствуют в типе Circle так же, как в типе Point и будут присутствовать в любом будущем типе, определенном в качестве потомка или Point или Circle.

     GetX, GetY  и  MoveTo  статические методы,  это означает,  что DragIt знает адрес каждой процедуры во время компиляции.  С  другой стороны, Show  - виртуальный метод.  Есть различные реализации Show для Point и Circle - и DragIt не знает во  время  компиляции  какая реализация будет     вызываться.    Короче  говоря,   когда  DragIt вызывается, она  ищет адрес правильной реализации  Show  в  таблице виртуальных методов,  переданного в AnyFigure. Если экземпляр имеет тип Circle,  то DragIt вызывает Circle.Show.  Если экземпляр  имеет тип Point,    то DragIt вызывает Point.Show.  Решение о том,  какая реализация Show будет вызываться не принимается вплоть  до  времени выполнения, фактически,   вплоть до того момента в программе, когда DragIt должна вызвать виртуальный метод Show.

     Теперь DragIt   работает   достаточно   хорошо  и,   если  она экспортируется модулем пакета,  то она может изобразить любой  тип, порожденный Point,   на экране независимо от того,  существует этот тип при компиляции пакета или нет.  Но нужно думать немного дальше: если любой   объект  может  быть изображен на экране,  то почему не сделать вычерчивание свойством самого графического объекта?

     Другими словами, почему не сделать DragIt методом?

     Сделаем DragIt методом!

     Действительно. Почему нужно передавать объект процедуре, чтобы изобразить объект на экране? Это старый способ мышления. Если можно написать процедуру для вычерчивания всех объектов графических фигур на экране,  то  тогда  объекты  графических  фигур  должны  быть  в состоянии изобразить себя на экране.

     Другими словами,  процедура DragIt действительно  должна  быть методом Drag.

     Чтобы добавить новый метод к существующей  иерархии  объектов, нужно немножко   подумать,   где   расположить  метод  в  иерархии. Подумайте об утилите,  предоставляемой методом и  решите  насколько широко   она   будет   применяться.  Вычерчивание  фигуры  включает изменение  позиции   фигуры   в   ответ   на   ввод   пользователя. Метафорически  можно  считать,  что  метод  Drag  аналогичен методу MoveTo c внутренним источником энергии.  В терминах наследования он расположен  непосредственно рядом с MoveTo - любой объект, которому подходит MoveTo,  должен так же наследовать и Drag.  Таким образом, Drag  должен добавляться к нашему абстрактному типу объектов Point, так чтобы все потомки Point могли использовать этот метод.

     Нужно ли, чтобы Drag был виртуальным методом?

     Лакмусовый тест на необходимость виртуализации метода  состоит в том,    ожидается   или  нет  изменение  функциональности  метода где-нибудь ниже  в  иерархическом  дереве.   Drag  -  это  свойство замкнутого-законченного типа.   Drag манипулирует только с позицией X,Y фигуры   и  трудно  предположить,   что  для  Drag  потребуются какие-нибудь изменения.  Следовательно, вероятно, что этот метод не нужно делать виртуальным.

     Будьте осторожны при любом таком решении.  Если Вы не сделаете Drag виртуальным, то Вы исключите все возможности для пользователей FIGURES.PAS   изменить   этот  метод  при  расширении  FIGURES.PAS. Возможно, что Вы не сможете представить обстоятельства, при которых пользователь  может  захотеть переписать Drag.  Но это не означает, что такие обстоятельства не могут возникнуть.

     Например, Drag  имеет  непредвиденное обстоятельство,  которое перевешивает баланс в пользу виртуализации метода.  Drag имеет дело с обработкой событий,  т.е.  перехват ввода от устройств,  подобных клавиатуре и манипулятору  "мышь",   происходит  в  непредсказуемые моменты времени,   эти события должны тем не менее обрабатываться в тот момент,  когда они произошли. Обработка событий - беспорядочное дело и   очень  зависит  от  аппаратуры.   Если  пользователь имеет несколько устройств ввода,  которые не объявлены в Drag в том виде, как он представлен, то пользователь будет не в состоянии переписать Drag. Не сжигайте все мосты. Сделайте Drag виртуальным методом.

     Процесс преобразования  DragIt  в  метод и добавление метода к Point является почти тривиальным.  Внутри определения объекта Point Drag - это просто еще один заголовок метода:

      Point = Object (Location)

     Visible : boolean

     Constructor Init (InitX, InitY : integer);

     procedure Show : virtual;

     procedure Hide : virtual;

     function IsVisible : Boolean;

     procedure MoveTo (NewX, NewY : integer);

     procedure Drag (DragBy : integer) : virtual;

     end;

      Позиция заголовка  метода  Drag в определении объекта Point не имеет значения.  Запомните,  что методы могут объявляться  в  любом порядке, а поля данных должны определяться перед первым объявлением метода. Сущность  преобразования  процедуры  DragIt  в  метод  Drag практически полностью   состоит в применении сферы действия Point к DragIt. В процедуре DragIt нужно определить методы AnyFigure.Show и AnyFigure.GetX и  т.д.  Drag теперь является частью Point,  поэтому квалифицировать имена методов больше нет необходимости.

     AnyFigure.GetX - это теперь просто GetX и т.д. И, конечно, var параметр AnyFigure  исключается  из  строки   параметров.   Неявный параметр Self теперь дает информацию о том, какой экземпляр объекта называется Drag.

     Примечание: Полный  исходный  код для FIGURES.PAS,  включающий Drag, реализованный как виртуальный метод, есть на Вашем диске.

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

     Это целый новый мир.

[оглавление]


Предыдущая страница     |     Следующая страница


Добавить в избанное Обсудить в форуме Написать автору сайта

Опрос

Конкурсы
Реклама

 

Web дизайн: Бурлаков Михаил    

Web программирование: Бурлаков Михаил

Используются технологии uCoz