Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 193
Текст из файла (страница 193)
Какие операции должен он предоставить? Мы можем ввести только те операции, которые может поддержать каждый контейнер (пересечение множеств операций), но это до смешного узкий интерфейс, цзактически, во многих интересных случаях это пересечение представляет собой пустое множество, В качестве альтернативы мы можем ввести объединение всех множеств операций и выдавать ошибку во время выполнения, если через интерфейс к объекту будет применена «несуществующая операпия». Интерфейс, представляющий собой такое объединение интерфейсов к множеству концепций, называется жирным интерфейсом.
Рассмотрим «универсальный контейнер» объектов типа л' 838 Глава 24. Проектирование и программирование с1авв Сопса1пег( риЫ(а в!гис1Вай орег( //классисключений сопв1 сЬаг" р, Ьай орег(сопв1 сЬаг' рр):р (рр) () ыг1иа1 ооы ри1(соав1 т ) (1ьгою Вой орег("солса!пег риГ); ) о!г(иа! Т'де!()(1ЬготоВай орег( Сои!а!пегьуер) ) о!г(иа! Т"й орега1ог() (!и!) ( 1ЬгоиВай орег('Соп1а!пег:.,'] (!п()"; ) оойиа! Т'йорега1оП) (сопв1 слог*)(1ЬговВай орег('Соп1а1пегг() (сЬаг")'),) //.- Тогда конкретные контейнеры можно обьявить так: с1авв 1лв! соп1а!пег; риЫгс Соп1а!пег, ргта1е !!в! ( риЬВс: иоЫ ри1 (сопв1 Т'); Т' де!(); // ...
нет оператора орега1ог() ... ), с!авв гес1ог соп1а!пег риЬВс Соп1атег, рпоа1е оес1ог ( риЫ!с: Т"& орега1огП (т1), Т" й орега1ог() (сопв1 сЬаг'); // ... нет функг!иг! ри1 () и де(О... Пока мы внимательны, все в порядке: оо!йЩ 1лв1 соп1атегвс, (гес1ог соп1а1пег ос; //. иьег (вс, ос); ооЫ ивег (Соп1а(пегй с1, Соп!атегй с2) Т" р ! = с! д е1 (); Т" р2 = с2(3); // не используются с2.де1() ив!(3) //- Однако це многие структуры данных хорошо поддерживают и индексацию, и операции в стиле списков.
Поэтому, вероятно, это не очень хорошая идея — опрелелить интерфейс, требующий и то, и другое. Это приводит к интенсивным опросам во время выполнения Я 15.4) нлн к обработке исключений !глава 14), чтобы избежать ошибок времени выполнения, Например; иоЫ ивег2 (Соп1агпегй с1, Соп!ат егй с2) О обнарузеение легко, но исправление // чожет оказаться тяяселии 839 24.5. Советы ( ггу ( Т'р! =с!.уег(); т рг=сг(2]; О. ) са1сб (Соп1агпегьвас( орега бал]) ( // Олиибка( // И ныло теперь? ) или ио!л1 иеегб (Сон!а!пега с1, Сопсагпегб с2] // раннее обнаружение (рпомительнол //исправление по-прежнему //может бить тяжвлилл ф'(йупатгс саегкИв1 соп1атег'> (бс1] 88 л(упат!с сае1< гес1ог сои!а!пег'> (йсг]] ( Гр!=с1 уе1(], Г р2 = сг(б]; //- е1ее ( О Ошибка! О И нто теперь? В обоих случаях может пострадать быстродействие, и сгенерированный код может оказаться на удивление большим.
В результате программисты будут соблазняться не обращать внимания на потенциальные ошибки и надеяться, что на самом деле их не возникнет, когда программа окажется у пользователей. Проблема с данным подходом состоит в том, что исчерпывающее тестирование также тяжело и дорого. Г!озтому жирных интерфейсов лучше избегать, когда на первое место ставится быстродействие, когда требуются строгие гарантии корректности, и вообще когда есть хорошая альтернатива. Использование жирных интерфейсов ослабляет взаимосвязь между концепциями и классами и таким образом открывает простор для использования наследования как просто удобства реализации. 24.5. Советы [1] Лктивнее используйте абстракцшо данных и объектно-ориентированное программирование; 9 24.2 ]2] По мере надобности (только) используйте методы и приемы Сл-ь; 9 24.2.
]3] Стремитесь к соответствию стилей программирования и проектирования; 9 24.2.1. ("4] Фокусируйте проектирование прежде всего на классах/концепциях, а не на функциях/обработке; 9 24.'2.2. (5] Пользуйтесь классами для представления понятий; 9 24.2.1, 9 24.3. ]б] Пользуйтесь паследовашлем (только) для представления иерархических взаимосвязей между понятиями; 9 24.2.2, 9 24.2,5, 9 24.3.2. 840 Глава 24. Проектирование и программирование [7] [8] [9] [10] [11] [12] [13] [14] [151 [1 6] [17] [18] [19] [20] [21] [221 [23] [24] [25] [26] [27] [281 [29] [30] Выражайте строгие гарантии относительно интерфейсов в терминах статичес- ких типов прикладного уровня; 9 24.2.3, Для облегчения решения четко определенных задач пользуйтесь генераторами программ и средствами прямого манипулирования; 6 24.2Л.
Избегайте генераторов программ и средств прямого манипулирования, кото- рые плохо стыкуются с универсальными языками программирования; 9 24.2А. Удерживайте разные уровни абстракции раздельными; 9 24.3.1. Фокусируйте внимание на проектировании компонент; 9 24 А. Убедитесь, что виртуальные функции имею~ четко определенный смысл, и что все замещающие функции реализуют желаемое поведение; 9 24.3А, З 24.3.2,1. Для выражения отношения и-а (является) пользуйтесь открытым наследова- нием; 9 24.3А.
Для выражения отношения Ьах-а (содержит) пользуйтесь членством; 9 24.3.4. Для выражения простого включения (содержания в себе) предпочитайте непос- редственное членство, а не с указатель на объект; 9 24.3.3, 24.3А. Убедитесь, что зависимости использования понятны, не циклические (по воз- можности) н минимальны; в 24.3.7.2. Для всех классов определяйте инварианты; 9 24.3.7.1. Предусловия/постусловия и другие утверждения явно выражайте утвержде- ниями (возможно, с использованием АззеИ []); 9 24.3.7.2. Определяйте интерфейсы так, чтобы они открывали (необходимый) минимум информации; в 24А. Миниыизируйте зависимости интерфейса от других интерфейсов; 9 24 А.2.
Поддерживайте строгую типизацию интерфейсов; 9 24А.2. Выражайте интерфейсы в терминах типов прикладного уровня; 9 24А,2. Выражайте интерфейсы так, чтобы запрос к интерфейсу можно было передать на удаленный сервер; 9 24.4.2. Избегайте жирных интерфейсов; 9 24А.2.
Где только возможно применяйте закрытые члены данных и функции-члены; 9 24 А.2. Пользуйтесь различением «защищенный/открытый» для учета различий по- требностей проектировщиков производных классов и конечных пользовате- лей; 9 24А.2.
Пользуйтесь шаблонами для обобщенного программирования; 9 24.4.1. Пользуйтесь шаблонами для параметризации политики выполнения алгорит- мов; ~ 24А.1. Пользуйтесь шаблонами, когда необходимо, чтобы разрешение типов имен про- изводилось во время компиляции; 9 24А.1. Пользуйтесь иерархией классов, когда необходимо, чтобы разрешение типов про- изводилось во время выполнения; 9 24.4.1. Роли классов Кое-что лучше изменитть ... но фундв иентольпие тем ьс должны упиваться своил~ упорстволс — Стефен Днс. Гоулд Разновидности классов — конкретные типы — абстрактные типы — узлы— изменяющиеся интерфейсы — объектный ввод/вывод — действия — интерфейсные классы — вспомогательные классьс-дескрипторы — использование счетчиков— прикладные среды разработки — советы — упражнения.
25.1. Разновидности классов Класс в С++ — это конструкция языка программирования, которая служит разнообразным потребностям проектирования. Я нахожу, что многис запутанные проблемы проектирования решаются введением какого-то нового класса, представляющего некоторое понятие, которое не было явным в предыдугдем эскизе проекта (возможно, также приходится удалять некоторые классы), Огромное разнообразие ролей, которые может играть класс, приводит к множеству разновидностей классов, каждая из которых хорошо приспособлена крещениючастных задач.
В этой главе описываются несколько классов-архетипов, их лостоинства и недостатки: з 25.2 Конкретные типы з 25.3 Абстрактные типы з 25А Узлы з 25.5 Операции ~ 25.6 Интерфейсы з 25.7 Вспомогательные классы з 25.В Прикладные среды разработки Эти «разновидности классов» являются понятиями проектирования, а не языковыми конструкциями. Недостигнутый и, вероятно, недостижимый идеал заключается в том, чтобы получить минимальный набор простых и ортогональных разновидностей классов, из которых можно конструировать полезные, хорошо ведущие себя классы.
Важно отметить, что в проекте встречаются все разновидности, и ни одна из нпх по природе своей не лучше других во всех отношениях. Большая путаница при обсуждении вопросов проектирования и программирования исходит от людей, старающихся использовать исключительно одну или две разновидности классов.
Обычно это дела- 842 Глава 25. Роли классов ется во имя простоты, однако приводит к искаженному и неестественному применению этих излюбленных ил«и разновидностей классов. Приведенное здесь описание делает упор на чистые формы этих разновидностей классов. Естественно, используются и гибридные формы. Однако гибрид должен появляться в результате проектного решения, основанного на оценке инженерных альтернатив, а не в результате ведущей в никуда попытки избежать принятия решений.
«Откладывание решения» слишком часто является завуалированной формой «отказа думать». Новичкам в проектировании обычно лучше держаться подальше от гибридов и следовать стилю существующих компонент, имеюгцих свойства, схожие с теми, что они стремятся достичь.