Б. Страуструп - Язык программирования С++ (1119446), страница 87
Текст из файла (страница 87)
В то же время, классы и иерархии классов очень полезны на стадиипроектирования, в частности, они дают нам большую определенность понятий, позволяют точно задатьвзаимоотношения между понятиями и помогают рассуждать о понятиях. По мере развития проекта этаопределенность и точность преобразуется во все более конкретные утверждения о классах и ихинтерфейсах.Важно понимать, что точно определенные и строго типизированные интерфейсы являютсяфундаментальным средством проектирования.
Язык С++ был создан как раз с учетом этого. Строго309Бьерн Страуструп.Язык программирования С++типизированный интерфейс гарантирует, что только совместимые части программы могут бытьскомпилированы и скомпонованы воедино, и тем самым позволяет делать относительно строгиедопущения об этих частях. Эти допущения обеспечиваются системой типов языка. В результатесводятся к минимуму проверки на этапе выполнения, что повышает эффективность и приводит кзначительному сокращению фазы интеграции частей проекта, реализованных разнымипрограммистами. Реальный положительный опыт интеграции системы со строго типизированнымиинтерфейсами привел к тому, что вопросы интеграции вообще не фигурируют среди основных тем этойглавы.Рассмотрим следующую аналогию: в физическом мире мы постоянно соединяем различные устройства,и существует кажущееся бесконечным число стандартов на соединения. Главная особенность этихсоединений: они специально спроектированы таким образом, чтобы сделать невозможным соединениедвух устройств, нерассчитанных на него, то есть соединение должно быть сделано единственнымправильным способом.
Вы не можете подсоединить электробритву к розетке с высоким напряжением.Если бы вы смогли сделать это, то сожгли бы бритву или сгорели сами. Масса изобретательности былапроявлена, чтобы добиться невозможности соединения двух несовместимых устройств. Альтернативойодновременного использования нескольких несовместимых устройств может послужить такоеустройство, которое само себя защищает от несовместимых с ним устройств, подключающихся к еговходу. Хорошим примером может служить стабилизатор напряжения. Поскольку идеальнуюсовместимость устройств нельзя гарантировать только на "уровне соединения", иногда требуется болеедорогая защита в электрической цепи, которая позволяет в динамике приспособиться или (и)защититься от скачков напряжения.Здесь практически прямая аналогия: статический контроль типов эквивалентен совместимости науровне соединения, а динамические проверки соответствуют защите или адаптации в цепи.Результатом неудачного контроля как в физическом, так и в программном мире будет серьезный ущерб.В больших системах используются оба вида контроля.
На раннем этапе проектирования вполнедостаточно простого утверждения: "Эти два устройства необходимо соединить"; но скоро становитсясущественным, как именно следует их соединить: "Какие гарантии дает соединение относительноповедения устройств?", или "Возникновение каких ошибочных ситуаций возможно?", или "Каковаприблизительная цена такого соединения?"Применение "статической типизации" не ограничивается программным миром. В физике и инженерныхнауках повсеместно распространены единицы измерения (метры, килограммы, секунды), чтобыизбежать смешивания несовместимых сущностей.В нашем описании шагов проектирования в $$11.3.3 типы появляются на сцене уже на шаге 2(очевидно, после несколько искусственного их рассмотрения на шаге 1) и становятся главной темойшага 4.Статически контролируемые интерфейсы – это основное средство взаимодействия программныхчастей системы на С++, созданных разными группами, а описание интерфейсов этих частей (с учетомточных определений типов) становится основным способом сотрудничества между отдельнымигруппами программистов.
Эти интерфейсы являются основным результатом процесса проектирования ислужат главным средством общения между разработчиками и программистами.Отказ от этого приводит к проектам, в которых неясна структура программы, контроль ошибок отложенна стадию выполнения, которые трудно хорошо реализовать на С++.Рассмотрим интерфейс, описанный с помощью "объектов", определяющих себя самостоятельно.Возможно, например, такое описание: "Функция f() имеет аргумент, который должен быть самолетом"(что проверяется самой функцией во время ее выполнения), в отличие от описания "Функция f() имеетаргумент, тип которого есть самолет" (что проверяется транслятором). Первое описание являетсясущественно недостаточным описанием интерфейса, т.к.
приводит к динамической проверке вместостатического контроля. Аналогичный вывод из примера с самолетом сделан в $$1.5.2. Здесьиспользованы более точные спецификации, и использован шаблон типа и виртуальные функции взаменнеограниченных динамических проверок для того, чтобы перенести выявление ошибок с этапавыполнения на этап трансляции.
Различие времен работы программ с динамическим и статическимконтролем может быть весьма значительным, обычно оно находится в диапазоне от 3 до 10 раз.Но не следует впадать в другую крайность. Нельзя обнаружить все ошибки с помощью статическогоконтроля. Например, даже программы с самым обширным статическим контролем уязвимы к сбоям310Бьерн Страуструп.Язык программирования С++аппаратуры. Но все же, в идеале нужно иметь большое разнообразие интерфейсов со статическойтипизацией с помощью типов из области приложения, см. $$12.4.Может получиться, что проект, совершенно разумный на абстрактном уровне, столкнется с серьезнымипроблемами, если не учитывает ограничения базовых средств, в данном случае С++.
Например,использование имен, а не типов для структурирования системы приведет к ненужным проблемам длясистемы типов С++ и, тем самым, может стать причиной ошибок и накладных расходов привыполнении. Рассмотрим три класса:class X {f()g()}// pseudo code, not C++class Y {g()h()}class Z {h()f()}используемые некоторыми функциями бестипового проекта:k(a, b, c){a.f()b.g()c.h()}// pseudo code, not C++Здесь обращенияX xY yZ zk(x,y,z)k(z,x,y)// ok// okбудут успешными, поскольку k() просто требует, чтобы ее первый параметр имел операцию f(), второйпараметр - операцию g(), а третий параметр - операцию h(). С другой стороны обращенияk(y,x,z);k(x,z,y);// fail// failзавершатся неудачно.
Этот пример допускает совершенно разумные реализации на языках с полнымдинамическим контролем (например, Smalltalk или CLOS), но в С++ он не имеет прямогопредставления, поскольку язык требует, чтобы общность типов была реализована как отношение кбазовому классу. Обычно примеры, подобные этому, можно представить на С++, если записыватьутверждения об общности с помощью явных определений классов, но это потребует большогохитроумия и вспомогательных средств. Можно сделать, например, так:class F {virtual void f();};class G {virtual void g();};311Бьерн Страуструп.Язык программирования С++class H {virtual void h();};class X : public virtual F, public virtual G {void f();void g();};class Y : public virtual G, public virtual H {void g();void h();};class Z : public virtual H, public virtual F {void h();void f();};k(const F& a, const G& b, const H& c){a.f();b.g();c.h();}main(){X x;Y y;Z z;k(x,y,z);k(z,x,y);k(y,x,z);k(x,z,y);}////////okokerror F required for first argumenterror G required for second argumentОбратите внимание, что сделав предположения k() о своих аргументах явными, мы переместиликонтроль ошибок с этапа выполнения на этап трансляции.
Сложные примеры, подобные приведенному,возникают, когда пытаются реализовать на С++ проекты, сделанные на основе опыта работы с другимисистемами типов. Обычно это возможно, но в результате получается неестественная и неэффективнаяпрограмма. Такое несовпадение между приемами проектирования и языком программирования можносравнить с несовпадением при пословном переводе с одного естественного языка на другой. Ведьанглийский с немецкой грамматикой выглядит столь же неуклюже, как и немецкий с английскойграмматикой, но оба языка могут быть доступны пониманию того, кто бегло говорит на одном из них.Этот пример подтверждает тот вывод, что классы в программе являются конкретным воплощениемпонятий, используемых при проектировании, поэтому нечеткие отношения между классами приводят кнечеткости основных понятий проектирования.12.1.4 Гибридный проектПереход на новые методы работы может быть мучителен для любой организации.
Раскол внутри нее ирасхождения между сотрудниками могут быть значительными. Но резкий решительный переход,способный в одночасье превратить эффективных и квалифицированных сторонников "старой школы" внеэффективных новичков "новой школы" обычно неприемлем. В то же время, нельзя достичь большихвысот без изменений, а значительные изменения обычно связаны с риском.Язык С++ создавался с целью сократить такой риск за счет постепенного введения новых методов. Хотяочевидно, что наибольшие преимущества при использовании С++ достигаются за счет абстракции312Бьерн Страуструп.Язык программирования С++данных, объектно-ориентированного программирования и объектно-ориентированного проектирования,совершенно неочевидно, что быстрее всего достичь этого можно решительным разрывом с прошлым.Вряд ли такой явный разрыв будет возможен, обычно стремление к усовершенствованиямсдерживается или должно сдерживаться, чтобы переход к ним был управляемым.