- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
The Programmers Stone (Программистский камень) - Alan Carter
Шрифт:
Интервал:
Закладка:
Определение границ атома познания может происходить циклически, а квалифицированный архитектор укажет для них правильное место, где верхний уровень, где нижний, а что посередине. На начальных стадиях это может быть огромный, единый атом познания, который нужно передать в руки ответственного работника и сказать: «Попробуй разобрать эту мешанину, пожалуйста!»
По определению мы не знаем, каков наилучший подход к атому познания. Если бы мы знали, то он не был бы атомом. Из этого следует, что это не может планироваться на основе диаграмм планирования проектов (диаграмм Ганта) в терминах подцелей. Это должно быть одной задачей, а о длительности можно только догадываться. Опытные картостроители поднаторели в догадках, но они не могут объяснить, почему проблема тянет на два дня, неделю, полгода. Поэтому у того, кто дал наилучший прогноз, очень мало аргументов в его защиту. Боязнь последующих объяснений — важный фактор, который часто отбивает у картостроителей охоту проявлять свои интуитивные способности и выдавать необходимые для планирования проекта цифры.
В результате расщепления атома познания работник обычно может выложить очень детальный набор описаний задач (целей), основанный на твердом понимании того, что должно быть сделано. Таким образом, во многих проектах следует поправить диаграммы Ганта, добавив туда расщепление атомов познания. Мы предполагаем, что большая часть проектов, пытающихся распланировать по Ганту все по дням, демонстрирует всеобъемлющую линейную модель производства. Программисты, работающие по таким диаграммам Ганта, не могут получить выгод из разумного управления атомами познания. Вместо того, чтобы повернуть свой разум к решаемой проблеме, они будут доказывать, что они хорошие работники, под «прессом», как будто унижая их можно заставить думать более ясно. Это грозит стрессом и снижает производительность.
Плато качества
Когда принята стратегия формирования мысленной карты проблемной области и попыток упростить ее, обычно сталкиваются с проблемой определения момента окончания работы над картой. Эта проблема возникает на каждом уровне проектирования. Сверхъестественно, но почти всегда есть глубокое решение, которое значительно проще всех остальных и очевидно минимальное. (Есть много способов это выразить, но потом этот вывод станет очевидным.) Хотя проповеди типа: «Ты узнаешь это, когда увидишь!» — несомненная истина, но они не говорят, куда посмотреть.
Единственный честный аргумент, который мы можем здесь предложить — это обещание, что это действительно произойдет. И хотя это ничего не доказывает, все что мы можем сделать — это показать работающий пример приведения в минимальное состояние. Но это работает — спросите любого, кто пытался.
В качестве примера возьмем код из прекрасной книги Джеффри Рихтера (Jeffrey Richter's Advanced Windows). Эта книга — полезное чтение для любого, кто пытается писать программы для Win32 API (Application Programming Interface) (поскольку иначе у вас не появится мысленная карта семантики системы).
Рихтер очень четко раскладывает по полочкам вопросы использования Win32, но даже в его примерах (и, в частности, как результат соглашений, которым он следует) появляется сложность, которую мы попробуем убрать. На странице 319 имеется функция SecondThread() Мы просто посмотрим на эту функцию, опустив остальную программу и некоторые глобальные определения:
DWORD WINAPI SecondThread (LPVOID lpwThreadParm) {
BOOL fDone = FALSE;
DWORD dw;
while (!fDone) { // Wait forever for the mutex to become signaled.
dw = WaitForSingleObject(g_hMutex, INFINITE);
if (dw == WAIT_OBJECT_0) { // Mutex became signalled.
if (g_nIndex >= MAX_TIMES) {
fDone = TRUE;
} else {
g_nIndex++;
g_dwTimes[g_nIndex — 1] = GetTickCount():
} // Release the mutex.
ReleaseMutex(g_hMutex);
} else { // The mutex was abandoned.
break; // Exit the while loop.
}
}
return(0);
}
Для начала просто упростим стиль скобок, уберем пробел между ключевым словом и открывающей скобкой, а также многословный комментарий к ReleaseMutex. Мы в курсе, что идет религиозная война между последователями Кернигана и Ритчи (K&R) и последователями Вирта (Wirth) по поводу стиля скобок, но симметрия обрамления блока действительно позволяет лучше увидеть некоторые вещи. Дополнительная строка, которая при этом появляется, даст выигрыш чуть позднее — следуйте за нами!
DWORD WINAPI SecondThread(LPVOID lpwThreadParm) { BOOL fDone = FALSE; DWORD dw; while(!fDone) { // Wait forever for the mutex to become signaled. dw = WaitForSingleObject(g_hMutex, INFINITE); if(dw == WAIT_OBJECT_0) { // Mutex became signalled. if(g_nIndex >= MAX_TIMES) { fDone = TRUE; } else { g_nIndex++; g_dwTimes[g_nIndex — 1] = GetTickCount(): } ReleaseMutex(g_hMutex); } else { // The mutex was abandoned. break; // Exit the while loop. } } return(0); }
Очень легко можно избавиться от одной локальной переменной: dw присваивают значение, а в следующей операции тестируют. Инвертирование смысла проверки помогает локализовать ссылку (проверка, затем изменение g_nIndex). А пока мы здесь, нет смысла инкрементировать g_nIndex просто для того, чтобы вычесть 1 из текущего значения в следующей операции! Мы уже использовали постфиксную форму оператора инкремента языка Cи, который как раз для этого и предназначен.
DWORD WINAPI SecondThread (LPVOID lpwThreadParm) { BOOL fDone = FALSE; while (!fDone) { // Wait forever for the mutex to become signaled. if (WaitForSingleObject(g_hMutex, INFINITE)==WAIT_OBJECT_0) { // Mutex became signalled. if (g_nIndex < MAX_TIMES) { g_dwTimes[g_nIndex++] = GetTickCount(); } else { fDone = TRUE; } ReleaseMutex(g_hMutex); } else { // The mutex was abandoned. break;// Exit the while loop. } } return(0); }
Прерывание цикла (break) зависит только от результата WaitForSingleObject, поэтому естественно переместить проверку в управляющее выражение, избавляясь от прерывания цикла и одного уровня вложенности:
DWORD WINAPI SecondThread (LPVOID lpwThreadParm) { BOOL fDone = FALSE; while (!fDone && WaitForSingleObject(g_hMutex, INFINITE)==WAIT_OBJECT_0) { // Mutex became signalled. if (g_nIndex < MAX_TIMES) { g_dwTimes[g_nIndex++] = GetTickCount(); } else { fDone = TRUE; } ReleaseMutex(g_hMutex); } return(0); }
Теперь просто сожмем… Мы знаем — многие стандарты кодирования говорят, что мы всегда должны ставить фигурные скобки, поскольку иногда у глупых людей получается нечитаемая мешанина, но посмотрите, что получается, когда мы пренебрегаем этим правилом и концентрируемся на повышении читаемости кода.
DWORD WINAPI SecondThread (LPVOID lpwThreadParm) { BOOL fDone = FALSE; while (!fDone && WaitForSingleObject(g_hMutex, INFINITE)==WAIT_OBJECT_0) { if (g_nIndex < MAX_TIMES) g_dwTimes[g_nIndex++] = GetTickCount(); else fDone = TRUE; ReleaseMutex(g_hMutex); } return(0); }
Теперь немного настоящей ереси. Черт возьми, в момент когда мы покончим с этой полной безответственностью, результат окажется совершенно неочевидным. (Здравый смысл поможет сделать лучше, чем правила.)
Ересь в том, что если мы знаем, для чего наши переменные, то мы знаем их типы. Если мы не знаем, для чего предназначена переменная, знание ее типа мало поможет. В любом случае, компилятор все равно сделает проверку типов. Поэтому избавимся от венгерской записи, а заодно и от переопределений типов, которые просто определены (#define), но не для нас. Сокрытие разыменования используя typedef — другое бесцельное упражнение, поскольку хотя и позволяет выполнить некоторую инкапсуляцию валюты, этого совершенно недостаточно, чтобы избавиться от беспокойства по этому поводу, поэтому аккуратные программисты вынуждены держать настоящие типы в голове. Поддержка концепции дальних указателей в именах переменных для 32 битного API с плоской адресацией — тоже довольно глупое занятие.
DWORD SecondThread (void *ThreadParm) { BOOL done = FALSE; while (!done && WaitForSingleObject(Mutex, INFINITE)==WAIT_OBJECT_0) { if (Index < MAX_TIMES) Times[Index++] = GetTickCount(); else done = TRUE; ReleaseMutex(Mutex); } return(0); }
Теперь смотрите. Мы достигнем Плато Качества…
DWORD SecondThread(void *ThreadParm) { while(Index < MAX_TIMES && WaitForSingleObject(Mutex, INFINITE) == WAIT_OBJECT_0) { if (Index < MAX_TIMES) Times[Index++] = GetTickCount(): ReleaseMutex(Mutex); } return(0); }
Одиннадцать строк против 26. На один уровень меньшая вложенность, но структура полностью прозрачна. Две локальных переменных исчезли. Нет блоков. Совсем нет вложенных else. Меньше мест, где могут скрываться ошибки.
(Если вы еще не программировали используя потоки (threads), то повторная проверка значения Index внутри тела цикла кажется грубой и ненужной. Если же программировали, то повторная проверка естественна и очевидна. Это очень важно: пока текущий поток приостановлен в WaitForSingleObject, другой поток скорее всего будет активен и изменит значение. То, что для вас очевидно, зависит от вашего опыта: еще одна мораль из этого примера — рассмотрения только структуры куска кода недостаточно.)
Наконец, текст делает совершенно ясным, что разные потоки выполняют функции в разных контекстах. Поэтому совершенно не нужно определять функцию с именем FirstThread(), в точности такую же, как SecondThread(), и вызывать их так:
hThreads[0] = CreateThread(…, FirstThread…); hThreads[1] = CreateThread(…, SecondThread…);
Когда можно просто
hThreads[0] = CreateThread(…, TheThread…); hThreads[1] = CreateThread(…, TheThread…);
Почти треть этого примера получена клонированием! Если мы обнаружим ошибку в одной реализации, нам нужно будет не забыть исправить аналогичные ошибки везде. Зачем беспокоиться, когда можно просто слить их в одну. Это хороший способ, когда поджимают сроки.

