Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [ 43 ] 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

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

В структурной системе код, который выполняет воспроизведение, является внешним. Некая функция получает откуда-то объект, после чего делает различные системные вызовы для вывода его на экран. Если вы говорите о printf(), то вызовы не очень сложные, но если речь заходит о Windows или Motif - у вас появляется проблема. Объектно-ориентированный проект фактически является вывернутым наизнанку в сравнении со структурным проектом.

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

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

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



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

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

96. Объект производного класса является объектом базового класса

97. Наследование - это процесс добавления полей данных и методов-членов

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



5 Они не передаются. Даже в Smalltalk есть только один объект, который или получает сообщение, или нет. Несмотря на это, интерпретаторы Smalltalk склоняются к реализации обработки сообщений при помощи нескольких таблиц указателей на функции, по одной на каждхй класс. Если интерпретатор не может найти обработчик сообщения в таблице диспетчеризации производного класса, то он просматривает таблицу базового класса. Этот механизм не используется в Си++, который является компилируемым языком и поэтому не использует многоуровневый просмотр таблиц в время выполнения. Например, даже если бы все функции в базовом классе были виртуальными, то таблица виртуальных функций производного класса имела бы по ячейке для каждой виртуальной функции базового класса. Среда времени выполнения Си++ не просматривает иерархию таблиц, а просто использует таблицу для текущего объекта. Подробнее об этом позднее.

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

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

В этом вопросе путаница создана многими книгами по языку Smalltalk, описывающими реализацию во время выполнения системы обработки сообщений так, как если бы сообщения передавались от производного к базовому классу.5 Это просто неверно (и в случае Smalltalk, и в случае Си++). Си++ использует наследование. Производный класс - это тот же базовый класс, но с несколькими добавленными полями и обработчиками сообщений. Следовательно, когда объект Си++ получает сообщение, он или обрабатывает его, или нет; он или определяет обработчик, или получает его в наследство. Если ни то, ни другое не имеет места, то сообщение просто не может быть обработано. И оно никуда не передается.

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



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [ 43 ] 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76