Как писать код, который невозможно поддерживать
в рубрике Java, Другое, Колонка Спанч Боба, Методологии
Перевод статьи Roedy Green Unmaintainable code. Для удобства, мы разбили статью на несколько частей, сохранив структуру и логику изложения автора. Сегодня мы публикуем первую часть. Она посвящена правилам именования.
Предисловие
Это самое популярное из моих эссе. Удивительно, сколько людей не догадывается, что автор дает “вредные советы”.
У меня есть великая цель: создать как можно больше рабочих мест для Java-программистов. И я хочу поделиться секретами мастерства: как написать код, который будет адски трудно поддерживать. Настолько трудно, что людям, которые будут заниматься этим после вас, придется потратить немало человеко-лет на внесение самых пустяковых изменений. Кроме того, у вас есть все шансы обеспечить себе пожизненную занятость, если только вы будете в точности следовать этим советам: ведь кроме вас, никто не сможет справиться с этой адской задачей. Правда, в конце концов рано или поздно наступит такой момент, когда вы обнаружите, что сами не разбираетесь в собственном коде!
Не переборщите, сразу принимаясь за дело слишком усердно. Ваш код не должен выглядеть как абсолютно безнадежный с точки зрения его поддержки, но он должен быть таким. Иначе его быстро перепишут или подвергнут рефакторингу, и все ваши усилия будут потрачены впустую.
Основные принципы
Quidquid latine dictum sit, altum sonatur.
(Что сказано на латыни, звучит внушительно)
Чтобы вконец запутать программиста, который будет заниматься сопровождением вашего кода (далее будем называть его условным противником), вам нужно предугадывать ход его мысли. Итак, у него есть огромный объем написанного вами кода, но нет времени на то, чтобы прочитать весь этот код, а уж тем более — чтобы досконально разобраться в нем. Его цель — быстро найти место, где нужно что-либо изменить и быстро внести эти изменения (при этом желательно, чтобы не возникло никаких неприятных побочных эффектов).
Он смотрит ваш код по частям. Каждый раз он видит лишь небольшой фрагмент вашего кода. И ваша задача — помешать ему составить общую картину. Вы должны максимально усложнить для него поиск нужного кода. Но самое главное — вы должны поставить его в такую ситуацию, когда он не сможет игнорировать ни один фрагмент вашего кода.
Программисты часто полагаются на соглашения. Вы же слегка нарушайте их, чтобы заставить своего условного противника внимательно читать каждую строчку написанного вами кода. Сначала может показаться, что любое свойство языка делает код неподдерживаемым. Это не совсем так; однако, если подойти к делу с умом и неправильно использовать возможности языка в нужных местах, можно легко добиться своей цели.
Однако, рефакторинг (занятие, которому каждый программист предается с великим удовольствием) может помешать вам в достижении вашей цели.
Именование
Когда я использую слово, — сказал Шалтай-Болтай презрительно, — оно означает только то, что я имею в виду — не более и не менее
(Льюис Кэррол, «Алиса в Зазеркалье»)
Первое, что вам придется освоить, чтобы овладеть мастерством писать неподдерживаемый код — это использовать всю мощь именования переменных и методов. Компилятор не может контролировать соблюдение правил именования — так воспользуйтесь же этим преимуществом, чтобы запутать условного противника и полностью его деморализовать.
1. Источники вдохновения
Откуда черпать вдохновение, спросите вы? Нет ничего проще. Купите какую-нибудь популярную книжку из серии “Как назвать ребенка”, и заимствуйте из него идеи по именованию переменных. Вот, например, чем плохо имя Анна? Короткое, звучное, и на транслите набирать легко. Еще проще, разумеется, набирать “qwerty” или “asdfgh” — это на заметку тем, кто не хочет слишком утруждать себя набором символов при вводе имен переменных.
2. Только одна буква
Еще одна неплохая идея — использование однобуквенных имен переменных. Прелесть этого способа в том, что, называя переменные a, b, c, вы добиваетесь важного стратегического преимущества: их потом будет невозможно искать в простом текстовом редакторе. И, конечно, никто и никогда не догадается, для чего нужны эти переменные. А если кто-нибудь заикнется о том, что славная традиция времен языка FØRTRAN использовать имена i, j, и k для счетчиков циклов несколько устарела, и теперь повсеместно используются ii, jj и kk, просто выразительно посмотрите на него и напомните, как поступала с еретиками испанская инквизиция.
3. Креативный подход, или многообразие вариантов
Далее, побольше креатива с разными вариантами написания имен переменных! Если уж вы вынуждены использовать описательные имена переменных и функций, вы можете писать одно и то же по-разному в разных местах. Например, разные варианты написания фрагмента имени функции из логической пары (SetPintleOpening, SetPintalClosing) лишают вашего условного противника возможности использовать grep или поисковые средства среды разработки. Использование лингвистических тонкостей вроде разных вариантов написания одного и того же слова в британском и американском английском придаст вашей стратегии изящества и утонченности.
4. Абстрактное мышление
Побольше используйте абстрактные термины (it, everything, data, handle, stuff, do, routine, perform), не забудьде и о цифрах: routineX48, PerformDataFunction, DoIt, HandleStuff и do_args_method.
5. А.К.Р.О.Н.И.М.Ы.
Используйте акронимы — это придает вашему коду достоинство немногословности и придает программному коду загадочности. Помните: настоящие программисты никогда не объясняют используемые сокращения, понимание сокращений у них поддерживется на генетическом уровне. Если вы еще не развили в себе эту способность, некоторым данную природой, потренируйтесь. Сначала, конечно, вам придется использовать описательные имена переменных. И, убедившись в том, что ваш код работает, срочно автоматически переименуйте все переменные и методы, заменив осмысленные имена абревиатурами. Кстати, можно автоматизировать этот процесс, доведя его до полного логического завершения — превращения передающих смысл имен в бессмысленный набор символов. О том, как это сделать, мы еще расскажем.
6. Синонимы
Возьмите словарь синонимов. Найдите как можно больше близких по смыслу слов (например, display, show, present), и используйте их, чтобы показать, что разница есть, хотя на самом деле ее нет. И наоборот, в именах функций, имеющих разное назначение, используйте одно и то же слово (например, используйте одно слово print для обозначения действий “печать на бумаге”, “запись в файл” и “вывод на экран”).
7. Не составляйте глоссарий проекта
Ни в коем случае не соглашайтесь составить глоссарий проекта, в котором будут даны четкие и недвусмысленные определения всех встречающихся терминов. Составление глоссария — явное нарушение принципа структурного проектирования, известного как сокрытие информации.
Если же все-таки написания глоссария не избежать, используйте определения в стиле “веревка есть вервие простое”; вот наглядный пример, взятый из руководства к Ant 1.6.5: “basedir: the absolute path of the project’s basedir (as set with the basedir attribute of <project>). [basedir: абсолютный путь к basedir проекта (определяется значением атрибута basedir)]“. После прочтения этого определения не становится понятно, что же такое basedir (можно предположить, что это абсолютный путь, хотя в приведенных примерах он начинается с символа “.”, с которого начинается относительный путь).
Водите читателя за нос — притворяйтесь, что вы даете нужную информацию там, где ее нет. При этом формулируйте свои пустые фразы так, чтобы у читателя осталось чувство, что он не способен понять смысл тех элементарных вещей, которые ему обяъсняют.
Не используйте простые и наглядные примеры — они просты для понимания и позволяют быстро схватить идею. Предоставьте читателю возможность поломать голову над тем, что же все-таки будет использоваться в качестве этого загадочного basedir при компиляции пакета com.mindprod.holidays — C:\, C:\com, C:\com\mindprod или C:\com\mindprod\holidays? Если же под чьим-то давлением вам все же приходится приводить примеры, смело критикуйте такой подход, называя его детским и непрофессиональным. Приводите такой аргумент: простые примеры могут создать у пользователей впечатление, что возможности вашего продукта ограничиваются лишь этим. Ведь ваша цель — не научить пользователя, как пользоваться программным продуктом, а поразить богатыми возможностями этого продукта! Разумеется, такого сильного впечатления простыми и конкретными примерами не создашь. А люди уважают то, что слишком абстрактно для простого понимания. В конце концов, ни один академик еще не привел ни одного простого примера!
8. Лингвистические головоломки: образование формы множественного числа по правилам, заимствованным из других языков
Один из скриптов VMS наследовал VAX-овский акцент, образуя множественное число. Вы можете пойти гораздо дальше, используя эсперанто, клингонский язык и даже язык хоббитов. На псевдо-эсперанто для образования множественного числа добавляется окончание oj. Вносите тем самым свой посильный вклад в борьбу за мир во всем мире.
9. РаБотАйтЕ С клАвИшЕй ShIfT
Разработайте псевдослучайный алгоритм переключения между верхним и нижним регистром и используйте его в середине слов, как в голову взбредет. Вот пример: ComputeRasterHistoGram().
10. Одинаковые имена
Везде, где только возможно, используйте одинаковые имена для классов, конструкторов, методов, членов-переменных, параметров, глобальных и локальных переменных. Не упускайте возможность повторно использовать имена для локальных переменных внутри блоков {}. Пусть условный противник ломает себе голову, обращая внимание на область видимости каждой переменной. В частности, в Java существуют богатые возможности сделать так, чтобы методы выглядели как конструкторы.
11. Диакритические значки
По возможности не упускайте случая использовать символы с диакритическими значками в именах переменных. Особенно изящно будет смотреться такой пример:
typedef struct { int i; } ínt;
Как видите, в имени ínt используется символ í, который похож на i. В обычном текстовом редакторе отличить его от i будет непросто!
12. Еще одна идея: длины имен и ограничения компилятора
Если существуют ограничения компилятора на длину имен (предположим, компилятор ограничивает длину имен первыми 8 символами), умело используйте окончания после 8-го символа. Например, вы можете использовать var_unit_update() в одном случае и var_unit_setup() — в другом. Компилятор интерпретирует оба наименования как var_unit.
13. Подчеркивание — ваш надежный товарищ
Используйте _ и __ в именах переменных.
14. Смешенье языков
Перемешайте несколько языков в произвольном порядке. Используйте эту тактику при написании комментариев и в именах переменных.
Не ограничивайтесь одним человеческим или машинным языком! Покажите свою эрудицию. Если начальство все же будет настаивать на использовании какого-то одного языка, скажите, что вы лучше излагаете свои мысли на вашем родном языке. Если же это не помогает, перейдите в контратаку: заявите, что подвергаетесь лингвистической дискриминации и угрожайте судом.
16. Символы из расширенного набора ASCII
Символы из расширенного набора ASCII чертовски привлекательно смотрятся в именах переменных. Рекомендую обратить особое внимание на ß, Ð, и ñ. Их тайное стратегическое преимущество в том, что их практически невозможно никак иначе вставить в текст программы, кроме как скопировав в буфер обмена из текстового редактора.
17. Имена, заимствованнные из других языков
Еще одна хорошая идея — заимствовать имена переменных из других языков. Например, вместо английского слова “point” можно использовать немецкое “punkt”. Представляете, сколько времени потратит условный противник, чтобы расколоть ваши головоломки! Заодно, возможно, ему удастся выучить несколько новых слов на разных языках. Это существенно расширит его кругозор.
18. Больше математики там, где не нужно!
Имена переменных легко замаскировать под математические операторы. Например, вот так:
openParen = ( slash + asterix ) / equals;
19. Звонкие имена
Используйте имена переменных, заимствованные из совершенно другой предметной области. Например, имена героев сказок или культовых фильмов вполне пригодны для этого. Разбавляйте их другими терминами, отвлекающими условного противника от логики программы, на которой он пытается сконцентрироваться при чтении вашего кода. Заставьте его немного подумать о вечном:
marypoppins = ( superman + starship ) / god;
20. Правильное использование i
Никогда не используйте символ i в качестве счетчика внутреннего цикла. Выберите какой-нибудь другой символ (лучше, конечно, каждый раз выбирать разные буквы алфавита). Что угодно, только не i. А эту самую i используйте, давая имена нецелочисленным переменным. Аналогично поступите с n: используйте n в качестве счетчика цикла.
21. Соглашения-заглушения
Игнорируйте общепринятые соглашения (например, Sun’s Coding Conventions; не стесняйтесь, ведь сама компания Sun делает это). Ведь компилятор на это не реагирует. Ваша цель — создавать разные имена переменных, которые различаются только регистром одного или двух символов. Если же вас все-таки вынуждают соблюдать соглашения об использовании регистра символов, вы все-таки можете изловчиться там, где выбор между верхним и нижним регистром неоднозначен: например, использовать оба варианта написания: inputFilename и inputfileName.
Еще лучше придумать свои собственные, сложные и запутанные правила именования. И затем выговаривать всем остальным за то, что они не следуют этим правилам.
22. Похожие символы
Используйте символ l (в нижнем регистре) для обозначения констант увеличенной точности: ведь 10l гораздо легче принять за 101, чем 10L. Выбирайте шрифты, в которых похожие символы и комбинации символов (0 и О, 1 и l, l и I) практически неразличимы. Проявите изобретательность, добейтесь, чтобы строку uvw wW gq9 2z 5s il17|!j oO08 `'" ;:,. m nn rn {[()]} вы приняли за что-то совсем другое.
23. Мыслите глобально о локальном!
Используйте одинаковые имена для глобального массива, объявленного в модуле A и приватного массива, объявленного в заголовочном файле для модуля B. Этим вы собьете с толку условного противника: ему может показаться, что вы используете глобальный массив в модуле B, хотя на самом деле это не так. И помните: если вы напишете об этом в комментариях, вы испортите шутку.
24. Больше путаницы при повторном использовании имен!
Повторное использование имен — мощное оружие, позволяющее деморализовать условного противника. Нужно всего лишь правильно это делать. Например, пусть у нас имеются две глобальные переменные A и B, а также функции foo и bar. Если переменная A чаще всего будет передаваться функции foo, а переменная B — функции bar, то, объявляя функции, объявите их как foo(B) и bar(A), чтобы внутри этих функций B именовалось как A и наоборот. Это внесет дополнительную путаницу. Делайте так почаще со всеми функциями и переменными. Чем больше функций и переменных у вас имеется, тем более смелые эксперименты вы можете ставить с повторным использованием их имен.
25. Больше путаницы при повторном использовании переменных!
Везде, где только можно, повторно используйте имена переменных, никак не связанных между собой. И наоборот: используйте одну временную переменную для двух совершенно разных целей (возможно, это позволит немного сократить размер стека). Но это довольно жесткие методы; более гуманно объявить переменную — например, присвоить ей значение в начале очень длинного метода, а затем где-нибудь в середине этого метода неожиданно поменять это значение. Хорошим примером может служить объявление значений координат в начале метода и приведение этих координат к другому базису в середине. Разумеется, об этом изменении вы не должны ни слова упоминать в комментариях.
26. Скрщн прнст успх
При использовании сокращений внутри имен переменных, не давайте условному противнику скучать, вносите разнообразие, используя несколько вариантов написания одного и того же слова. Это не позволит расслабиться, используя примитивные средства вроде текстового поиска для понимания каких-либо аспектов вашего кода. Не забудьте о различных вариантах написания в британском и американском английском. Чередуйте их. Например, можно писать через раз color и colour. Усложните условному противнику задачу: вариантов написания все-таки ограниченное число, и если вы будете каждый раз писать слово полностью, то условный противник скоро выучит все ваши уловки наизусть. Сокращайте слова! Сокращайте их так, чтобы было невозможно понять, что вы имеете в виду. Для отвода глаз можете даже разок небрежно упомянуть о том, что используете сокращения.
27. Имена, сбивающие с толку
Имена методов нужно тщательно подбирать. Нужно стремиться к тому, чтобы каждый метод выполнял нечто большее (или меньшее), чем обещает его имя. Например, метод isValid(x) может иметь побочный эффект: преобразовывать x в двоичную константу и сохранять результирующее значение в базе данных.
28. m_
В C++ существует соглашение об именовании: символ m_ используется в начале имен членов. Это позволяет отличить их от методов, поскольку имена методов также начинаются с символа m.
29. o_apple и obj_apple
Используйте префиксы “o” и “obj” в именах разных экземпляров одного класса, чтобы продемонстрировать, что вы думаете о более глобальных вещах.
30. Венгерская нотация
Венгерская нотация при умелом использовании может быть столь же мощным оружием в умелых руках, как атомная бомба. Пожалуй, ничто не может помочь вам одержать полную победу над условным противником быстрее, чем это средство. Вот несколько тактических приемов его применения, которые следует иметь под рукой.
a_crszkvc30LastNameCol? Правильно, это постоянная ссылка, являющаяся аргументом функции, которая содержит данные, получаемые из поля базы данных с именем LastName, имеющего тип varchar(30), входящего в первичный ключ таблицы.31. Шедевры мировой культуры
Используйте имена констант, напрягающие память условного противника и развивающие его чувство прекрасного. Например, используйте LancelotsFavouriteColour вместо blue для константы 0×0204FB. На экране этот цвет выглядит точь-в-точь как самый обычный синий. Однако вашему условному противнику придется помучиться, выясняя, что это за цвет. И только тот, кто знает Monty Python and the Holy Grail наизусть, догадается, что любимый цвет Ланселота — синий. А если условный противник этого не знает, то какой, спрашивается, он программист?
32. Продолжаем экспериментировать с цветами
Понятно, что вместо названий цветов лучше использовать числовые константы. Однако условный противник не дремлет, овладевает передовыми технологиями и наверняка знает, как расшифровываются шестнадцатеричные цветовые коды. Возьмем, например, 0×0204FB. Очевидно, что Red = 02, Green = 04, Blue = FB. Значит, скорее всего, это синий цвет. Гораздо труднее ему будет ориентироваться, если использовать десятичные коды. Вряд ли кто-либо способен без помощи калькулятора определить, что 132347 соответствует синему цвету. В идеале нужно подбирать цвета, десятичный код которых будет шестнадцатеричной записью других цветов: например, 808000 (в шестнадцатеричном представлении, это темно-желтый цвет) на самом деле является 0×0c5440 (голубой цвет).
Однако, останавливаться на достигнутом не стоит, и можно поэкспериментировать еще и с переименовыванием цветов: мало кто представляет себе, как выглядят puce, teal и papayawhip, и это оставляет широкий простор для экспериментов с переназначением цветовых констант.
Подшутите немного над условным противником: используйте стандартные имена цветов, но выберите самые уродливые или кричащие цвета. Если ваш условный противник ленив, то первое, что он сделает — изменит цвета на более подходящие, но имена тех ужасных цветов, которые вы использовали, останутся теми, которые вы им присвоили.
Один комментарий на “Как писать код, который невозможно поддерживать”
Прокомментировать
Вы должны быть авторизованы для комментирования.




:
[...] Наталья пишет: Возьмите словарь синонимов. Найдите как можно больше близких по смыслу слов (например, display, show, present), и используйте их, чтобы показать, что разница есть, хотя на самом деле ее нет. И наоборот, в именах функций, имеющих разное … [...]
24 июля, 2010 в 16:11