                                       Джефф Дантеманн, Крис Мариначчи

                   НОВЫЕ ОБЪЕКТЫ В СТАРЫХ СТРУКТУРАХ

        Когда Вы отваживаетесь применить для нового проекта новый  язык,
старые  тексты  программы  обычно  отбрасываются. Пиcать новые программы
приятно, переписывать старые на новый язык - тяжелая, неприятная работа.
Кроме того, старые программы уже  работают. Если они не испортились,  то
зачем их переделывать?

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

        Новая технология проявляется с все большей регулярностью в инду-
стрии инструментов программирования.  Давно ничто не  производило такого
фурора, как ООП, - ничто, пожалуй, после появления методов  структурного
программирования в 60-х годах.

        Стоит ли ООП разговора о нем?

        Ответ - несомненно да. И причины, почему нужно писать новые про-
граммы с помощью ООП,  совпадают с причинами, почему  стоит переписывать
старые программы:

        Легкость сопровождения - ООП  программы легче читать и  понимать
(а, следовательно, и  изменять) чем традиционные  структурированные. ООП
техника предлагает высокоэффективные  методы управления сложностью  про-
грамм путем введения функциональной иерархии и удалением деталей, на ко-
торые программист не должен обращать внимание в каждый отдельный  момент
времени.

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

        Расширяемость - одно из достоинств концепции наследования -  это
то, что объекты можно дополнять новыми свойствами без ненужного дублиро-
вания кода. Дочерний объект объявляется как все, что имеет его  родитель
и определяет только  то, что отличает  от (или дополняет)  родительского
объекта.


                 ЗАТРАТЫ И ПРИОБРЕТЕНИЯ

        Преобразование программы  требует ответственных  решений, касаю-
щихся не только  этой программы, но  и программ, которые  будут написаны
или переписаны в будущем.  Действительно, перед тем как  разработчик на-
чнет преобразовывать старую программу в объектно-ориентированную, он до-
лжен определить стратегию разработки объектов с учетом будущих программ.
Таким образом, можно распределить затраты между будущими проектами и те-
кущими программами.  Разработчики должны  тщательно изучить  эти затраты
прежде чем начинать переписывать  любую программу. Эти затраты  включают
время, инструменты, талант и, возможно, программы, которые нельзя  пере-
писать.

        Широкомасштабное планирование требует времени, сложно и  дорого.
Без хорошо продуманной иерархии нет возможности использовать преимущест-
ва позднего связывания и  полиморфизма. Однако, разработка иерархии  не-
проста и может потребовать  такого уровня координации усилий  индивидуа-
лов, какого не требовалось ранее. В процесс может потребоваться  включе-
ния архитектора проекта и поддерживающего персонала.

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

        Кроме того, существующие инструменты программирования могут ока-
заться несовместимыми с техникой ООП. Старые библиотеки подпрограмм  мо-
гут не линковаться с новыми ООП модулями. Отладчики могут быть не в  со-
стоянии отлаживать ООП код, особенно сильно использующий позднее  связы-
вание.

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

        Кроме того, возможно,  что существующие программы  требуют таких
усилий, при переписывании на ООП язык, что легче написать ее с нуля. На-
пример, программы написанные неструктурно, просто невозможно  переделать
в ООП.

        В целом, чем выше связь между модулями, процедурами и т.п.,  тем
сложнее переделать ее в ООП.

        Кроме того,  сложно поддаются  переделке элементы,  использующие
системные ресурсы, например, прерывания. Одни программы легче преобразу-
ются, чем другие. Неплохо определить какие программы легче преобразовать
до начала преобразования.


           СТРУКТУРИРОВАННЫЙ ПАСКАЛЬ И ООПАСКАЛЬ

        Люди долгое время считали,  что ООП требует новых  и непривычных
