как писать нечитаемый код (гарантированная работа на всю жизнь ;-)

введение

Не ищите злой умысел там, где все можно объяснить глупостью.
Наполеон


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

общие принципы

Quidquid latine dictum sit, altum sonatur.
(Что угодно, сказанное по-латински, звучит как мудрость.)


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

имена

- Когда _я_ беру слово, оно означает то, что я хочу, не больше и не меньше, - сказал Шалтай-Болтай презрительно.
Льюис Кэрролл, «Алиса в Зазеркалье», Глава 6.


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

Новое применение детских имен.Купите книгу для выбора имени ребенка, и у вас никогда не будет проблемы "как назвать переменную". Fred - отличное имя и печатается легко. Так же можно использовать другие простые для клавиатуры названия - asdf или aoeu, если вы используете DSK-клавиатуру.

Однобуквенные названия переменных.Если вы назовете ваши переменные a, b, c, их упоминания в коде будет невозможно найти с помощью поиска в текстовом редакторе. Кроме того, никто не сможет догадаться, для чего они нужны. Если кто-нибудь намекнет вам, что можно нарушить традицию времен FORTRAN-а именовать переменные-индексы i, j, k, заменив их ii, jj и kk - напомните им, что с еретиками делала Инквизиция.

Творческое использование ошибок.Если вы вынуждены использовать осмысленные названия переменных и функций, делайте в них орфографические ошибки. Делая ошибки в одних названиях и не делая в других (например, SetPintleOpening и SetPintalClosing), можно сделать невозможным использование grep и поиска в IDE. Это работает на удивление хорошо. Можно также использовать слова на разных языках или разные варианты написания одного и того же слова.

Используем абстракции.Придумывая названия функций и переменных, часто используйте абстрактные слова, такие как it, everything, data, handle, stuff, do, routine, perform, а так же цифры, например routineX48, PerformDataFunction, DoIt, HandleStuff и do_args_method.

А.К.Р.О.Н.И.М.Ы.Используйте акронимы, чтобы код был кратким. Настоящие мужчины не дают определений акронимам - они их понимают на
подсознательном уровне.

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

Используйте формы множественного числа из других языков.Скрипт в VMS отслеживал "statii", возвращаемые различными "VAXen". Языки Эсперанто, Клингон и Хоббитанский вполне пригодны для таких целей. Можно использовать «oj» из Эсперанто для создания форм множественного числа. Тем самым вы приближаете мир во всем мире.

ПроПисНые БукВы.Используйте прописные буквы в начале слогов внутри слова случайным образом. Например, ComputeRasterHistoGram().

Повторное использование имен.Как только правила языка допускают, используйте одинаковые имена для классов, конструкторов, методов, переменных, полей, параметров и локальных переменных. Более того, повторное используйте имена локальных переменных внутри блоков {}. Целью здесь является принуждение изучающего код программиста к аккуратному отслеживанию области видимости любой переменной. В частности, в Java, маскируйте обычные методы под видом конструкторов.

Используйте ограничения компилятора на длину имен.Если компилятор различает, скажем, только первые 8 символов имен, то изменяйте окончания, например var_unit_update() в одном случае и var_unit_setup() в другом. Компилятор воспримет оба случая как var_unit.

Знак подчеркивания - лучший друг.Используйте _ и __ в качестве идентификаторов.

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

Расширенные символы ASCII.Расширенные символы ASCII чудесно подходят в качестве имен переменных, в том числе и символы Я, Р и с. Их практически невозможно набрать в простом текстовом редакторе, не используя copy-paste.

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

Математические названия.Выбирайте имена переменных, обозначающие математические операторы, например:

openParen = (slash + asterix) / equals;

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

marypoppins = (superman + starship) / god;

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

Переименуй и используй.Этот трюк особенно хорошо работает в Ada - языке, устойчивом к большинству стандартных техник запутывания. Люди, придумавшие названия объектов и пакетов, которыми вы пользуетесь - идиоты. Вместо того, что пытаться изменить их, используйте переименование и подтипы для назначения собственных имен всему. Кое-где используйте оригинальные имена в качестве ловушек для невнимательных.

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

