Бьерн Страуструп - Язык программирования С++. Главы 11-13
Страница 54. Каркас области приложения



13.7 Каркас области приложения

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

         class filter {
         public:
           class Retry {
           public:
             virtual const char* message() { return 0; }
           };

         virtual void start() { }
         virtual int retry() { return 2; }
         virtual int read() = 0;
         virtual void write() { }
         virtual void compute() { }
         virtual int result() = 0;
       };

Нужные для производных классов функции описаны как чистые виртуальные,
остальные функции просто пустые. Каркас содержит основной цикл
обработки и зачаточные средства обработки ошибок:

       int main_loop(filter* p)
       {
         for (;;) {
             try {
                 p->start();
                 while (p->read()) {
                       p->compute();
                       p->write();
                 }
                 return p->result();
             }
             catch (filter::Retry& m) {
               cout << m.message() << '\n';
               int i = p->retry();
               if (i) return i;
             }
             catch (...) {
               cout << "Fatal filter error\n";
               return 1;
             }
           }
         }

Теперь прикладную программу можно написать так:

         class myfilter : public filter {
            istream& is;
            ostream& os;
            char c;
            int nchar;

         public:
            int read() { is.get(c); return is.good(); }
            void compute() { nchar++; };
            int result()
                { os << nchar
                     << "characters read\n";
                     return 0;
                }

            myfilter(istream& ii, ostream& oo)
              : is(ii), os(oo), nchar(0) { }
         };

и вызывать ее следующим образом:

        int main()
        {
          myfilter f(cin,cout);
          return main_loop(&f);
        }

Настоящий каркас, чтобы рассчитывать на применение в реальных задачах,
должен создавать более развитые структуры и предоставлять больше
полезных функций, чем в нашем простом примере. Как правило, каркас
образует дерево узловых классов. Прикладной программист поставляет
только классы, служащие листьями в этом многоуровневом дереве,
благодаря чему достигается общность между различными прикладными
программами и упрощается повторное использование полезных функций,
предоставляемых каркасом. Созданию каркаса могут способствовать
библиотеки, в которых определяются некоторые полезные классы, например,
такие как scrollbar ($$12.2.5) и dialog_box ($$13.4). После определения
своих прикладных классов программист может использовать эти классы.

 
« Предыдущая статья   Следующая статья »