языков, таких как Смолток. Однако за последние несколько лет объектно  -
ориентированные расширения Паскаля и  С опровергли эту концепцию.  Фирма
Apple опубликовала свой Object Pascal в середине 80-х. Вскоре после это-
го Бйорн Строустрэп определил С++ объектно-ориентированные расширения С.
С тех пор эффектные  и эффективные ОО-расширения структурных  языков по-
явились на  сцене -  например, Objective  C, используемый  в компьютерах
NeXT.

        В середине  1989-го Borland  International расширила  свой Turbo
Pascal ведением ООП  концепций: полиморфизма, инкапсуляции  и наследова-
ния.

        Полиморфизм - это способность объектов адекватно реагировать  на
директивы процедур, не знающих в точности типа объекта. Это  достигается
с помощью позднего связывания,  т.е. определения адреса вызова  во время
выполнения программы, а не во время компиляции.

        Инкапсуляция - это объединение  кода и данных в  одну структуру.
Это воплощено в виде структуры - объекта, определяемой по аналогии с за-
писью:

   type
     Point = object
               X, Y : integer;
               Visible : boolean;
               procedure MoveTo(NewX, NewY : integer);
               procedure WheveIs(var PosX, PosY : integer);
               function IsVisible : boolean;
               procedure Show;
               procedure Hide;
             end;

        Извне можно обращаться к полям или пользоваться методами, выпол-
няющими полезные манипуляции с полями.

        Наследование позволяет дочернему объекту использовать поля  дан-
ных и методы родительского объекта, добавляя или изменяя их для реализа-
ции новых свойств:

        Circle = objekt (Point)
                   Radius : integer;
                   procedure Grоw(GrowBy : integer);
                   procedure Shrink(ShrinkBy : integer);
                 end;

        Круг отличается от  точки только тем,  что имеет радиус.  Методы
Grow и Shrink реализованы для изменения поля данных Radius. Все, что оп-
ределено в  объекте Point,  доступно в  Circle, т.е.  реализация ACircle
объекта   Circle   наследует   метод,   к   которому   можно  обратиться
ACircle.Shrink .

        В Turbo Pascal'е наследуемые методы можно просто заменять с  по-
мощью переопределения. Компилятор узнает, что идентификатор  переопреде-
ляет  при  разборе  нового  определения.  Ключевое слово Object Pascal'я
OVERRIDE оказывается  избыточным. Позднее  связывание реализовано  с по-
мощью объявления метода виртуальным (VIRTUAL). Объекты, наследующие друг
у друга свойства,  могут использовать одно  имя для виртуальных  методов
(например, Show), однако для каждого объекта реализация может  отличать-
ся. Какая реализация должна  выполняться при данном вызове  виртуального
метода не решается до выполнения программы (позднее связывание).  Тради-
ционные процедурные вызовы связываются во время компиляции.

        С помощью позднего связывания в Turbo Pascal 5.5 становится воз-
можным полиморфизм. Один вызов виртуального метода может принимать  раз-
личные формы в зависимости от типа объекта, для которого он вызывается.


            МОЖНО ЛИ ПЕРЕПИСАТЬ ЭТУ ПРОГРАММУ?

        Прежде чем  обсудить, как  переписать программу,  стоит рассмот-
реть, какие программы тяжело или невозможно объектно ориентировать.

        Начнем с вопроса, структурирована ли программа? За  неструктури-
рованные программы  лучше не  браться. Обычно  это паскалевские варианты
старых программ, написанных на Бейсике или Фортране.

        В некотором смысле ООП - это структура структур. Если  фундамен-
тальные программные структуры, такие как процедуры и записи, не  исполь-
зуются, переписывание на  ООП язык придется  начинать с нуля.  Возможно,
даже спецификации  придется переписывать,  поскольку неструктурированные
спецификации будут скорее тормозом при написании ООП программ.

        Еще один вопрос  может быть задан  в отношении любой  программы.
Кто-нибудь вообще понимает ее?  Старую, редко используемую и  плохо ком-
ментированную программу лучше отложить в сторону или переспецифицировать
и дать переписать тому, кто в глаза не видел старую программу.

        Еще один вопрос, менее ясный и причиняющий больше хлопот.  Зави-
