10.3 Делегаты

Предыдущая  Содержание  Следующая V*D*V

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

Важно отметить, что наша точка зрения сместилась: объект теперь часть сценария обратного вызова. Мы называем такой объект делегатом. Как только делегат объявляет о себе содержащему его объекту, содержащий проверяет, что обратные вызовы могут быть обработаны делегатом и позже содержащий выполняет только те вызовы, которые ожидает делегат.

Для примера мы реализуем простую среду разработки приложений для текстового фильтра, то есть программу, которая читает строки из потока стандартного ввода или из файлов, указанных в виде параметров, управляет ими и записывает результат в поток стандартного вывода. В качестве приложения мы рассмотрим программу для подсчёта строк и символов в текстовом файле. Вот основная программа, которая может быть определена как часть файла реализации Wc.dc:

 

int main (int argc, char * argv []) {

    void * filter = new(Filter(), new(Wc()));

 

    return mainLoop(filter, argv);

}

 

Мы создаём универсальный объект-фильтр и отдаём его в виде делегата специализированному объекту Wc считающему строки и символы. filter получает параметры нашей программы и выполняет mainLoop() с обратными вызовами к объекту Wc.

 

% WcClass: Class Wc: Object {

    unsigned lines;         // количество строк в текущем файле

    unsigned allLines;      // количество строк в предыдущих файлах

    unsigned chars;         // количество байтов в текущем файле

    unsigned allChars;      // количество байтов в предыдущих файлах

    unsigned files;         // обработанные файлы

%—

    int wc (_self, const Object @ filter, \

                const char * fnm, char * buf);

    int printFile (_self, const Object @ filter, \

                const char * fnm);

    int printTotal (_self, const Object @ filter);

%}

 

Методы в Wc не делают ничего за исключением подсчёта строк и символов и сообщения о результатах. wc() вызывается с параметром в виде буфера, содержащего одну строку:

 

% Wc wc {               // (self, filter, fnm, buf)

%casts

    ++ self -> lines;

    self -> chars += strlen(buf);

    return 0;

}

 

После того, как будет обработан один файл, printFile() сообщает статистику и добавляет её к общей:

 

% Wc printFile {       // (self, filter, fnm)

%casts

    if (fnm && strcmp(fnm, "-"))

        printf("%7u %7u %s\n",

                    self -> lines, self -> chars, fnm);

    else

        printf("%7u %7u\n", self -> lines, self -> chars);

    self -> allLines += self -> lines, self -> lines = 0;

    self -> allChars += self -> chars, self -> chars = 0;

    ++ self -> files;

    return 0;

}

 

fnm это аргумент с именем текущего файла. Это может быть нулевой указатель или знак минус; в этом случае имя файла в выводе не показывается.

Наконец, printTotal() сообщает о текущем общем количестве, если printFile() вызывали несколько раз:

 

% Wc printTotal {       // (self, filter)

%casts

    if (self -> files > 1)

        printf("%7u %7u in %u files\n",

            self -> allLines, self -> allChars, self -> files);

    return 0;

}

 

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

 

Предыдущая  Содержание  Следующая