Анимация
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

Если концепцию абстрактной фабрики необходимо обобщить и реализовать в виде библиотеки, нужно предоставить пользователю возможность создавать фабрики произвольных наборов типов, а не только типов window. Button и Scroll ваг. Шаблоны этой способностью не обладают.

Хотя на первый взгляд шаблон Abstract Factory не предоставляет широких возможностей для абстрагирования и обобщения, некоторые моменты достойны более глубокого изучения.

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

2. Функциями-членами класса WidgetFactory невозможно свободно манипулировать (см. приведенный выше код). Набор сигнатур виртуальных функций в принципе невозможно сделать обобщенным. В качестве примера рассмотрим следующий код.

template <class Т>

Т* MakeRedwidget(widgetFactory& factory) {

Т* pw = factory.CreateTO; Невозможно!!! pw->setColor(RED); return pW;

Нужно вызвать функции Createwindow, CreateButton или CreateScrol 1 Bar, в зависимости от того, что представляет собой класс Т - класс wi ndow. Button или Scroll Bar соответственно. В языке С++ невозможно добиться такой подстановки текста.

3. И последнее (по порядку, но не по важности) - библиотеки только выиграли от того, что прекратились бесконечные дискуссии о соглашениях, принимаемых для записи имен (createwi ndow, create wi ndow или Createwi ndow), и тому подобных мелочах. В библиотеках реализован легкий и стандартный способ принятия подобных решений. Абстрактные фабрики также должны были бы иметь такую возможность.

Итак, подытожим наши пожелания. Что касается п. 1, было бы прекрасно, если бы мы смогли создавать объекты класса WidgetFactory, передавая список параметров шаблонному классу AbtsractFactory.

typedef AbstractFactory<Window, Button, ScrollBar> WidgetFactory;

Из п. 2 следует, что нам необходим шаблонный вызов для разных функций вида CreateA-A-A-, например, Create<window>(), Create<Button>() и т.д. Тогда мы смогли бы вызывать эти функции с помощью обобщенного кода.

template <class Т>

т* MakeRedwidget(WidgetFactory* factory) {

т* pw = Factory.create<T>(); вот это прекрасно! pw->SetColor(RED); return pw;



Однако мы не можем реализовать эти пожелания. Во-первых, оператор typedef для класса WidgetFactory применить невозможно, поскольку шаблоны не могут иметь переменное количество параметров. Во-вторых, шаблонный синтаксис Сге-ateXxxQ недопустим, так как виртуальные функции не могут быть шаблонными.

Итак, очевидно, что правила языка программирования не позволяют достичь высокого уровня абстракции и возможностей повторного использования кода.

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

3.2. Определение списков типов

По разным причинам язык С++ иногда позволяет профаммисту сказать: "Это лучшие пять строк кода, которые я когда-либо написал!". Возможно, это связано с семантическим богатством этого языка или с необычностью его свойств. По этой традиции списки типов выглядят крайне просто.

template <class т, class и>

struct Typelist

typedef т head; typedef и Tai1;

namespace TL {

... алгоритмы работы со списками типов ...

Все, что относится к спискам типов, за исключением определения самого класса Typelist, пребывает в пространстве имен TL. В свою очередь, пространство имен TL находится внутри пространства имен библиотеки Loki, как и весь код этой библиотеки. Для того чтобы упростить примеры, в этой главе упоминания пространства имен TL пропушены. Читателю следует помнить это, используя заголовочный файл Type-list, h. (Если вы забудете, компилятор вам напомнит!)

Класс Typelist содержит два типа. Доступ к ним обеспечивается внутренними именами Head и Tail. Вот именно! Нам не нужны списки типов, содержащие три и больше элементов, поскольку они у нас уже есть. Например, рассмотрим список типов, состоящий из трех вариантов типа char.

typedef Typelist<char, Typelist<signed char, unsigned char> > CharList;

(Обратите внимание на раздражающий, но необходимый пробел между двумя грамматическими лексемами (token) > в конце сфоки.)

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



Здесь используется одна особенность, состоящая в том, что шаблонный параметр может иметь любой тип и быть конкретизацией своего собственного шаблона. Это старое, хорощо известное свойство шаблонов, которое часто применяется при создании таких необычных матриц, как vector < vector<double> >. Поскольку класс туре! iSt имеет два параметра, его всегда можно расширить, заменив один из параметров другим классом Typelist, и так до бесконечности.

Однако существует одна небольшая проблема. Пока мы можем создавать списки, состоящие из нескольких типов, но у нас нет инструмента для описания списков, не содержащих никаких типов или содержащих только один тип. Для этого необходим нулевой тип списка (null list type) и класс NullType, описанный в главе 2, предназначен именно для этого.

Примем соглашение, что каждый список типов должен заканчиваться классом NullType, служащим маркером конца списка. Это намного удобнее традиционного нулевого байта "\0", который применяется для обозначения конца строк в традиционных функциях языка С. Теперь можно дать определение класса Typelist, состоящего лишь из одного элемента.

Определение класса NullType дано в главе 2 typedef Typelist<int, NullType> OneTypeOnly;

Определение класса Typelist, состоящего из трех типов char, принимает следующий вид.

typedef Typelist<char, Typelist<signed char,

Typelist<unsigned char, NullType> > > AllcharTypes;

Следовательно, мы получили шаблон неофаниченного списка типов Typelist, который может содержать любое их количество.

Посмотрим теперь, как можно манипулировать списками типов. (Как и прежде, это относится к типам Typelist, а не к объектам типа Typelist.) Приготовьтесь к приключениям. С этого момента мы пофужаемся в подземелье языка С++, мир странных новых правил - мир профаммирования на этапе компиляции.

3.3. Линеаризация создания списков типов

Сами по себе списки типов слишком напоминают конструкцию из языка LISP, поэтому их нелегко использовать. Такие конструкции очень нравятся профаммистам на языке LISP, но они не очень хорошо согласуются с языком С++ (не говоря уже о пробелах между символами <, которые нельзя забывать). Например, вот как выглядит список целочисленных типов.

typedef Typelist<signed char, Typelist<short int,

Typelist<int, Typelist<long int, NullType> > > > Signedlntegrals;

Списки типов были бы превосходной концепцией, но они явно нуждаются в более привлекательной упаковке.

Для того чтобы упростить процедуру создания списков типов, в файле Typelist.h из библиотеки Loki определено большое количество макросов, преобразующих рекурсию в простое перечисление, правда, за счет утомительных повторений. Однако это - не проблема. Повторение выполняется только один раз, в библиотечном коде. Это позволяет масштабировать списки типов для большого количества элементов (50). Типичный макрос выглядит следующим образом.



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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105