А. Александреску - Современное проектирование на C++ (1119444), страница 10
Текст из файла (страница 10)
Реализации двух классов, основанных на стратегии лггау, приведены ниже. севр1асе <с1азз т> зсгцсс сьдггау ( тб в1евепслс(т* рсг, цпз!дпед 1пс )пдех) Часть!. Методы ( гесигп рсг(э'пдех); соме тб д1епепсдс(т' рсг, ипзэопед 1пс эпг)ех) сомс ( гесигп рсг(эпдех); сепр1асе <с1азз ть зсгисс сэносдггау (); Проблема эаклвэчается в том, что предназначение стратегии дггау (указать, ссылается интеллектуальный указатель на массив нли нет) плохо согласованно с другой стратегней— разрушением. Уничтожать указатели на обьекты можно с помощью оператора ое1есе, а указатели на массивы, состоящие из объектов, — оператором ое1есе Ц.
Две независимые друг от лруга стратегии являются ортогональными. На основании ланного определения можно утверждать, что стратегии дггау и реэсгоу не являются ортогональными. Для того чтобы описать способ работы с величинами, хранягцимися в массиве, и способ уничтожения объектов в виде отдельных стратегий, необходимо описать их взаимодействие. Например, в стратегии дггау„кроме функций, можно прелусмотреть булевскую константу и передать ее в стратегию оезсгоу. Это осложнлет и несколько ограничивает процесс проектирования обеих стратегий.
Неортогональные стратегии несовершенны, поэтому нужно стараться их избегать. Они снижают уровень типовой безопасности во время компиляции и усложняют процесс разработки как главного класса, так и классов стратегий. Если приходится прибегать к неортогональным стратегиям, нужно хотя бы минимизировать их взаимную зависимость, передавая класс стратегии в качестве аргумента шаблонной функции другого класса стратегии.
Этот способ компенсирует недостатки, связанные с неортогональностью, за счет гибкости, присущей интерфейсам, основанным на шаблонах. Теневой стороной этого метода остается тот факт, что одна стратегия должна явно задавать некоторые детали реализации других стратегий. Это снижает степень инкапсуляции. 1.13.
Резюме Проектирование — это выбор. Чаше всего проблема заключается не в том, что задачу в принципе невозможно решить, а в том, что у нее существует множество способов решения. Необходимо знать, какие из возможных решений удовлетвори~ельно решают поставленную задачу. Зто вынуждает нас переходить от высших архитектурных уровней к низшим. Более того, выбранные варианты можно комбинировать, что приводит к появлению "проклятия выбора" из большого количества вариантов. Для того чтобы преодолеть "проклятие выбора" с помощью кода, имеющего разумные размеры, разработчики библиотек, предназначенных для проектирования программного обеспечения, должны изобретать и применять специальные способы, Изначально эти методы были предназначены для обеспечения гибкое~и при автоматической генерации кода в сочетании с небольшим количеством элементарных механизмов (рппийче деисез).
Сами по себе библиотеки предоставляют огромное количество таких механизмов. Более того, они содержат гпеяифихации (зрес!Дсайопз), на основе которых создаются новые механизмы, так что пользователь может создавать их самостоятельно. Зто существенно влияет на открытость (ореп-епг)ед) проектирования, Глава 1. Разработка классов на основе стратегий основанного на стратегиях. Эти механизмы называются сарагпегиями, а их реализации, соответственно, называются классами серагпегий. Механизм стратегии основан на комбинации шаблонов и множественного наследования. Класс, использующий стратегии (главный класс), представляет собой шаблон со многими шаблонными параметрами (часто называемыми шаблонными шаблоииыми параметрими)„каждый из которых является стратегией.
Главный класс "переадресовывает" части функциональных возможностей стратегиям и действует в качестве хранилища нескольких согласованных между собой стратегий. Классы, построенные на основе стратегий, обладают богатыми возможностями и элегантно раскладываются иа функциональные свойства.
Стратегия может обеспечивать функциональное свойство, передаваемое главному классу с помощью открытого наследования. Более того, главный класс может реализовывать расширенные функциональные возможности, которые используют факультативные свойства стратегий, Если факультативные функциональные возможности не предусмотрены, главный класс компилируется успешно при условии, что расширенные функциональные возможности не используются. Мощь стратегий проявляется через их способность смешиваться и настраиваться.
Класс, основанный на применении стратегий, может реализовывать различные режимы работы, комбинируя более простые виды поведения, прелусмотренные стратегиями. Это превращает стратегии в эффективное средство против "проклятия выбора". Используя классы стратегий, можно настраивать не только режим работы, но и структуру. Это важное свойство выводит проектирование, основанное на стратегиях, за рамки простого генерирования типов, характерного для контейнерных классов.
Классы, основанные на стратегиях, обеспечивают гибкость преобразований. При использовании копирования "стратегия за стратегией" каждая стратегия может распознавать, к каким еше стратегиям она имеет доступ, или преобразовываться в них с помощью соответствующего конструктора преобразования, оператора преобразования или обоих одновременно. Разбивая классы на стратегии, нужно слеловать двум вюкныы принципам. Первый из них . - следует локализовать, назвать и изолировать проектные решения в классе.
Эта цель является предметом компромисса и может достигаться различными путями. Второй принцип -- нужно искать ортогональные стратегии, т.е. стратег~и, не нужлаюшиеся во взаимодействии друг с другом и способные изменяться независимо друг от друга. Часть Е Мвтоды В этой главе описывается множество приемов программирования на языке С++, используемых на протяжении всей книги. Поскольку эти приемы оказываются полезными в разных ситуациях, они описываются в максималыю обшем виле, чтобы их легко было применить повторно. Таким образом, олин и тот же прием может использоваться в разных ситуациях. Некоторые из этих приемов, например, частичная специализация шаблонов, представляют собой особенности языка программирования. Другие, например, диагностические утверждения на этапе компиляции (сошр)1е-ниве аззенюпз), программируются о глельно.
В этой главе рассматриваются следующие приемы и инструменты программирования. ° Статическая проверка диагностических утверждений. ° Частичная специализация шаблонов. ° Локальные классы. ° Отображения между типами и значениями 1шаблонные классы тпт2туре и туре2туре). ° Шаблонный класс ве1ест, средство лля выбора типа на этапе компиляции на основе проверки логического выражения. ° 1'аспознаванис конвсртирусмости и наследования на этапе компиляции.
° Класс туретпто, улобная оболочка лля класса зтд::туре )п1о. ° класс тга)тэ, коллекция характеристик (1ппм), применимых к любому типу в языке С++. Взятые по отдельности, каждый прием и соответствуюший ему кол могут выглялегь тривиально. Обычно их размер не превышает 5 — 1О вполне понятных строк. Однако эти приемы программирования обладаюз важной особенностью: они "нетерминальны", т.е.
их можно комбинировать лруг с другом, генерируя идиомы высокого уровня. В совокупности они образуют солилную основу для служебной библиотеки, позволяюшей создавать мошные архитектурные конструкции. Приемы иллюстрируются примерами, а не сухим описанием. Читая лругие главы книги, вы можете возврашаться к этой главе за справками. 2.1. Статическая проверка диагностических утверждений С появлением обобщенного программирования на языке С++ возникла необходимость в более совершенных средствах статической проверки (и более конкретных сообщениях об ошибках). Допустим, что вы разрабатываете функцию лля безопасного приведения типов (за(е сап)пя).
Вам нужно привести один тип к другому, не потеряв при этом информацию, 'причем типы большего размера не должны преобразовываться в типы меньшего размера. тевр1ате «с1азз то, с1азв Ггов> то зауе гез'птегргет саят(егов Фгов) аззегс(зт'аеоб(ггов) <= азхеоб(то)); гетцгп гетпсегргет саят<то>(бгов); Эта функция вызывается с помощью точно такой же синтаксической конструкции, как и встроенное приведение типов в языке С++.