сит ли программа или ее составные части от не ООП инструментов? Экранные
генераторы и тулбоксы для Паскаля попадают как раз под эту категорию.

        Эти продукты игнорируют объекты и требуют чтобы вызовы  процедур
и описания данных  были сделаны определенным  образом. Хотя Вы  и можете
сделать  программы,  использующие  инструменты,  ООП, инструменты скорее
всего станут источником фрустраций  и затруднят эволюцию программ  вдоль
естественных объектноориентированных путей. Более того, возможность  по-
вторного использования и расширения  модулей, включающих не ООП  инстру-
менты, будет очень ограничена или даже исключена.


                   ПЕРВЫЕ ШАГИ К ПЕРЕПИСЫВАНИЮ

        В отличие  от полностью  объектно-ориентированных языков,  таких
как Смолток, программирование на Turbo Pascal дает широкий выбор  уровня
объектно-ориентированности программы. Более того, Вы можете  конвертиро-
вать традиционную паскалевскую программу постепенно, не снижая ее произ-
водительности.

        Первые шаги очень просты. Удалите слова, вступающие в конфликт с
новыми  ключевыми  словами  и  предопределенными идентификаторами. Turbo
Pascal 5.5 прибавляет  только 4 новых  ключевых слова: OBJECT,  VIRTUAL,
CONSTRUCTOR и DESTRUCTOR. Если программа использует их или новые  предо-
пределенные идентификаторы Self и Fail, Вы должны выбрать новые  иденти-
фикаторы.


              ПОИСК ПОЧТИ ОБЪЕКТОВ В СТАРЫХ ПРОГРАММАХ

        Программисты часто удивляются как просто можно переписать  неко-
торые куски  программ в  объектно-ориентированной форме.  Это происходит
тогда, когда они  создают библиотеки программ  вдоль магистрали ООП,  не
догадываясь об их ориентации. Часто они делают все, что нужно, не считая
инкапсуляции.

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

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


              ОПРЕДЕЛЕНИЕ ЦЕНТРАЛЬНОГО ОБЪЕКТА ПРОГРАММЫ

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

        Такая структура данных - основа того, что происходит в программе
и весь остальной код в программе обслуживает ее в некотором смысле. Если
такая структура существует, она  несомненно, должна стать объектом.  Вся
штука в том, как узнать, какой код принадлежит центральному объекту, ка-
кой код просто находится рядом.

        Это не такая простая задача, если учесть, что в силу своей слож-
ности большие объекты могут (и должны) содержать более легкие и  простые
объекты. Документ  текстового процессора  - хороший  пример. Большинство
процессоров представляет  документ в  виде строк  текста. Сами  строки -
прекрасные кандидаты на объектификацию. Объект документ должен возложить
управление строками на объекты строки и заниматься лишь отношениями меж-
ду ними, например, перераспределять между ними данные при переформатиро-
вании абзацев.

        Очевидно, проводить границы легче, если держать в голове некото-
рую иерархию объектов. Одна из самых тяжелых проблем - проведение грани-
цы между структурами данных и пользовательским интерфейсом. Для увеличе-
ния скорости отображения данных на экране, центральная структура  данных
часто очень тесно связана с процедурами вывода на экран. Эта связь дела-
ет отделение (и возможность повторного использования)  пользовательского
интерфейса проблематичным.

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

        Есть и менее очевидные преимущества данного подхода. Если у  Вас
есть система поддержки, то сделав текстовый редактор или таблицу наслед-
ником объекта окна,  Вы можете легко  разделить экран между  несколькими
документами и таблицами.


                 ПОСЛЕДОВАТЕЛЬНОЕ ПРЕВРАЩЕНИЕ

        По мере разработки объектно-ориентированных подсистем для  новых
