Книга: Записки автоматизатора. Профессиональная исповедь

Формулирование задач

Формулирование задач

В соответствии с российскими (и, кстати, американскими) стандартами составление технического задания (ТЗ) возлагается на разработчика. Это естественно, так как обычно клиент сам не знает, чего хочет, что можно реализовать и как.

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

Посмотрим на примерах, к чему это приводит.

Невинная фраза из ТЗ «Все количество распределяется по всем покупателям поровну с округлением до 1 шт. Погрешность округления относится на последнего покупателя» приводит к очень интересным результатам.

Итак, на 100 покупателей поступило 40 штук. Поровну означает 40/100 = 0,4 штуки. С округлением до одной штуки получается 0, то есть все 40 штук получит последний покупатель.

Еще веселее, если на 100 покупателей поступило 60 штук. Тогда все покупатели, кроме последнего, получат по одной штуке, а последний… – 39 (минус тридцать девять) штук.

Программа соответствует ТЗ.

Вообще ошибки округления – это бич современных систем учета, особенно в России, где правила бухгалтерского учета про них просто не упоминают, якобы у нас все всегда точно. Как следствие, в России их никаким способом нельзя обрабатывать правильно. У меня сформировалось стойкая убежденность, что наши налоговые органы вполне умышленно никогда не отвечают на такие вопросы и не дают необходимых разъяснений: так удобнее сделать нарушителем любого, кто отчитывается перед налоговиками. Следующий пример из приказа МНС я могу воспринимать только как издевательство:

«По коду строки 100 указано 5 месяцев, в течение которых транспортное средство было зарегистрировано на организацию в 2003 году, следовательно, коэффициент, который следует указать по коду строки 110, составит 5/12 = (5: 12)».

Приказ МНС № БГ-3-21/724 от 29 декабря 2003 г.

Ясно? 5/12 равно 5:12, так МНС приказал. А уж сколько знаков после запятой оставить – это ваша проблема: сколько ни укажите, будете не правы.

Значит, тем более все вопросы, связанные с округлениями, надо подробно описать в техническом задании и по возможности разъяснить руководству (с примерами на конкретных цифрах).

Про программиста, написавшего программу для банка, добавлявшую все ошибки округления на его банковский счет и ставшего миллионером (на время, пока его не посадили), я читал еще в начале семидесятых (книжка называлась «Кибернетическая смесь»). Теперь обязательно пытаюсь рассказать эту историю боссам, а потом уже показываю, как после получения груза на сто тысяч долларов тысяча долларов растворяется в системе.

Мнимые очевидности

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

Про полтинники. Я уже писал, как в компании, продававшей периодическую печать, киоскеры, работавшие в метро, взмолились, чтобы мы округляли розничные цены на газеты до полтинника. Руководство нас на это благословило, и я программисту ровно так задание и сформулировал: «В поле PriceMetro поместить значение из поля PriceRetail, округленное до 0,5».

Звонит программист:

– А каким способом округлять?

– Обычным.

– Андрей, я не знаю обычного способа округлять до 0,5. Пожалуйста, опиши.

– Ты издеваешься?

– Нет, я серьезно.

– Хорошо.

Я в раздражении швыряю трубку, открываю текст задания и… задумываюсь. На всякий случай лезу в Интернет, потом благодарю Бога, что у меня есть умный и спокойный программист, и записываю в задании:

ДРОБН:= А – ЦЕЛОЕ (А)

ЕСЛИ ДРОБН < 0,25 ТО ДРОБН:= 0

ИНАЧЕ ЕСЛИ ДРОБН < 0,75 ТО ДРОБН:= 0,5

ИНАЧЕ ДРОБН:= 1

ОКРУГЛ05 (А) = ДРОБН + ЦЕЛОЕ (А)

Дело в том, что не существует никакого «общего» способа округления до полтинников. Его нужно было записать в задании явно. Что я и сделал самым понятным для программиста способом.

Про дополнительное оборудование (история, рассказанная Александром Ройзнером за 15 лет до предыдущей). Мы пытались формализовать действия, которые во время поездки должен совершать машинист локомотива, чтобы в дальнейшем правильность этих действий оценивала автоматизированная система. По этому поводу с начальником одного из локомотивных депо произошел следующий диалог:

– По правилам при проверке тормозов нужно после появления тормозного эффекта и снижения скорости на 10 км/ч в грузовом груженом, грузопассажирском, пассажирском поезде и на 4?6 км/ч в грузовом порожнем поезде произвести отпуск тормозов. Снижение скорости мы определить можем. А как определить «эффект торможения»?

