[You95] Edward Yourdon. Managing projects to produce good-enough software. IEEE Software, March 1995.
Приложение ВОтветы к упражнениям
Ответ: По нашему разумению, более ортогональным является класс Split2. Он сосредоточен на собственной задаче – расщеплении строк и игнорирует подробности, связанные с источником обрабатываемых им строк. Это не только упрощает разработку программы, но и придает ей большую гибкость. Класс Split2 может расщеплять строки, считываемые из файла, сгенерированные другой программой или передаваемые через операционную среду.
Ответ: Если все сделано корректно, то, по всей вероятности, немодальное. Система, которая использует немодальные диалоговые окна, испытывает меньшее беспокойство о том, что происходит в любой конкретный момент времени. Скорее всего, она будет обладать лучшей инфрастуктурой взаимодействия между модулями по сравнению с модальной системой, которая может содержать встроенные предположения о состоянии системы – предположения, которые могут привести к большему связыванию и уменьшению ортогональности.
Ответ: Здесь есть элемент лукавства. Объектная технология может обеспечить наличие более ортогональной системы, но поскольку она имеет больше средств, которые могут эксплуатироваться с нарушением режима, в реальности легче создать неортогональную систему, используя объекты, чем создавать ее при помощи процедурного языка. Ее особенности – множественное наследование, исключительные ситуации, перегрузка операторов и переопределение родительского метода (через механизм подклассов) – предоставляют достаточные возможности для увеличения связанности не столь очевидными способами.
Применяя объектную технологию и приложив небольшое дополнительное усилие, вы можете добиться наличия более ортогональной системы. И хотя вы всегда можете написать неструктурированную программу на процедурном языке, объектно-ориентированные языки, используемые в малых дозах, могут сделать ее более насыщенной.
Ответ: Для спасения ситуации прибегнем к устаревшим технологиям! Нарисуйте на лекционной доске несколько картинок – автомобиль, телефон и дом – с помощью фломастера. Для этого не нужно быть великим художником, вполне достаточно условных изображений. Поместите на доску памятные записки, описывающие содержимое целевых страниц в активных областях экрана. В ходе встречи вы можете совершенствовать рисунки и менять расположение памятных записок.
Ответ: Поскольку мы хотим, чтобы язык был расширяемым, сделаем таблицу синтаксического анализатора управляемой. Каждый элемент таблицы содержит символ команды, флаг, говорящий о необходимости аргумента, и имя подпрограммы, вызываемой для обработки этой конкретной команды.
typedef struct {
char cmd; /* the command letter */
int hasArg; /* does it take an argument */
void (*func)(int, int); /* routine to call */
} Command;
static Command cmds[] = {
{'P', ARG, doSelectPen},
('V', NO_ARG, doPenUp},
{'D', NO_ARG, doPenDown},
{'N,' ARG, doPenDir},
{'E', ARG, doPenDir},
{'S', ARG, doPenDir},
{'W', ARG, doPenDir}
};
Основная программа довольно проста: считать строку, отыскать команду, при необходимости принять аргумент, затем вызвать функцию обработчика.
while (fgetsfbuff, sizeof(buff), stdin)) {
Command *cmd = findCommand(*buff);
if (cmd) {
int arg = 0;
if (cmd->hasAr&& !getArg(buff+1, &arg)) {
fprintf(stderr,"'%с' needs an argument\n», *buff);
continue;
}
cmd->func(*buff, arg);
}
}
Функция, которая ищет команду, исполняет последовательный перебор таблицы, возвращая либо совпадающий элемент, либо NULL.
Command *findCommand(int cmd) {
int i;
for (i = 0; i if (cmds[i].cmd==cmd) return cmds + i; } fprintf(stderr, «Unknown command %c'\n», cmd); return 0; } И наконец, считывание числового аргумента довольно просто, если использовать подпрограмму sscanf. int getArg(const char *buff, int 'result) { return sscanf(buff, «%d», result) == 1; } Ответ 6: При использовании BNF спецификация времени могла бы выглядеть следующим образом: Ответ: В нашем примере мы составили программу, используя генератор bison, который представляет собой GNU-версию генератора уасс. Для ясности здесь показано только тело программы синтаксического анализатора. Полная версия есть на сайте www.pragmaticprogrammmer.com. time: spec EOF { if ($1>= 24*60) yyerror("Time is too large»); printf("%d minutes past midnight\n», $1); exit(0); } ; spec: hour ':' minute { $$ = $1 + $3; } | hour ':' minute ampm { if ($1>11*60) yyerrorf «Hour out of range»); $$ = $1 + $3 + $4; } | hour ampm {if ($1>11*60) yyerror("Hour out of range»); $$ = $1 + $2; } ; hour: hour_num {if ($1>23) yyerror("Hour out of range»); $$ = $1 * 60; }; minute: DIGIT DIGIT {$$ = $1*10 + $2; if ($$> 59) yyerrorf «minute out of range») , }; ampm: AM {$$ = AM_MINS;} | PM {$$ = PM_MINS;) ; hour num: DIGIT {$$ = $1;) | DIGIT DIGIT {$$ = $1*10 + $2;} ; Ответ: $_ = shift; /"(\d\d?)(am|pm)$/ && doTime($1, 0, $2, 12); /"(\d\d?):(\d\d)(am|pm)$/ && doTime($1, $2, $3, 12); /"(\d\d?):(\d\d)$/ && doTime($1, $2, 0, 24); die «Invalid time $_\n"; # # doTime(hour, min, ampm, maxHour) # sub doTime($$$$) { my ($hour, $min, $offset, $maxHour) = @_; die «Invalid hour: $hour» if ($hour>= $maxHour); $hour += 12 if ($offset eq «pm») print $hour*60 + $min, « minutes past midnight\n"; exit(0); } Ответ: Ответ должен быть изложен, исходя из нескольких допущений: • Лента содержит информацию, которую необходимо передать. • Известна скорость ходьбы человека. • Известно расстояние между компьютерами. • Временем, необходимым для переноса информации на ленту и с ленты, можно пренебречь. • Потери данных при хранении на ленте примерно равны их потерям при передаче по каналу связи. Ответ: Учитывая допущения ответа 9: Объем информации, содержащейся на стриммерной кассете (4 Гбайт), составляет 32 х 10^9 бит, так что передача эквивалентного объема по каналу со скоростью 1 Мбайт/с заняла бы около 32000 сек. (примерно 9 ч). Если человек движется с постоянной скоростью 3,5 мили в час, то, для того чтобы канал связи превзошел курьера, два компьютера должны располагаться друг от друга на расстоянии не менее 31 мили. Если это расстояние меньше, то победа остается за человеком. Ответ: Ответ к данному упражнению составлен на языке Perl. my @consts; my $name = <>; die «Invalid format – missing name» unless defined($name); chomp $name; # Read in the rest of the file while (<>) { chomp; s/"\s*//; s/\s*$//; die «Invalid line: $_» unless /"(\w+)$/; push @consts, $_; } # Now generate the file open(HDR, «>$name.h») or die «Can't open $name.h: $!"; open(SRC, «>$name.c») or die «Can't open $name.c: $!"; my $uc_name = uc($name); print HDR «/* File generated automatically – do not edit */\n"; print HDR «extern const char *$ {ucjiame)_name[];"; print HDR «typedef enum {\n"; print HDR join»,\n», @consts; print HDR «\n) $uc_name;\n\n"; print SRC «* File generated automatically – do not edit */\n"; print SRC «const char *$ {uc name}_name[] = {\n \""; print SRC join «\»,\n \"», @consts; print SRC «\"\n};\n"; close(SRC); close(HDR); Используя принцип DRY, мы не будет вырезать и вклеивать этот вновь написанный файл в нашу программу. Вместо этого мы «включим» его – данный плоский файл является главным источником этих констант. Поэтому нам понадобится файл сборки для восстановления заголовка при изменении файла. Следующий фрагмент содержится в системе отладки в исходном дереве (имеется на web-сайте). etest.c etest.h: etest.inc enumerated.pl perl enumerated.pl etest.inc Ответ: Вот ответ, написанный на языке Perl. my $dir = shift or die «Missing directory» , for my Sfile (glob(u$dir/*.pr)) { open(IP, «$file») or die «Opening $file: $!"; undef $/; # Turn off input record separator - my Scontent = close(IP); if (Scontent Гrusestrict/m) { rename Sfile, «$file.bak» or die «Renaming $file: $!"; open(OP, «>