программ, попробуйте присоединить их к конвертируемой программе.  Огром-
ное преимущество ООП подсистем - это то, что они полностью отделяемы  от
самой программы. Если они  не конфликтуют с существующими  подсистемами,
Вы можете добавлять проверенную ООП подсистему всего лишь подлинковав ее
и вызвав ее методы.

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

                   ПРОГРАММА КАК ОБЪЕКТ

        Когда Вы занимаетесь ООП, Ваша программа превращается в  контей-
нер, набитый объектами. Но почему бы не разрабатывать программы, которые
сами являются объектами? Тогда сама программа может стать повторно испо-
льзуемой подсистемой большой программы.

        Такая программа-объект может  иметь всего два  метода: Activate,
который инициализирует и выполняет программу, и DeActivate, который  ос-
вобождает ресурсы,  используемые программой  и передает  управление про-
грамме, активировавшей эту программу, например, COMMAND.COM.

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

        Упаковывание такой  программы в  ООП обертку  может быть гораздо
более легким, чем попытка конвертировать ее запутанные внутренности.

        Если Вы воспользуетесь этой схемой, текстовый процессор  превра-
тится в объект документ, финансовая программа в объект гроссбух. Скрытое
преимущество этой схемы состоит в том, что гроссбух, документ или объект
электронная таблица могут быть полями записи в базе данных.  Аналогично,
документ может быть содержимым ячейки электронной таблицы или аргументом
формулы, возвращающей его размер, дату создания или булевское  значение,
определяемое тем, есть ли вхождение в него некоторой строки.


            ОСНОВНЫЕ НАПРАВЛЕНИЯ ПРЕОБРАЗОВАНИЯ

        Переписывание программ на ООП  - дело не безнадежное.  Вы можете
переписывать постепенно и продолжать настолько, насколько позволяют вре-
мя, Ваша энергия и сама старая программа.

        Вот простая стратегия для этой работы:

        - Найдите почти объекты в Вашей программе и сделайте их объекта-
ми. Делая это Вы приобретете некоторые навыки ООП быстрее, чем начиная с
нуля.

        - Составьте план ООП иерархии для будущих программ. Этот процесс
потребует  высокоуровневого  планирования  пользовательского интерфейса,
системы помощи, онлайн обучения  и других, достаточно независимых  и по-
вторно используемых подсистем.

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

        - По мере разработки объектно-ориентированных подсистем для  бу-
дущих программ,  постарайтесь добавлять  их к  переписываемой программе.
Этот шаг может потребовать  переписывания большой части программы,  если
она плохо принимает объекты.

        Время, которым  Вы располагаете,  в значительной  мере определит
объем переписывания.

        Какие опасности подстерегают Вас на этом пути?

        Во-первых, не пересолите. Не превращайте в объекты простые  типы
- перечислимые, числа, символы, булевские.

        Во-вторых, не используйте виртуальные методы, если позднее  свя-
зывание не необходимо. Вызовы статических методов эквивалентны по скоро-
сти и накладным расходам вызовам обычных процедур. Более того, интеллек-
туальный Борландовский линковщик отбрасывает все статические методы, ко-
торые никогда не используются, уменьшая длину программы. С  виртуальными
методами этого нельзя сделать в принципе.

        Не разрабатывайте иерархию так, чтобы приспособить ее к вывертам
Вашей не ООП программы. Повторное использование этой иерархии потянет за
собой эти выверты во все последующие программы.

        Помните, что Ваша цель - не переписывание ради переписывания. Вы
делаете программу  объектно-ориентированной чтобы  получить определенный
выигрыш, однако  это требует  затрат. После  преобразования программы Вы
можете обнаружить,  что "овчинка  выделки не  стоит". Однако,  Вы можете
сделать такое заключение просто из-за недостаточной информации об ООП  и
малого ООП опыта.

        Прежде чем конвертировать существующую программу, напишите  хотя
бы одну ООП программу с нуля. Дайте процессу переписывания шанс. Порази-
тельные преимущества ООП техники  будут проявляться в самых  неожиданных
местах.

                                            Перевод О.П.Пилипенко, 1991г.