– А эффект торможения каждый машинист задницей чувствует.

– Значит, нам надо включить в измерительную систему задницу?

Кстати, прекрасные образцы для подражания в постановке задач имеются в Книге Левит Ветхого Завета:

«скажите сынам Израилевым: вот животные, которые можно вам есть из всего скота на земле:

всякий скот, у которого раздвоены копыта и на копытах глубокий разрез, и который жует жвачку, ешьте;

только сих не ешьте из жующих жвачку и имеющих раздвоенные копыта: верблюда, потому что он жует жвачку, но копыта у него не раздвоены, нечист он для вас;

и тушканчика, потому что он жует жвачку, но копыта у него не раздвоены, нечист он для вас,

и зайца, потому что он жует жвачку, но копыта у него не раздвоены, нечист он для вас;

и свиньи, потому что копыта у нее раздвоены и на копытах разрез глубокий, но она не жует жвачки, нечиста она для вас» (Левит 11: 2–7).

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

Оставьте здравый смысл дома

Обычно постановщики-фантазеры гнездятся в компаниях-разработчиках, но иногда встречаются и внутри производственных и торговых предприятий. Бойтесь их.

При описании бизнес-процессов фантазия постановщика становится очень большим недостатком. Правило здесь есть только одно, но оно не допускает отклонений.

Если какие-то части бизнес-процесса вам не известны точно, то их нужно узнать, а не выдумать.

Здравый смысл и логику в такие моменты лучше оставлять дома. Иначе в системе будут реализованы решения красивые, логичные, но абсолютно не соответствующие реальности.

«По логике вещей это должно быть так». По логике, может, и так, но кто вам сказал, что бизнес-процессы подчиняются хоть какой-то логике? А если логика и есть, то она совершенно не обязана быть айтишной, аристотелевой или даже женской. Самое ужасное, что бизнес-процесс создается совместными стараниями людей, может быть, и разумных, но преследующих самые разные цели. Есть закон, основанный на думной логике, подзаконные акты, основанные на смеси нескольких ведомственных логик (к примеру, налоговой, милицейской и железнодорожной), бизнес-правила ваших крупных контрагентов, которые вы не в состоянии поменять или не применять, жгучее желание вашего хозяина, противоречащее не менее жгучему желанию его компаньона, а напоследок ограничение по количеству кнопок, на которые младший бухгалтер может нажать, не ошибаясь слишком часто.

Вот может измениться сумма денег в зависимости от количества накладных, по которым продан товар? «По логике вещей» нет.

А теперь возьмите карандаш и запишите варианты, в которых это не так, исходя из вашего опыта, не поглядывая в то, что я написал ниже.

Теперь давайте сравним.

Округлениестоимости по каждой из двух накладных даст не тот же результат, что округление по одной накладной.

Фиксированная сумма на накладную: за оформление и/или доставку одного заказа берется фиксированная плата. Если накладных две, то эту сумму возьмут дважды.

Скидки, зависящие от позиции товара в накладной. Правила розничной продажи могут включать скидки, зависящие и от общей стоимости покупки, и от количества предметов в покупке, и даже от порядка следования строк в накладной («при покупке на сумму свыше 1000 рублей скидка 3 %», «купите два товара и получите третий бесплатно», «на второй товар скидка 5 %, на третий – 10 %, на четвертый и последующие – 15 %» и т. д.).

Рассчитываемая стоимость доставки: в сумму накладной включена стоимость перевозки товара. Тут Кафка отдыхает.

Вот выдержка из правил расчета стоимости перевозки мелких отправок железнодорожным транспортом:

«Минимальный расчетный вес отправки 10 кг. При исчислении провозной платы масса груза до 1000 кг округляется до полных 10 кг, а масса груза свыше 1000 кг округляется до полных 100 кг.

Количество груза определяется в м3, если масса 1-го м3 груза менее 200 кг.

Объем груза округляется до 0,05 м3».

Теперь вам понятно, что стоимость доставки 6 кг груза по двум накладным будет ровно в два раза больше, чем по одной, а вот стоимость доставки 1010 кг по одной накладной будет больше, чем по двум накладным на 500 и 510 кг.

Еще интереснее случай перевозки пенопластовых блоков и чугунных гирь. Включив и то и другое в одну накладную, вы заплатите за вес перевозимого, а оформив две накладные, за чугун будете платить по весу, а за пенопласт – по объему.