Соглашения.Игнорируйте Sun Java Coding Conventions, тем более что и сам Sun им не следует. К счастью, компилятор никому не расскажет о том, что вы их нарушили. Целью является использование имен, отличающихся лишь прописными и строчными буквами. Если вы вынуждены подчинятся соглашению о использовании прописных букв, вы можете нарушить его в случае неоднозначного выбора, например использовать оба варианта: inputFilename and inputfileName. Придумайте собственное безнадежно сложное соглашение об именах и затем ругайте всех, кто ему не следует.

Строчная буква l очень похожа на цифру 1.Используйте строчную букву l для обозначения констант типа long, например 10l скорее всего будет принято за 101 а не за 10L, которым оно является. Запретите любые шрифты, в которых явно различаются uvw, wW, gq9, 2z, 5s, il17|!j, oO08, `'", ;,. m nn rn, {[()]}. Подходите к делу творчески. Используйте недостатки шрифтов, в которых трудно различить указаные выше символы в парах идентификаторов вроде parselnt и parseInt или D0Calc и DOCalc. Идеальной парой имен переменных являются swimmer и swimner. А как вам такое имя переменной: swirnrner? :)
Кстати l является отличным выбором для имени переменной, так с первого взгляда этот символ похож на константу 1.

Похожие по написанию и звучанию имена переменных.Если у нас есть переменная с именем xy_z, почему бы не сделать несколько других переменных с именами xy_Z, xy__z, _xy_z, _xyz, XY_Z, xY_z и Xy_z. Преимущество переменных, отличающиеся от других только знаками подчеркивания и регистром символов, в том, что они мешают тем, кто запоминает имена по их звучанию или побуквенному написанию, вместо того, чтобы запоминать их полностью.

Повторное использование имен глобальных переменных в качестве локальных.Объявите глобальный массив в модуле A и локальный с таким же именем в заголовке модуля B. Таким образом, будет казаться, что в модуле B используется глобальный массив, хотя на самом деле это не так. Не упоминайте это в комментариях.

Снова повторное использование.Используйте области видимости как можно более сложно, дублируя имена переменных противоречивым образом. Например, у вас есть глобальные переменные A и B и функции foo и bar. Если известно, что переменная A будет постоянно передаваться в foo и B - в bar, объявите функции как function foo(b) и function bar (a), и, таким образом, внутри функций А будет ссылаться на B и наоборот. При большом количестве функций и глобальных идентификаторов возможно создать большую запутанную паутину взаимно противоречивых обращений к одним и тем же именам.

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

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

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

m_.Соглашение об именовании из мира С++ - использование m_ перед полями-членами класса. Это, в теории, должно помочь отличить их от методов. Но слово "method" тоже начинается с буквы "m".

o_apple obj_apple.Используйте префиксы o или obj для каждого экземпляра класса, чтобы продемонстрировать ту большую полиморфную картину, которую вы себе представляете.

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

Настаивайте на использовании c для констант в C++ и других языках, которые прямо запрещают присваивание константе.
Находите и используйте венгерские префиксы, используемые в других языках программирования. Например, настаивайте на использовании префиксов области видимости l_ и a_ {локальная переменная и аргумент} из PowerBuilder и всегда используйте стиль VB с префиксами для каждого типа элемента управления в С++. То, что в исходном коде MFC не используется венгерская нотация для типов элементов управления - вас не касается.

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

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

Настаивайте на использовании всей доступной информации в префиксах имен. К примеру, a_crszkvc30LastNameCol. У команды поддержки ушло три дня, чтобы понять, что это громадное имя переменной означает константную ссылку являющуюся аргументом функции, в которой содержится информация из колонки базы данных типа Varchar[30] с названием LastName, являющейся частью первичного ключа таблицы. Грамотно скомбинировав эту методику с принципом "все переменные должны быть public" вы получите способ изготовления кода, мгновенно устаревающего сразу после написания.

Используйте в собственных целях тот факт, что человеческий мозг способен удерживать не больше 7 элементов информации одновременно. К примеру, код, написанный в соответствии с вышеуказанным стандартом, имеет следующие свойства:
- одно выражение присваивания содержит 14 элементов информации о типах и именах;
- один вызов функции, в которую передаются три параметра и возвращается результат, содержит 29 элементов информации о типах и именах; - возможно улучшить этот хороший, но слишком немногословный стандарт. Вообразите, как обрадуются ваши коллеги и начальство предложению использовать 5-буквенный префикс, составленный из названия дня недели и времени для того чтобы различить код, написанный в Monam (понедельник утром) и FriPM (пятница пополудни);
- достаточно легко загрузить краткосрочную память даже с помощью не сильно сложно иерархической структуры, особенно если на экране нельзя одновременно увидеть начало и конец каждого блока.

Опять венгерская нотация.Еще один фокус с венгерской нотацией заключается в том, чтобы "изменить тип переменной, но ее имя не менять". Это уже произошло при переносе Windows-приложений : win16 WndProc(HWND hW, WORD wMsg,WORD wParam, LONG lParam) превратилась в Win32 WndProc(HWND hW, UINT wMsg,WPARAM wParam,LPARAM lParam), где w предполагает, что параметр является словом, хотя на самом деле это длинное целое. Логическим завершением этого подхода станет переход на Win64, когда параметры станут 64-битными целыми, а старые префиксы w и l останутся навсегда.

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

Загадочные ссылки на фильмы.Используйте имя константы вроде LancelotsFavouriteColour вместо blue и присвойте ей значение $0204FB. Цвет будет выглядеть почти как голубой, но при изучении кода придется экспериментировать c цветами, чтобы понять, на что похож этот цвет $0204FB. Только те, кто хорошо помнит фильм "Монти Пайтон и Святой Грааль", будут в курсе, что любимый цвет Ланселота - голубой. Те, кто не сможет вспомнить весь этот фильм, недостойны быть программистами.

маскировка

Чем дольше не проявляется ошибка в программе, тем труднее ее найти .Roedy Green

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

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

for(j=0; j{
total += array[j+0 ];
total += array[j+1 ];
total += array[j+2 ]; /* Main body of
total += array[j+3]; * loop is unrolled
total += array[j+4]; * for greater speed.
total += array[j+5]; */
total += array[j+6 ];
total += array[j+7 ];
}


Без подсветки синтаксиса заметите ли вы, что три строки кода закомментированы?

Пространства имен.Struct/union и typedef struct/union являются различными пространствами имен в C (но не в C++). Используйте одинаковые имена в обоих пространствах имен. Сделайте их, если возможно, почти совместимыми.

typedef struct {
char* pTr;
size_t lEn;
} snafu;
struct snafu {
unsigned cNt
char* pTr;
size_t lEn;
} A;

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

#define a=b a=0-b

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

#define fastcopy(x,y,z) /*xyz*/

...


fastcopy(array1, array2, size); /* ничего не делаем */

кстати, комментарий в этой строке нарушает принципы запутывания кода (прим. перев.)

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

#define local_var xy_z

разделите "xy_z" на две строки:

#define local_var xy\
_z // local_var OK


Таким образом, поиск xy_z по файлам ничего не даст, а для препроцессора С "\" в конце строки означает ее продолжение на следующей строке.

Произвольные имена, выглядящие как ключевые слова.В документации, если вам нужно произвольное имя для файла, используйте file. Ни в коем случае не используйте очевидно случайное имя вроде Charlie.dat или Frodo.txt. В общем, в примерах используйте в качестве произвольных имена как можно более похожие на ключевые слова. Например, хорошими именами для параметров будут: bank, blank, class, const, constant, input, key, keyword, kind, output, parameter, parm, system, type, value, var и variable. Если вы используете настоящие зарезервированные слова, и их не пропустит компилятор - тем лучше. Пользователи безнадежно запутаются в переменных, выглядящих как ключевые слова, а вы будете как бы и не причем, утверждая, что названия выбраны для того, чтобы помочь им разобраться в назначении переменных.

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

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

Как спрятать запретные глобальные переменные?Поскольку глобальные переменные - это "моветон", определите структуру для того, чтобы сложить их всех туда. Назовите ее правильно, например EverythingYoullEverNeed (ВсеЧтоКогдаНибудьПонадобится). Добавьте во все функции параметр-указатель на эту структуру и назовите его handle, чтобы еще больше все запутать. Таким образом, будет казаться, что вы не используете глобальных переменных, а получаете нужные значения через handle. Затем объявите структуру статически и передавайте указатель на нее, чтобы код всегда использовал один и тот же ее экземпляр.

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

#define xxx global_var // в файле std.h
#define xy_z xxx // в файле ..\other\substd.h
#define local_var xy_z // в файле ..\codestd\inst.h


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

Перегрузка для загрузки.В C++ переопределите стандартные функции с использованием #define. Это будет выглядеть, как будто вы используете стандартную функцию, в то время как вы будете использовать нечто совершенно другое.

Выбор лучшего перегруженного оператора.В C++ используйте перегрузку операторов «+», «-», «*», «/» для выполнения операций, не имеющих никакого отношения к сложению, вычитанию, и т. д. Таки, если Страуструп может использовать оператор сдвига для ввода-вывода, чем мы хуже его? При переопределении оператора «+» удостоверьтесь, что i = i + 5; имеет смысл, совершенно отличающийся от i += 5;. Вот пример высокого искусства запутывания с использованием перегрузки операторов: Перегрузите оператор «!» для класса, но сделайте так, чтобы он не имел никакого отношения к инвертированию или отрицанию. Заставьте его возвращать целое число. Затем, чтобы получить из него логическое значение, используйте «! !» Однако логическое значение будет инвертированным, поэтому вы должны использовать «! ! !». Не перепутайте оператор «!», возвращающий 0 или 1, с оператором побитовой инверсии «~».

Перегрузка newПерегрузите оператор new - это гораздо более опасно, чем перегрузка «+», «-», «*» и «/». Заставив этот оператор выполнять что- либо, не имеющее отношения к его настоящему назначению, вы заведете всех, кто попытается создать экземпляр объекта, в тупик. Еще можно скомбинировать это с использованием зависимости от регистра, определив метод с именем New или переменную с таким именем.

#define.Выражение #define в C++ заслуживает посвящения ему отдельной статьи за его возможности в плане запутывания кода. Используйте определенные с помощью #define переменные с именами в нижнем регистре, маскируя их под обычные переменные. Никогда не используйте параметры для макросов препроцессора. Используйте только глобальные определения с помощью #define. Одним из наиболее впечатляющих вариантов использования препроцессора, о котором я слышал, было требование пятикратного прохода по коду, перед тем как его можно было скомпилировать. Мастер запутывания кода, грамотно используя #define и #ifdf, способен заставить файлы заголовков определять различные вещи в зависимости от того, сколько раз они включены. Это становится особенно интересным в случае включения одного заголовочного файла в другой. вот особенно запутывающий пример:

#ifndef DONE

#ifdef TWICE

// put stuff here to declare 3rd time around
void g(char* str);
#define DONE

#else // TWICE
#ifdef ONCE

// put stuff here to declare 2nd time around
void g(void* str);
#define TWICE

#else // ONCE

// put stuff here to declare 1st time around
void g(std::string str);
#define ONCE

#endif // ONCE
#endif // TWICE
#endif // DONE

Развлечение начнется при передаче функции g() параметра char*, поскольку будут вызываться различные версии g() в зависимости от того, сколько раз был подключен заголовочный файл.

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

документация

Правду может сказать любой дурак, но чтобы хорошо лгать, требуется умный человек.
Samuel Butler (1835 - 1902)


Неверная документация - это хуже, чем отсутствие документации вообще.
Bertrand Meyer


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

Лгите в комментариях.Даже не обязательно постоянно лгать, достаточно забывать синхронизировать комментарии с изменениями в коде.
Документируйте очевидные вещи.Заполните код комментариями вроде /* добавить 1 к i */ однако никогда не документируйте важные вещи, вроде конечного назначения модуля или метода.

Документируйте "Как?" вместо "Зачем?".Документируйте только детали работы программы, а не цель ее работы. Таким образом, при ошибке будет невозможно понять, что вообще должен делать код.

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

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

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

По сути, документ должен описывать алгоритм в виде иерархии шагов, разложенных в в виде отдельных параграфов с автоматической нумерацией. Используйте минимум 5 уровней вложенности заголовков. Убедитесь, что в итоге получится не менеьше 500 автоматически пронумерованных параграфов. Пример из жизни:

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

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

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

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

Глюки.Никогда не документируйте глюков в коде. Если вы подозреваете, что в классе есть ошибка - держите это при себе. Если у вас есть идеи, как переписать или реорганизовать код, ради бога, не записывайте их. Помните слова кролика Тампы из "Бэмби": "Если не можешь сказать что-нибудь хорошее, лучше промолчать." Вдруг ваши комментарии увидит программист, написавший этот код? Или владелец фирмы? А если клиент? Да вас же уволят. Лучше ограничьтесь анонимным комментарием "это нужно исправить", относящимся неясно к чему, и никто не будет считать, что его критикуют.

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

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

/* Оптимизированный внутренний цикл.
Эта часть слишком сложная для тупиц из Software Services Inc., которые потратили бы на нее в 50 раз больше памяти и процессорного времени, используя дебильные функции из. */
class clever_SSInc
{
.. .
}


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

Кобол на перфокартах.Всеми силами сопротивляйтесь новым веяниям в области средств разработки. Не верьте, что до любого объявления можно перейти одним кликом и исходите из предположения, что код, разработанный в Visual Studio 6.0 будет поддерживаться человеком, использующем edlin или vi. Настаивайте на использовании драконовского стиля комментариев, чтобы похоронить за ними исходный код.

Комментарии Монти Пайтона.Для метода с названием makeSnafucated вставьте JavaDoc /* make snafucated */. Нигде не определяйте, что означает слово snafucated. Только дурак не знает точно, что означает snafucated. Примеры использования этого метода можно посмотреть в Sun AWT JavaDOC.

разработка программы

Важнейшее правило создания сложного в поддержке кода - каждое значение должно быть определено в как можно большем количестве мест и как можно большим количеством способов.
- Roedy Green


Чтобы писать легкий для поддержки код, любые сведения должны описываться только один раз. Таким образом, если что-то нужно изменить - достаточно изменить в одном месте и программа останется работоспособной. Следовательно, для написания кода, который невозможно поддерживать, данные необходимо описывать каждый раз заново, во множестве мест и множеством способов. К счастью, языки вроде Java изо всех сил стараются сделать написание такого неподдерживаемого кода легкой задачей. Например, почти невозможно изменить тип часто используемой переменной, так как все приведения и преобразования перестанут работать, так же как и присвоения к временным переменным старого типа. Кроме того, если значение переменной отображается на экране, весь связанный с ней код форматирования и ввода данных необходимо просмотреть и исправить вручную. Семейство "алголоподобных" языков, включающее C и Java, описывает сохранение данных в массивах, хэш-таблицах, файлах и базах данных совершенно различным образом. В языках вроде Abundance и, частично, Smalltalk, этот синтаксис одинаков - нужно только поменять объявление. Пользуйтесь ошибками Java. Поместите данные, которые в будущем будут занимать больше места, чем доступная память, в массив. Программист, который будет поддерживать программу, замучается переделывать его для использования файлов. Или же помещайте небольшие файлы в базу данных, а потом ему придется заниматься оптимизацией производительности, переделывая это все с использованием массивов.

Приведение типов в Java.Способ приведения типов в Java - дар богов. Его можно использовать без малейшего сомнения, так как этого требует сам язык. Каждый раз, получая объект из Collection, необходимо привести его к исходному типу. Таким образом, обращение к этому типу можно поместить во множестве мест кода. Если когда-либо тип нужно будет изменить, придется так же исправлять все преобразования типов. Компилятор не особо поможет тому неудачнику, который будет поддерживать ваш код, понять, все ли обращения он исправил и не исправил ли он чего лишнего. Например, все приведения к (short) придется изменить на (int) если тип переменной short изменится на int. Существует движение за то, чтобы ввести обобщенный оператор приведения типов (cast) и обобщенный оператор преобразования (convert), которые сделают ненужными переделку кода в случае изменения типов переменных. Постарайтесь, чтобы эта ересь не попала в спецификацию языка. Проголосуйте "против" RFE 114691 и любых обобщений, которые сделают множество приведений типов ненужными.

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

Bubblegum b = new Bubblegom();

К несчастью, популярность оператора ++ не дает толком использовать псевдоизбыточный код, вроде такого:

swimmer = swimner + 1;

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

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

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

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

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

Холостые интерфейсы.Напишите пустой интерфейс с названием вроде WrittenByMe и реализуйте его во всех ваших классах. Затем напишите классы- обертки для всех используемых встроенных классов Java. Идея заключается в том, чтобы все объекты в вашей программе реализовывали этот интерфейс. В конце концов, напишите все методы таким образом, что их аргументы и возвращаемые значения будут иметь тип WrittenByMe. Это сделает практически невозможным разобраться в том, как работают методы, и заставит использовать множество веселых преобразований типов. Кроме того, заставьте всех в команде написать собственный личный интерфейс (например, WrittenByJoe) и реализовать его в их классах. Затем вы сможете ссылаться на объекты с помощью переменных большого количества бессмысленных типов интерфейсов.

Гигантские обработчики.Никогда не создавайте отдельные обработчики событий для компонентов. Для всех кнопок в проекте создайте один обработчик и заполните его гроздьями if...else чтобы выяснить, какая кнопка была нажата.

Слишком хорошо - тоже нехорошо.Используйте инкапсуляцию и ООП на всю катушку, например:

myPanel.add( getMyButton() );
private JButton getMyButton()
{
return myButton;
}


Выглядит безвредно, да? Не беспокойтесь. Когда нибудь ЭТО сработает ;)
Друг моего друга.Как можно чаще используйте friend-объявления в С++. В дополнение к этому, используйте передачу указателя класса-создателя создаваемому классу. Теперь вам не придется тратить ваше время на всякие мелочи вроде продумывания интерфейсов. И используйте ключевые слова private and protected - это продемонстрирует хорошую инкапсуляцию кода.

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

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

Обертки, обертки, обертки.Если вам приходится использовать чужой код, отделите ваш код от чужого грязного кода как минимум одним слоем оберток. Иначе что вы будете делать, если когда-нибудь автор чужого кода поменяет названия всех методов? Конечно, можно будет глобально переименовать все вызовы в VAJ (Visual Age for Java) или сделать обертку уже потом. Но возможность такой ситуации является отличным поводом для того чтобы изолировать чужой код раньше, чем с ним сделают что-нибудь невменяемое. Один из главных недостатков Java в том, что невозможно решить множество мелких задач без бессмысленных методов-оберток, которые не делают ничего, кроме вызова другого метода с таким же или похожим именем. Это означает, что есть возможность написать обертки до четырех уровней вглубь, и никто не обратит внимания. Для еще большего помрачения рассудка переименуйте методы-обертки, используя различные синонимы из словаря. Это создаст иллюзию того, что методы что-то действительно делают. Кроме того, множество синонимов затруднит выработку единой терминологии для проекта. Чтобы никто не посмел привести ваш код к меньшему уровню оберток, кое-где пропускайте обертки и вызывайте код напрямую.

И еще неМНОГО оберток.Все функции API должны быть окружены обертками минимум 6-8 раз, с объявлениями функций в различных исходных файлах. Используйте #define для создания кратких ссылок на эти функции.

У нас нет от вас секретов!Объявите все методы и поля публичными. Вдруг кто-нибудь когда-нибудь захочет их использовать? Однажды объявленный публичным метод уже не скроешь, не так ли? Это затруднит в дальнейшем исправление внутренних алгоритмов. Кроме того, у этого способа есть приятный побочный эффект - становится неясным назначение класса. Если начальство начнет спрашивать в своем ли вы уме - ответьте, что вы следуете классическим принципам прозрачности интерфейсов.

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

Перемены и перестановки.Измените порядок следования параметров в методе с названием drawRectangle(height, width) на drawRectangle(width, height) без изменений в имени метода. Через несколько релизов поменяйте их обратно. С первого взгляда понять, какой из вариантов вызывается, будет невозможно. Читателю предоставляется возможность самому придумать другие варианты использования этого способа.

Тема с вариациями.Вместо передачи параметра в один метод, создайте столько методов, сколько сможете. Например, вместо setAlignment(int alignment) где alignment является константой со значениями, соответствующимb left, right и center, создайте три метода: setLeftAlignment, setRightAlignment и setCenterAlignment. Само собой, для полного эффекта, скопируйте общий код во все три метода, затруднив поддержание их согласованными.

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

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

And That`s Final!Определите все классы, у которых нет наследников, как final. В конце концов, вы проект закончили, и никто не сможет улучшить его, расширяя ваши классы. Тем более, что это может быть потенциальной проблемой безопасности - ведь java.lang.String сделан final именно по этой причине. Если другие работники, участвующие в проекте, будут возмущаться, расскажите им об увеличении производительности.

