Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 186
Текст из файла (страница 186)
Например, аппаратные и программные ресурсы сис- темы могут стать хорошими кандидатами в прикладные классы. В этом отражается тот факт. что систему можно рассматривать с разных точек зрения; то, что для одно- го — деталь реализации, для другого — характерная черта прикладной области. Хо- рошо спроектированная система содержит классы, поддерживающие логически раз- ные взгляды на систему. Например: [1] классы, представляющие прикладные понятия (например, легковые машины и грузовики); [2] класськ представляющие обобщения прикладных понятий (напригиер, транспортные средства); [3] классы, представляющие аппаратные ресурсы (например, класс, управляющий распределением памяти); [4] классы, представляющие ресурсы системы (например, потоки вывода); [5] классы, используемые для реализации других классов (например, списки, очереди,блокировки), [6] встроенные типы данных и управляющие структуры.
В более сложных системах сохранять разделение между логически разными типами классов и нс смешивать уровни абстракции становится нетривиальным делом. В про- стых системах можно выделить три уровня абстракций: [1+2] уровень прикладного представления системы; [3+4] уровень, отражающий компьютер, на котором выполняется моделирование; [5+6] уровень, отражающий низкоуровневую (языка программирования) реализацию. Как правило, чем больше система, тем больше уровней абстракции требуется для ес описания, и тем труднее становится определять и поддерживать эти уровни.
Отме- тим, что такое разделение на уровни абстракции имеет прямые соответствия в приро- де и в других видах человеческих конструкций. Например, можно считать, что дом состоит из; [1] атомов; [2] молекул; [3] бревен и кирпичей; [4] полов, степ и потолков; [5] комнат. Пока эти уровни абстракции разделены, вы можете поддерживать согласованный взгляд на дом. Однако, если их смешать, возникают нелепости, Например, высказы- вание «Мой дом состоит из нескольких тысяч фунтов углерода, нескольких сложных полимеров, 5000 кирпичей, двух ванных комнат и 13 потолков» звучит глупо. Учи- тывая абстрактную природу програм»к равносильное по глупости высказывание о сложной системс не всегда так легко узнается.
Перевод понятия прикладной области в класс проекта — не простая механическая операция. Она часто требует понимания. Учтите, что понятия прикладной области сами по себе являются абстракциями. Например, «налогоплательщики», «монахи» н «сотрудники» на самом деле в природе не существуют; эти понятия — просто ярлы- ки, которые навешивают на личностей, чтобы классифицировать их в соответствии с некоторой системой. Реальный или даже воображаемый мир (например, литература, особенно научная фантастика) иногда выступает просто в качестве источника поня- Глава 24, Проектирование и программирование 810 24.3.2.
Иерархии классов Рассмотриы моделирование уличного городского движения, чтобы примерно определить наиболее вероятное время, требуемое машинам экстренных служб (полиция, скорая помощь, пожарные), чтобы добраться до места аварии. Ясно, что нам нужно представить легковые машины, грузовики, машины скорой помощи, пожарные машины, полицейские автомобили, автобусы и т.
д, Появляется наследование, поскольку понятия реального мира не существуют изолированно друг от друга; они связаны друг с другом множеством связей. Без осознания этих взаимосвязей мы не воспринимаем понятия. Поэтому модель, не выражающая этих взаимосвязей, не адекватно отражает наши понятия. То есть нам нужно, чтобы классы выражали понятия, но этого не достаточно. Нам также нужны способы представления связей между классами. Одним из мощных средств непосредственного представления иерархических связей является наследование. В нашем примере мы, вероятно, особо выделим автомобили экстренных служб, а также захотим различать легковые и грузовые машины.
Это приведет к следующей иерархии классов; )гв/ис1в Атби(аавв Р(ге вая(ав (скорая помощь) (пожариая машина) Ооой ааИ 1а~Ывг (пожариая машина с лестницей) Ройсе саг (полиция ) ' Я все равно не смог бы вынести такого беспорядка на экране. тий, которые при преобразовании в классы радикально меняются. Например, экран моего персонального компьютера не напоминает поверхность рабочего стола, несмотря на то, что он был разработан, чтобы служить метафорой рабочего стола', а окна на экране имеют лишь самое отдаленное отношение к тем затейливым приспособлениям, которые вызывают сквозняки у меня в кабинете. Смысл лгоделирования реальности не в том, чтобы рабски копировать то, что мы видим, а скорее использовать это в качестве отправной точки для проектирования, в качестве источника вдохновения и якоря, за который можно зацепиться, когда неосязаемая природа программ угрожает превзойти нашу способность понимания собственного продукта.
Предостережение: начинающим часто оказывается трудно «найти классы>, но эта трудность вскоре преодолевается без долговременных неприятных последствий. Однако потом часто наступает фаза, когда классы — и их нас.ледственные отношения— начинают неуправляемым образом размножаться. Это может вызвать долговременные проблемы со сложностью, понятностью и эффективностью получающийся программы. Не надо каждую сиюминутную деталь представлять отдельным классом, и не всякую взаимосвязь нужно выражать как наследование. Постарайтесь помнить, что цель проекта — смоделировать систему с соответствующим уровнем детализации и на соответствующем уровне абстракции.
Нахождение баланса между простотой и общностью — не простое дело. а1) 24.3. Классы Здесь Егпегуепсу представляет понятие транспортного средства некоторой экстрен- ной службы, существенное для нашего моделирования: оно может нарушать кое-ка- кие правила дорожного движения, в случае движения по вызову имеет преимущества на перекрестках, его контролирует диспетчер и т, д. Вот версия на С+-~-; с1азв УяЫс1е(/" ...'/), с!авзЕгпегуепсу(/" ...'/); с!авв Саг риЫ1с У«Ыс(е(/' -.
"/); с!авя Тгисй: риЫ1с Уяй!с1е(/*.. */); с!азв Ро!!се саг; риЫ!с Саг, рго1ес1еа Етегдепсу ( /' ... */); с!авзАтЬи(апсе.риЫ!с Саг, ргогесгег!Етегуепсу(/' ... "/), с!аяв Риге епу!пе: риЫк Тгисй, рго1есгег(Ет егуепсу ( /' ... */ ), с1аяяНоой апг( (агй(ег: риЫ!ср!ге епу!пе(/* ... */); Наследование — это связь самого высшего уровня, которая выражается на С++; на ранних стадиях проектирования оно играет важнейшую роль. Часто для выражения связи существует выбор между наследованием и членством. Рассмотрим альтернативное понятие того, что значит быть транспортным средством экстренной службы: транспортное средство является экстренным, если у него есть проблесковый фонарь («мигалказ). Это позволило бы упростить иерархию классов, заменив классЕгпегдепсу членом в классе УеЫс1е: Уе!ис1е (ер1) г -2'-- Ро!!се саг АтЬи1аися Г!ге епфпе Ноой апИ 1агЫег Здесь ер1г означает «указатель на экстренностьг.
Теперь класс Етегдепсу просто используется как член в классах, которым может понадобиться действовать как транспортныс средства экстренной службы: с!аяв Етегуепсу(/ ... /); с(аяз УяЫс!е ( рго1есггг( Етегцепсу'яр1г; /" ... */), с!аяя Саг риЫк Узй!г!я(/' ... */); с1азз Тгигй риЫк Уяй!с(я (/* ... */); с!азз Ро!ке саг рибдс Саг ( /' ...
"/); с!азвАтЬи!апсе риЫ!с Саг(/'... '/); с!аяя Ргге епуспе;риЫгс Тгисй ( /' ... */); с1авзНоой апб (аг(г!егриЫ!ср1гя епд!пе (/* ... */); Здесь, транспортное средство является транспортным средством экстренной службы, если УеЫс(еэер1гне равен нулю. «Простыез легковые автомобили и грузовики инициализируются с Уей!с1еэер1г равным нулю, в остальных случаях Уей(с(есер1г не равен нулю. Например: //яонструкторлегкооого аятояобиля СаггСаг () ( ер1г=у; 812 Глава 24.
Проектирование н программирование // конструктор полицейской машины Райае гаг.радев гаг)) 1 ерГг = пега Етегувпеу; ) Определение вещей таким образом делает возможным простое преобразование из транспортного средства экстренной службы в обычное и наоборот: ааа)ЯЪепге)е*р) Ые1еге р->ергг; р->ергг=у; // теперь эта не транвпоргпнае средство // экстренной службы р->ерьг= пвгаЕтегуепау; //снова появились «экспгренносгпь» Итак, какой вариант иерархии классов лучше? Обгций ответ таков: «Лучше та про грамма, которая лучше моделирует интересующие нас аспекты реального мира>. То есть, выбирая между моделями при неизбежных ограничениях эффективности и простоты мы должны стремиться к большему реализму.
В данном случае легкость преобразования обычных транспортных средств в транспортные средства экстренных служб мне кажется неестественной, 11ожарные машины и машины скорой помощи имеют особую конструкцию, укомплектованы обученным персоналом и работают под управлением диспетчера, что требует специального оборудования для связи. Это соображение показывает,чтопонятие «транспортноесредство экстреннойслужбы» должно бытй фундаментальным и представляться в программе непосредственно, чтобы улучшить проверку типов и сделать осмысленным использование инструментальных средств. Если бы мы моделировали некую задачу, гле роли автомобилей менее четко определены — скажем, области, где для доставки спецперсонала к месту происшествия используются частные автомашины, и где связь обеспечивается портативными радиопередатчиками — более подходящим может оказаться другой способ моделирования. Для тех, кому моделирование уличного движения кажется надуманным примером, может быть, стоит напомнить, что задача выбора между наследованием и членством возникает при проектировании постоянно.
См. также эквивалентный пример с полосой прокрутки пз 8 24.3.3. 24.3.2.1. Зависимости внутри иерархии классов Ес1ественно, производные классы зависят от базовых. Не так часто замечают, что может быть справедливо и обратное'. Если в классе есть виртуальная функция, он зависит от производных классов, выполняющих часть его обязанностей, при условии, что эти производные классы замепйагот его виртушгьные функции. Если какой-то член базового класса вызывает виртуальную функцию этого класса, то базовый класс зависит от своих производных в самой своей реализации.
Аналогично, если какой-то ' Это наблюдение было сформулировано так; «Безумие наследственно. Вы заражаетесь пм от своих детей». 813 24.3. Классы класс пользуется защищенными членами, то он опять же зависит от своих производ- ных классов в самой свЬей реализации. Рассмотрим пример: с1авв В ( l/... рго1ес1ед: 1пг а; риЫ(с. и(гги а1 ш11 (; (п1 у () ( 1пг х =Я; гегигп х — а; ) ); Что делает у()? Ответ критическим образом зависит от определения Т() в некотором производном классе.