А теперь рассмотрим еще один вопрос.

Может ли быть так, чтобы за два одинаковых автомобиля с одинаковыми двигателями, отличающихся только выбитыми на их отдельных частях номерами и находящихся в собственности и распоряжении у одного лица, нужно было платить разный транспортный налог?

Вот реальный случай. В одной партии грузовиков с завода поступили два одинаковых «КамАЗа». В их паспортах была указана мощность двигателей 184 киловатта (а в лошадиных силах мощность не указана). В паспорте одного из грузовиков оказалась ошибка в номере шасси. Грузовик остался на месте, а паспорт отправили на завод переписывать. Новый паспорт выписывали более тщательно и мощность двигателя указали не только в киловаттах, но и в лошадиных силах: 250 л с. (184 кВт).

Все дальнейшие фокусы связаны с тем, что Инструкцию по заполнению паспортов транспортных средств создало и утвердило Министерство внутренних дел и в ней нет правил округления. Поэтому завод вправе написать в паспорте то, что написал. А вот Методические рекомендации по применению главы 28 «Транспортный налог» части второй Налогового кодекса Российской Федерации разработало и утвердило Министерство по налогам и сборам. И там в разделе V, п. 19 читаем:

«В случае если в технической документации на транспортное средство мощность двигателя указана в метрических единицах мощности (кВт), то соответствующий пересчет во внесистемные единицы мощности (лошадиные силы) осуществляется путем умножения мощности двигателя, выраженной в кВт, на множитель, равный 1,35962 (переводной коэффициент – 1 кВт = 1,35962 л с.) (Физические величины: Справочник / А. П. Бабичев, Н. А. Бабушкина, А. М. Братковский и др.; под ред. И. С. Григорьева, Е. З. Мейлихова. – М.: Энергоатомиздат, 1991.)

При этом при пересчете во внесистемные единицы мощности (лошадиные силы) округление производится с точностью до второго знака после запятой».

Аминь. Вот фрагмент таблицы налоговых ставок.


Смотрим таблицу и обнаруживаем, что грузовик, у которого мощность двигателя указана только в киловаттах, попадает в строку «Свыше 250 л с.» со ставкой 45 рублей за лошадь. Переводим киловатты в лошадиные силы: 184 х 1,35962 = 250,17008, округляем до второго знака и умножаем на ставку.

Налог за 12 месяцев получится равным 250,17 х 45 = 11257,65 рубля.

Для грузовика, у которого мощность указана в лошадиных силах, ставка берется из строки «до 250 л с. включительно». Налог за 12 месяцев оказывается 250 х 35 = 8750 рублей.

Разница в налоге для одинаковых грузовиков составила 2507,65 рубля. Неплохо, а?

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

Предупреждение. Пожалуйста, не забудьте, что я написал этот текст не в тот момент, когда вы его читаете. За время, прошедшее между этими событиями (хотя Событие, конечно, только то, что вы эту книгу читаете, а не что, я ее написал) многие нормативные акты по налогообложению могли неоднократно поменяться, а во многих программах одни ошибки исправили, а другие добавили. Не используйте эту книгу ни в качестве пособия по расчету налогов, ни в качестве руководства по каким-либо программным средствам.

«Мне так нужна распределенность!»

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

Традиционная ошибка, которую разработчики совершают довольно часто, состоит в том, что решение этого вопроса откладывается «на потом» в надежде, что превратить локальную систему в распределенную удастся с помощью небольших модификаций. Я сталкивался с системами, реализованными таким способом, на этапе их сопровождения. Сталкивался очень больно. В системах постоянно появлялась информация, перекореженная в результате активной работы пользователей, разделенных тысячами километров и несколькими часовыми поясами, а я каждый день должен был собирать информационные пазлы, не только рассыпавшиеся, но еще и сломанные в процессе транспортировки, в каждом случае пытаясь понять, в каких местах нашей необъятной Родины и в какой последовательности были нажаты кнопочки и какие скрипты теперь нужно написать, чтобы восстановить сколько-нибудь непротиворечивое состояние системы.

Ситуация с проектированием распределенных систем вдобавок сильно затуманена большим количеством специальных терминов, сильно пугающих неспециалистов, а специалистами трактуемых совершенно по-разному. Поэтому я сначала попытаюсь сформулировать на относительно простом русском языке основные выводы для читателей, для которых специальные слова типа «коллизия», «клинч», «асинхронная репликация» мало что значат, а потом попытаюсь объясниться на специальном жаргоне с более смелыми.

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