Удаляйтесь интерфейсов.Используя Java, презирайте интерфейсы. Если начальство возмущается, скажите им, что интерфейсы Java вынуждает вас использовать "cut-and-paste" для кода классов, которые реализуют одинаковые интерфейсы, и они знают, как тяжело это будет поддерживать. Вместо этого поступайте как разработчики Java AWT - поместите в классы всю функциональность, доступную только классам-наследникам и добавьте в методы множество проверок экземпляра класса (instanceof). Таким образом, если кому-то понадобится использовать ваш код - пусть расширяют ваши классы. Если им нужен код из двух ваших классов – ну, значит, не повезло, одновременно они их расширить не смогут. Если все-таки приходится использовать интерфейсы, сделайте один универсальный и назовите его как-нибудь вроде ImplementableIface. Еще один перл - добавление Impl к именам классов, которые реализуют интерфейсы. Это можно использовать, например, для классов, которые реализуют Runnable.

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

Переменные окружения.Если вы пишете классы для использования другими программистами, поместите код, проверяющий переменные окружения (getenv() в C++ и System.getProperty() в Java) в статические инициализаторы классов и передавайте все аргументы классам в таком виде, вместо параметров конструктора. Преимущество такого подхода в том, что инициализаторы вызываются при загрузке модулей программы, до создания любых экземпляров классов, то есть обычно они будут вызываться до функции main(). Другими словами, у программы не будет возможности повлиять на значения параметров до того, как они попадут в ваши классы - пользователи должны настраивать переменные окружения так же как и вы!

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

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

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

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

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

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

Локальные переменные.Не используйте локальных переменных. Если вы испытываете искушение использовать их, лучше сделайте их полями экземпляра или статическими полями, чтобы разделить их с другими методами класса. Это сократит работу в будущем, когда другим методам понадобятся похожие переменные. Программисты, использующие C++, могут также сделать все переменные глобальными.

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

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

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

продолжение следует



Roedy Green, Canadian Mind Products. Перевод Zju.
обсуждение статьи


Сетевые решения. Статья была опубликована в номере 02 за 2006 год в рубрике PRIcall

©1999-2024 Сетевые решения