• Функция rep()
, возвращающая строковое представление базового запроса. Эту функцию использует функция eval()
для создания объекта класса QueryResult
, представляющего соответствия, а также оператор вывода, отображающий выражение запроса.
Как уже упоминалось, все четыре типа запроса не связаны друг с другом наследованием; концептуально они элементы одного уровня. Каждый класс использует тот же интерфейс, а значит, для представления этого интерфейса следует определить абстрактный базовый класс (см. раздел 15.4). Назовем этот абстрактный базовый класс Query_base
, поскольку он должен служить корневым классом иерархии запроса.
Проектирование иерархии наследования — это достаточно сложная тема, которая выходит за рамки данного вводного курса. Однако имеет смысл упомянуть об одном достаточно важном факторе проектирования, с которым должен быть знаком каждый программист.
При определении класса как открыто производного от другого производный и базовый классы реализуют взаимоотношения типа "
Еще одним популярным способом взаимоотношений классов является принцип "
В рассматриваемом примере с книжным магазином базовый класс представляет концепцию книги, продаваемой по предусмотренной цене, а класс Bulk_quote
"
Класс Query_base
определит функции eval()
и rep()
как чистые виртуальные (см. раздел 15.4). Каждый из классов, представляющих специфический вид запроса, должен переопределить эти функции. Классы WordQuery
и NotQuery
унаследуем непосредственно от класса Query_base
. У классов AndQuery
и OrQuery
будет одна общая особенность, которой не будет у остальных классов в системе: у каждого будет по два операнда. Для моделирования этой особенности определим другой абстрактный базовый класс, BinaryQuery
, представляющий запросы с двумя операндами. Классы AndQuery
и OrQuery
наследуются от класса BinaryQuery
, который в свою очередь наследуется от класса Query_base
. Результатом этих решений будет проект классов, представленный на рис. 15.2.
Рис. 15.2. Иерархия наследования Query_base
Рассматриваемая программа будет отрабатывать запросы, а не создавать их. Но чтобы запустить программу на выполнение, необходимо определить способ создания запроса. Проще всего сделать это непосредственно в коде при помощи выражения С++. Например, чтобы создать описанный ранее составной запрос, можно использовать следующий код:
Query q = Query("fiery") & Query("bird") | Query ("wind");
Это довольно сложное описание неявно предполагает, что код пользовательского уровня не будет использовать унаследованные классы непосредственно. Вместо этого будет создан класс интерфейса по имени Query
(Запрос), который и скроет иерархию. Класс Query
будет хранить указатель на класс Query_base
. Этот указатель будет связан с объектом типа, производного от класса Query_base
. Класс Query
будет предоставлять те же функции, что и классы Query_base
: функцию eval()
для обработки соответствующего запроса и функцию rep()
для создания строковой версии запроса. В нем также будет определен перегруженный оператор вывода, чтобы отображать соответствующий запрос.