Рис. 7. Пример системы, работающей с удаленными пользователями

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

В гипер-суперсистемах возможны ситуации, когда для хранения и обработки информации не хватает мощностей серверного оборудования, расположенного на одной площадке, но каналы связи обладают достаточной мощностью и надежностью. В таких случаях используются синхронные распределенные системы (иначе – системы с синхронной репликацией). На уровне бизнес-логики распределенность таких систем не видна: как вас не слишком волнует, расположена база на одном диске или на многих, на одном сервере или на многих, так вам не очень важно, расположена база в одном Урюпинске или еще и в Арзамасе. И если пользователь в Арзамасе начал редактировать запись о контрагенте, то другому пользователю из Урюпинска просто придется подождать, поскольку эта запись будет заблокирована.

Собственно, блокировки будут и при использовании централизованной базы данных. Пресловутый онлайн – мечта всех управленцев – означает всего лишь уменьшение задержки при передаче информации. Кстати, если определить, при передаче какой информации нужно минимизировать эти задержки, может выясниться, что это далеко не весь массив данных. Если еще начать выяснять, какова же все-таки максимально допустимая задержка, то может оказаться, что необходимо, например, иметь актуальную информацию об остатках товара один раз в день, а все остальное достаточно обновлять раз в неделю. Так мы приходим уже к периодической репликации.

Конечно же, бывают случаи, когда задержка даже в секунды может дорого стоить (классический пример – биржевая торговля), но чаще всего управленец, требующий непременно данных в режиме реального времени, не будет ими пользоваться чаще одного раза в сутки. И возможно, эти данные ему можно предоставить и более дешевыми средствами, нежели прокладкой каналов до каждого ларька и установкой многомиллионной системы на рабочем месте каждой уборщицы. Понимание этого позволяет значительно сэкономить средства – именно так получаются вполне успешные внедрения маломощных систем в огромных компаниях. – Д. К.

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


Рис. 8. Пример синхронной распределенной системы

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

Собственно, не так давно «каналом связи» между двумя площадками в Москве в компании, где я работал, служил курьер, который перевозил винчестер с изменениями базы данных с площадки на площадку два раза в день.

В таких случаях пользователи на каждой площадке работают со своей местной (локальной) базой данных, а сами базы выравниваются или периодически, или когда возникает такая возможность.

Урюпинский пользователь в такой системе может хладнокровно править наименование организации в соответствии с ИНН в тот самый момент, когда арзамасец начнет изменять в своей копии той же записи ИНН организации в соответствии с названием. Эта шутка носит высоконаучное название коллизии. Понятно, что попытка свести воедино арзамасско-урюпинские результаты (то есть разрешить коллизию) вызовет определенные проблемы.


Рис. 9. Пример распределенной системы с асинхронными репликациями

Такие системы называются системами с асинхронными репликациями, а сам комплект изменений, за один раз передаваемый от одной локальной базы к другой, – репликой. Те, кому не нравится импортное слово «репликация», используют слово «тиражирование», которое почему-то считают более русским. На мой взгляд, больше подходит «выравнивание», но не хочется путать читателя еще больше.

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

Сейчас любая уважающая себя СУБД имеет встроенные механизмы, которые позволяют организовывать асинхронные репликации и настраивать способы разрешения коллизий. Многие на это ловятся, считая, что «в случае чего» это дает возможность быстро превратить нераспределенную информационную систему в распределенную.

Не тут-то было. Выясняется, что необходимость обмениваться данными нужно было в процессе проектирования держать в голове постоянно, разрабатывая как схему базы, так и способы обработки данных.

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

Очень интересно получится, когда сервера системы начнут функционировать в разных часовых поясах. Конечно, СУБД при репликации позаботится о модификации временных меток, которые она сама установила, но как работать с другими датами и временами, записанными в базе, вам было бы неплохо заранее разобраться самому, чтобы потом не гадать, глядя на 0:00, была в этот момент полночь в Москве или в Петропавловске-Камчатском.

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

