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

В таком случае мы должны либо сами модифицировать иерархию классов Query, либо договориться, чтобы это сделали за нас. В данной ситуации мы, как авторы всей системы, сами изменим код, модифицировав конструкторы подтипов и включив виртуальную функцию-член add_op() для добавления операндов после определения оператора (мы покажем, как она применяется, чуть ниже, при рассмотрении функций evalRParen() и evalWord()).

<p>17.7.1. Определение класса UserQuery</p>

Объект класса UserQuery можно инициализировать указателем на вектор строк, представляющий запрос пользователя, или передать ему адрес этого вектора позже, с помощью функции-члена query(). Это позволяет использовать один объект для нескольких запросов. Фактическое построение иерархии классов Query выполняется функцией eval_query():

// определить объект, не имея запроса пользователя

UserQuery user_query;

string text;

vectorstring query_text;

// обработать запросы пользователя

do {

while( cin text )

query_text.push_back( text );

// передать запрос объекту UserQuery

user_query.query( &query_text );

// вычислить результат запроса и вернуть

// корень иерархии Query*

Query *query = user_query.eval_query();

}

while ( /* пользователь продолжает формулировать запросы */ );

Вот определение нашего класса UserQuery:

#ifndef USER_QUERY_H

#define USER_QUERY_H

#include string

#include vector

#include map

#include stack

typedef pairshort,short location;

typedef vectorlocation,allocator loc;

#include quot;Query.h"t;

class UserQuery {

public:

UserQuery( vector string,allocator *pquery = 0 )

: _query( pquery ), _eval( 0 ), _paren( 0 ) {}

Query *eval_query(); // строит иерархию

void query( vector string,allocator *pq );

void displayQuery();

static void word_map( mapstring,loc*,lessstring,allocator *pwm ) {

if ( !_word_map ) _word_map = pwm;

}

private:

enum QueryType { WORD = 1, AND, OR, NOT, RPAREN, LPAREN };

QueryType evalQueryString( const string &query );

void evalWord( const string &query );

void evalAnd();

void evalOr();

void evalNot();

void evalRParen();

bool integrity_check();

int _paren;

Query *_eval;

vectorstring *_query;

stackQuery*, vectorQuery* _query_stack;

stackQuery*, vectorQuery* _current_op;

static short _lparenOn, _rparenOn;

static mapstring,loc*,lessstring,allocator *_word_map;

};

#endif

Обратите внимание, что два объявленных нами стека содержат указатели на объекты типа Query, а не сами объекты. Хотя правильное поведение обеспечивается обеими реализациями, хранение объектов значительно менее эффективно, поскольку каждый объект (и его операнды) должен быть почленно скопирован в стек (напомним, что операнды копируются виртуальной функцией clone()) только для того, чтобы вскоре быть уничтоженным. Если мы не собираемся модифицировать объекты, помещаемые в контейнер, то хранение указателей на них намного эффективнее.

Перейти на страницу:

Похожие книги