355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Ларри Константин » Человеческий фактор в программировании » Текст книги (страница 9)
Человеческий фактор в программировании
  • Текст добавлен: 6 октября 2016, 04:26

Текст книги "Человеческий фактор в программировании"


Автор книги: Ларри Константин



сообщить о нарушении

Текущая страница: 9 (всего у книги 26 страниц) [доступный отрывок для чтения: 10 страниц]

20
Свет мой, зеркальце

«Свет мой, зеркальце, скажи, да всю правду доложи: я ль на свете всех милее»? Злой королеве из «Белоснежки» достаточно было посмотреть в зеркало, чтобы получить точную картину. Разработчики программного обеспечения тоже должны иметь такую возможность. Им нужны хорошие зеркала, которые просто и точно отражают создаваемое программное обеспечение. Злая королева могла быть недовольна тем, что видела в зеркале; тем не менее ее зеркало давало достоверную и понятную картину.

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

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

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

Получение картины

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

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

Хорошая система обозначений позволяет создавать картины, которые можно интерпретировать аналитически, с помощью тщательного изучения деталей, и понять интуитивно как гештальт,[27]27
  От нем. gestalt – структура, модель некоего явления, интегрированная таким способом, что воспринимается как единое целое, которое не может быть достигнуто как простая сумма его частей.


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

.

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

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

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

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

Система обозначений и юзабилити

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

Разработка хорошей системы обозначений подобна разработке хорошего графического интерфейса. Целью здесь является сокращение нагрузки на человеческую память. Великий закон юзабилити утверждает, что система должна быть доступной для использования человеком, который знает о ее назначении, но не знаком с ее программным обеспечением. При этом не должно возникать необходимости в обучении, помощи или руководствах (Constantine, 1991 [14]). Таким образом, по-настоящему хорошая система обозначений – это такая система, которую опытный разработчик, знающий, как проектировать и писать программы, сможет интуитивно понять без шпаргалок и недельных курсов обучения. Вы не должны запоминать разные условные обозначения (например, то, что прямоугольник с двойной рамкой означает динамический объект, а прямоугольник с флажком в углу – компонент библиотеки).

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

Сильные связи между частями должны выглядеть, как сильные связи; слабые связи должны выглядеть, как более слабые. Устойчивые объекты должны быть показаны сплошными фигурами, а динамические объекты должны выражать смысл активности или изменяемости. Например, наследование одним классом свойств и характеристик другого класса означает, что подкласс в большой степени зависит от родительского класса – подобно тому как генетические особенности ребенка сильно зависят от особенностей обоих родителей. Для точного отражения характеристик программного обеспечения, использующего наследование, этот механизм должен быть показан более четко, чем передача сообщения объекту или ссылка на него как на атрибут (Page-Jones, Constantine и Weiss, 1990 [57]).

Сделать систему обозначений интуитивно понятной и простой в изучении помогут небольшие детали. В системе обозначений для объектно-ориентированных программ, разработанной Джекобсоном (Jacobson и др., 1992 [44]), те объекты, которые взаимодействуют с внешним окружением, на одной стороне имеют символ (– в качестве визуального ключа, обозначающего это взаимодействие. Динамические объекты, которые управляют последовательностью действий, снабжены стрелкой, входящей в границу, что означает цикл или итерацию. В нескольких системах обозначений внутренние элементы компонентов, которые доступны извне, показаны в виде расширений границы компонента.

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

Подумайте об этом.

Из журнала Software Development, том 2, № 3, март 1994 г.

21
Методичное сумасшествие

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

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

В основе всех основных методов анализа и проектирования программного обеспечения лежит очень небольшой набор базовых принципов, которые постоянно открываются заново и описываются новыми словами, но речь по-прежнему идет о все том же старом гуталине. Суть дела заключается в том, как люди решают сложные задачи. Все методы разработки программного обеспечения базируются на пяти принципах: (1) упорядочен-ное продвижение, (2) решение с помощью разбиения, (3) независимость компонентов, (4) целостность компонентов, (5) структурное соответствие.

Шаг первый

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

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

Разбиение

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

Мы имеем. Если мы сталкиваемся с неподатливой сложностью, то что мы делаем? То же, что делали наши предки, когда имели дело с огромной ногой мастодонта, которую нельзя проглотить: мы откусываем часть и начинаем ее пережевывать. Для того чтобы справиться с большой задачей, мы разбиваем ее на более мелкие. Здесь есть некоторая магия ума, махание руками, которое, вообще говоря, нисколько не уменьшает сложность задач, возникающих при программировании. Но зачастую это так или иначе помогает, создавая у перегруженного разработчика иллюзию, что большие, сложные системы можно построить из множества маленьких, простых кусочков кода.

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

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

В традиционном структурном проектировании эти принципы воплощены в общепризнанных понятиях связывания и сцепления. Связывание – это мера взаимосвязи и взаимозависимости между компонентами программного обеспечения; сцепление определяет, в какой мере компонент является четко определенным функциональным целым. Эти давно известные понятия были заново открыты в новом мире объектной технологии. Хорошие объектно-ориентированные методы напоминают программистам об уменьшении объектного связывания и об увеличении сцепления методов с помощью инкапсуляции всего необходимого в отдельный объект (Henderson-Sellers и Constantine, 1996 [39]). В противном случае вы получите запутанный клубок переплетенных между собой объектов, не поддающихся анализу и повторному использованию.

Структурное соответствие

Все три первых принципа решения сложных задач касаются отдельных частей программного обеспечения, но они мало что говорят о том, как эти части лучше всего объединить в рабочее целое. Большинство методов в той или иной форме говорят о необходимости ориентироваться на реальное окружение задачи и повторять структуру задачи в структуре решения. Другими словами, программное обеспечение необходимо планировать в соответствии с той практической задачей, для решения которой оно предназначено. В этом состоит принцип структурного соответствия, своего рода вариант высказывания Баухауса (Bauhaus), перенесенного в область разработки программного обеспечения: форма должна следовать из функции.

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

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

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

Естественно, для того чтобы создать хороший метод и оправдать звание настоящего методиста, требуется намного больше. Например, вы должны обладать способностью выдумывать новые слова. Вам нужно применять как можно больше новых терминов, иначе люди подумают, что они уже знают то, о чем вы им рассказываете. Кроме того, вы должны уметь растягивать несколько хороших идей на 700 страниц текста. И не забудьте про систему обозначений, про те красивые фигурки, которые отличают ваш метод от множества других. Однако будьте осторожны и не сделайте картинки слишком простыми, а принципы – слишком прозрачными, иначе вы не сможете обосновать свои гонорары за консультантские и преподавательские услуги!

Из журнала Software Development, том 2, № 5, май 1994 г.

22
Говоря по существу

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

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

Понятие сущностного моделирования в разработке программ происходит, по крайней мере, от идей структурного проектирования. Схемы потоков данных были придуманы в качестве непроцедурной модели вычислений, которая отделяла суть того, что должен был выполнить компьютер, от того, как это должно быть исполнено посредством алгоритма или процедуры. Модульная конструкция, соответствующая сути задачи, позволяет создавать более надежное программное обеспечение, основа которого сохраняется при изменениях в деталях процесса. По крайней мере, такова была идея. На практике схемы потоков данных зачастую превращались чуть ли не в обыкновенные блок-схемы с забавными символами. Последующие дополнения, такие как сохранение данных и управляю-щая логика, неявным образом способствовали процедурному мышлению. В конце концов сущностные модели получили признание с появлением книги «Essential Systems Analysis» (Основы системного анализа) (МсМе-namin и Palmer, 1984). Эта книга, признанная сегодня классической, сделала сущностные модели краеугольным камнем перестроенного и обновленного метода структурного анализа.

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

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

Сущностные интерфейсы

Разработка пользовательских интерфейсов – это одна из областей, где сущностные модели могут найти хорошее применение. Моделирование сущностных пользовательских ситуаций при проектировании интерфейсов является подходом, расширяющим разработанный Джекобсоном (Jacobson и др., 1992 [44]) метод объектно-ориентированных пользовательских ситуаций. Сущностная пользовательская ситуация – это абстрактный сценарий одного законченного и полезного взаимодействия с системой, рассматриваемого с точки зрения намерений или цели пользователя. Это обобщенное описание вида или категории использования системы, которое передает действия пользователя и ответы системы[28]28
  Наверное, эти две стороны модели еще лучше охарактеризовать как намерения пользователя и функции системы. Именно так они обозначены в методе проектирования, ориентированном на пользователя и основанном на существенных пользовательских ситуациях (Constantine и Lockwood, 1999 [30]).


[Закрыть]
в упрощенной и обобщенной форме. Суть идеи состоит в том, что разрабатываемые интерфей-сы должны соответствовать намерениям пользователей – тому, что пользователи хотят достичь. Исходные условия, связанные с технологией, – форма визуальных компонентов или даже вид устройств, применяемых для взаимодействия, – должны быть минимальны.

Для примера возьмем задачу по извлечению наличных денег из банкомата. Физическая модель может быть следующей: клиент вставляет карту; система читает информацию с магнитной полоски и запрашивает PIN-код;[29]29
  PIN (Personal Identification Number) – личный номер, код.


[Закрыть]
пользователь вводит PIN-код; система подтверждает PIN-код и предлагает меню возможных операций; пользователь выбирает нужную операцию; система предлагает меню счетов; пользователь выбирает счет; система запрашивает сумму; пользователь вводит сумму и нажимает на кнопку подтверждения; система выдает наличность; пользователь берет деньги и убегает. Что может быть проще?

Магнитные полоски

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

Эта сущностная пользовательская ситуация также прокладывает путь к более простому интерфейсу, поскольку выделяет суть дела с точки зрения пользователя, а именно получение наличных денег, что является самой распространенной операцией, выполняемой с помощью карточек. Большинство пользователей регулярно снимают одну и ту же сумму с одного и того же счета. Первый вариант действия, предлагаемый системой, может соответствовать типичному выбору данного пользователя и воспроизводиться из памяти: «Обычные 250 долларов с текущего счета, мистер Чат-ворт?». Или что-то в этом роде.

Сущностная модель отражает идеализированную цель проектирования. Хорошо разработанный пользовательский интерфейс требует ровно столько шагов или столько информации, сколько определено в сущностной пользовательской ситуации. Клиенту банка должно быть достаточно сказать: «Это я. Как обычно. Спасибо!» – и уйти. Мы определяем идеальный случай – если бы мы его не смоделировали, то не смогли бы проектировать в соответствии с ним. А без проектирования в соответствии с идеальным случаем можно не увидеть, где нас ограничивает технология или технические условия, и упустить возможность использования альтернативных подходов.

Re: редизайн

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

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

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

Транс-действия

К сожалению, многие профессионалы испытывают затруднения в выявлении сути задач. Не каждый способен к абстрактному мышлению, необходимому для сущностного моделирования. Обдумывание сути требует отказа от технических шор, мешающих увидеть предметы в новом свете. Меилир Пейдж-Джонс (Meilir Page-Jones) называет это «разыменовыва-нием», мысленным выходом за рамки принятых понятий и представлений. Особенно это удается одаренным комедийным актерам и талантливым дизайнерам.

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

Меилир сразу же перевел мне: «Остерегайтесь машин, выпрыгивающих из воды слева».

Разыменовывайтесь. Попробуйте. И старайтесь думать по существу. Из журнала Software Development, том 2, № 11, ноябрь 1994 г.


    Ваша оценка произведения:

Популярные книги за неделю