Пример. Пусть по каким-то причинам вы храните фамилию в одной таблице базы, а имя и отчество – в другой, ссылающейся на первую. В Урюпинске оператор заполнил форму, в которой указал Орлв Андрей Георгиевич. С помощью репликаций эта информация должна оказаться в Арзамасе. Реплики отправляются по расписанию, объем одной реплики ограничен. Поэтому в Арзамас в первой реплике пришла только фамилия из главной таблицы. Оператор в Арзамасе увидел ошибку в фамилии и исправил ее. Теперь в Арзамасе в главной таблице «Орлов», а в зависимой еще пусто. Наконец со следующей репликой в Арзамас приходит запись, в которой указано «Андрей Георгиевич». Но в базу она не попадет: главная запись уже была изменена, и процедура автоматического разрешения коллизий «Андрея Георгиевича» отвергнет.

Если же вы еще и не все зависимости описали на уровне СУБД, а поддерживаете какие-то из них с помощью алгоритмов обработки, то становится совсем загадочным, как это все собрать на принимающем конце.

А поскольку многие системы стараются поддерживать несколько СУБД, то вероятность использования механизмов приложения, а не СУБД в них достаточно велика, что делает репликацию средствами СУБД достаточно сложной. – Д. К.

Арсенал средств обработки в современных СУБД весьма внушителен, все они полезны в каких-то случаях, но желание разработчика использовать все эти средства сразу может приводить к результатам слабо предсказуемым. А ведь часто данные в нескольких зависимых записях сначала изменяются хранимой процедурой, в работу которой вклиниваются несколько триггеров, после чего результат полируется заданием (джобом), который запускается каждые пять минут, а посреди всего этого отрабатывает репликация, а потом нечто похожее происходит и в базе-приемнике…

Продолжать скорбный список можно достаточно долго, но я надеюсь, что основную мысль я все-таки донес: попытка превратить неплохо работающую систему с нераспределенной базой в распределенную систему может привести к необходимости делать проект сначала.

Еще немного про коллизии, то есть про независимое изменение одних и тех же данных на разных площадках. При репликации они должны быть разрешены, а значит, из двух вариантов изменения должен быть выбран только один или отброшены оба изменения.

Коллизия – это в любом случае плохо. Разрешение коллизии, каким бы способом оно ни производилось (по времени изменения записи, по приоритету площадки или пользователя, администратором системы вручную после необходимых телефонных звонков), означает, что вы похоронили чью-то работу.

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

Коллизий не будет, если вам удастся организовать работу так, чтобы на разных площадках изменялись разные типы объектов. Проблем не будет, если номенклатуру и контрагентов добавлять только в центральном офисе, а использовать на всех площадках.

Несколько хуже, когда на разных площадках изменяются разные объекты одного типа, различаемые по какому-то признаку. Хуже потому, что этот признак тоже можно на какой-то площадке изменять. То есть формирование приходных и расходных накладных на один и тот же товар не доставит вам проблем, поскольку пара «Получатель» и «Отправитель» в таких накладных будет для каждой площадки разной, а вот с информацией о клиентах, приписанных к точкам обслуживания, работать будет хуже, поскольку клиенты любят менять точки обслуживания, и если вы отбирали клиентов для репликации по этому признаку, то неплохо понять заранее, где именно разрешить изменять этот признак и как будет происходить репликация измененной записи.

Для банковских и кассовых документов больше подходит регламентное разделение времени изменения документов. Например, три дня их можно изменять только на площадке, где они введены, а начиная с четвертого дня – только в центральном офисе.

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

Очень жаль, что в техническом задании не принято писать «Программа должна работать»…

Единственный общий совет этого раздела: старайтесь не экономить время на этапе постановки (написания технического задания). Реально это единственный этап, на котором время вам выделено именно для того, чтобы вы думали – дальше нужно будет трясти. Правда, руководство и на этом этапе требует сверхскоростей. Ведь для него хуже нет, чем вот уже два месяца платить зарплату и не иметь с этого не только никакого барыша, но даже хотя бы какого-то видимого результата. Однажды на очередной покрик «Давай-давай!!!» я отыскал в Интернете, распечатал и раздал всем генерально-исполнительным начало первой главы «Винни-Пуха»:

«Ну вот, перед вами Винни-Пух.

Как видите, он спускается по лестнице вслед за своим другом Кристофером Робином, головой вниз, пересчитывая ступеньки собственным затылком: бум-бум-бум. Другого способа сходить с лестницы он пока не знает. Иногда ему, правда, кажется, что можно бы найти какой-то другой способ, если бы он только мог на минутку перестать бумкать и как следует сосредоточиться. Но увы – сосредоточиться-то ему и некогда».

Алан Александр Милн «Винни-Пух и все-все-все» (пересказ Б. Заходера)

Авторитет Милна на время подействовал.

Оглавление книги


Генерация: 1.073. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз