Более того, если вы будете последовательно следовать этим правилам, вы сможете гарантировать себе работу на всю жизнь, поскольку никто другой не сможет понять ваш код.
Основные принципы
Чтобы сорвать планы вашего коллеги по обслуживанию кода, вы должны понять, как он думает. У него есть ваша гигантская программа, но у него нет времени прочитать весь код, а тем более понять его.Он хочет побыстрее найти ошибку и исправить ее, чтобы в дальнейшем она никак не влияла на дальнейшую работу системы.
Сейчас он уже изучает ваш код под лупой.
В любой момент времени он может видеть только небольшую часть вашей программы, и вам нужно убедиться, что он никогда не сможет увидеть всю картину.
Вы должны сделать так, чтобы он никогда не смог найти то, что ему нужно.
И, что еще более важно, вы должны настолько усложнить его работу, что любое упущение приведет к катастрофе.
Специальные методы
1. Ложь в комментариях.Не обязательно врать на каждом шагу: достаточно, чтобы комментарии никак не были связаны с кодом.
2. Заполните свой код комментариями типа /* добавьте 1 к i */, но никогда не включайте в свой код общие вещи, такие как назначение модуля или метода.
3. Убедитесь, что каждый метод делает немного больше (или немного меньше), чем предполагает его название.Например, метод isValid(x) может, помимо прочего, преобразовывать x в двоичный формат и сохранять результаты в базе данных.
4. Используйте сокращения, чтобы сделать код кратким.Настоящие мужчины никогда не дают определений своим аббревиатурам: они понимают их на подсознательном уровне.
5. Чтобы создать еще больший эффект, старайтесь не использовать инкапсуляцию.Тем, кто вызывает метод, нужны все возможные подсказки, чтобы напомнить им, как работает этот метод.
6. Если вы, скажем, разрабатываете систему бронирования авиабилетов, сделайте так, чтобы для добавления новой авиакомпании вам приходилось редактировать код как минимум в 25 разных местах.И никогда не указывайте в комментариях, где именно: те, кто собирается работать с вашим кодом, не имеют права вносить в него коррективы, если не понимают его до конца.
7. Чтобы повысить эффективность, скопируйте и вставьте фрагменты кода несколько раз.Это будет быстрее, чем создавать несколько модулей для повторного использования.
8. Никогда не пишите комментарии к переменным.Информация об использовании переменной, ее границах, допустимых значениях, скрытом и отображаемом количестве десятичных знаков, единицах измерения, форме представления, правилах заполнения переменной (например, максимальное количество символов или обязательное заполнение), соответствуют ли значения переменной можно доверять и т. д. должны быть идентифицированы в ходе работы программы.
Если ваш начальник заставляет вас делать комментарии, делайте их в теле метода, но никогда не пишите комментарии при объявлении переменных, даже временных.
9. Постарайтесь записать как можно больше данных в одну строку.В этом случае нет необходимости вводить множество временных переменных, а также уменьшается размер исходных файлов, так как становится меньше строк и лишнего пустого места.
Еще одним преимуществом написания длинных строк кода является то, что разработчикам, которые не могут читать текст размером в 6 пунктов, приходится увеличивать шрифт, чтобы увидеть записи в строке.
10. Кд, нпснный бз глснх, крч [код, написанный без гласных, короче].Сокращая имена переменных или методов, дайте волю своему воображению и используйте несколько вариантов одного и того же слова, иногда записывая его в полной форме.
Таким образом, вы сможете бороться с бездельниками, которые используют текстовый поиск, чтобы понять лишь одну из функций вашей программы.
Рассмотрите различные варианты написания названий, которые могут сбить с толку других.
Например, вы можете одновременно использовать британский цвет, американский цвет и определенные варианты этого слова, такие как кулерц.
В случае, когда вы пишете все имена полностью, каждая из ваших переменных имеет только одно написание.
Специалист службы поддержки легко их запомнит. А поскольку одно слово можно сократить несколькими способами, для одной и той же цели можно использовать несколько разных переменных.
Более того, специалист по сопровождению программного обеспечения может не заметить, что это несколько отдельных переменных.
11. Никогда не используйте автоматическое выравнивание кода.Попробуйте как-то повлиять на начальство, чтобы оно запретили это в вашей компании, аргументируя это тем, что автоматическое прокачивание вызывает неожиданные изменения в системе контроля версий или тем, что у каждого разработчика должен быть свой стиль прокачки и он должен оставаться нетронутым ни в одном из написанных им модулей.
Отключить автоматическое выравнивание не так уж и сложно, хотя это может значительно сократить количество нажатий клавиш и дней, потраченных на непонимание плохо выровненного кода.
Вы должны настаивать на том, чтобы все использовали один и тот же стиль форматирования не только при сохранении данных в общем репозитории, но и при редактировании кода.
Это вызовет массовое негодование среди сотрудников, и руководство будет вынуждено отменить автоматическое форматирование, чтобы сохранить спокойствие.
Затем вы можете случайно выровнять свой код так, что возникнет оптическая иллюзия, что тела циклов и условий будут выглядеть длиннее или короче, чем должны быть, или что ключевое слово else соответствует неправильному ключевому слову if.
12. Никогда не вставляйте фигурные скобки { } в блоки if-else, если этого не требует синтаксис.Когда в вашем коде подряд несколько выражений и блоков if-else, причем при неправильном выравнивании, вы можете запутать даже опытного коллегу.
13. Обязательно строго следуйте рекомендациям, запрещающим операторы перехода, выходы из цикла и прерывания цикла, особенно если вы можете увеличить глубину цикла if-else еще как минимум на пять уровней.
14. Используйте длинные имена переменных, отличающиеся друг от друга всего одним символом или регистром.Идеальная пара переменных — пловец и пловец.
Воспользуйтесь всеми возможными недостатками шрифтов, которые не позволяют различить ilI1| или oO08, например, в случае parselnt и parseInt или D0Calc и DOCalc. В этой ситуации наиболее подходящим символом в имени переменной является буква l, так как с первого раза ее невозможно отличить от константы 1. Имена переменных также могут отличаться только регистром, например, HashTable и Hashtable.
15. Если у вас мало времени, попробуйте повторно использовать существующие несвязанные имена переменных.Аналогичным образом вы можете использовать одни и те же временные переменные для разных целей, тем самым уменьшая размер стека.
Это действительно жестоко, если вы немного меняете переменную: в начале очень длинного метода вы можете присвоить переменной одно значение, а потом где-то в середине вы спокойно меняете его на другое - скажем, меняете начало индекса переменная от 0 до 1. Только убедитесь, что это изменение не записано в документации.
16. Используйте строчную букву l при обозначении длинных констант. Например, 10л гораздо легче спутать с 101, чем с 10л.
17. Не обращайте внимания на общепринятые соглашения об именах Java для классов и переменных, которые, например, обычно пишут классы, переменные строчными буквами, константы пишутся с заглавной буквы, а слова в именах классов/переменных пишутся с заглавной буквы.Даже Sun не следует этим соглашениям (примеры: instanceof/isInstanceOf, Hashtable/HashTable).
Не волнуйтесь, компилятор даже не выдаст вам предупреждения.
Однако, если ваш начальник заставляет вас следовать общепринятым соглашениям, всякий раз, когда у вас есть выбор, начинать ли с заглавной буквы или нет, писать все заглавными буквами или писать в случайном порядке, например, вы можете использовать как имя входного файла, так и имя выходного файла.
Более того, вы можете пойти на крайние меры и создать свои собственные безумно сложные правила именования, а затем обвинять других в несоблюдении этих правил.
Идеальный вариант — создать как можно больше имен переменных, лишь на всякий случай немного отличающихся друг от друга.
18. Никогда не используйте i в качестве внутренней переменной в цикле.Вы можете выбрать любое имя, но только не это.
Вы можете использовать переменную i для других целей.
Точно так же вы можете использовать n в качестве счетчика циклов.
19. Никогда не используйте локальные переменные.Всякий раз, когда вы чувствуете, что вам нужно сделать переменную локальной, объявите ее как объект или статическую переменную, чтобы она широко использовалась всеми другими методами в классе.
Вы избавите себя от лишней работы в дальнейшем, когда вам понадобится объявить те же переменные в других методах.
Разработчики C++ иногда идут еще дальше и объявляют все возможные переменные глобальными.
20. Никогда не документируйте ошибки, которые не обнаружены компилятором.Если вы думаете, что в классе скрывается ошибка, лучше промолчать об этом.
Если у вас есть идеи, как пересобрать или переписать код, ради бога, не пишите о них нигде.
Что, если программист, работающий над этим кодом, увидит ваши комментарии? А что, если их увидит руководитель вашей компании или клиент? Вас могут уволить.
21. Для развлечения можно подобрать как можно больше слов, обозначающих одно и то же действие, например, показать, показать, представить.Неявно подразумевают, что между элементами программы якобы есть небольшая разница, которой на самом деле не существует. И наоборот, если вы видите две похожие функции, имеющие принципиальную разницу, всегда используйте одно и то же слово для описания этих функций.
Например, слово печать означает одновременно запись в файл, печать на принтере и отображение на экране.
Не слушайте никого, кто попросит вас дать определения всем конкретным терминам.
Это было бы непрофессионально с точки зрения принципа сокрытия информации.
22. В именах функций старайтесь использовать более абстрактные понятия, такие как оно, все, данные, дескриптор, материал, делать, подпрограмма, выполнение и числа, например, подпрограммаX48, PerformDataFunction, DoIt, HandleStuff и do_args_method.
23. В Java все параметры базового типа фактически доступны только для чтения, поскольку они передаются по значению.Вызываемая процедура может изменять эти параметры, но не окажет никакого влияния на значения переменных в вызывающей процедуре.
Все объекты, наоборот, можно изменять, так как в этом случае ссылка на эти объекты передается по значению, то есть сам объект передается по ссылке.
Поэтому вызываемая процедура может изменять поля вашего объекта любым способом.
Никогда не говорите о том, может ли метод изменять поля в каждом из передаваемых им параметров.
Названия ваших методов должны указывать на то, что сами методы могут только читать значения этих полей, хотя на самом деле эти методы могут их изменять.
24. Никогда не указывайте в комментариях единицы измерения для любого типа переменных — входных, выходных или параметров — будь то футы, метры или поля.Это имеет большое значение не столько для точности расчетов, сколько для процесса разработки в целом.
Поэтому не указывайте в комментариях коэффициенты пересчета каких-либо единиц измерения или методы, с помощью которых были получены окончательные значения.
Простой и очень эффективный способ — записать в комментариях неправильные единицы измерения.
Если у вас совсем нет совести, вы можете придумать свои единицы измерения, назвав их своим именем или именем неизвестного человека и не давая никаких объяснений.
Если вас кто-то критикует, вы можете сказать, что таким образом вы отделяете целочисленные вычисления от операций с числами с плавающей запятой.
25. Инженерные расчеты можно программировать двумя способами.Первый метод заключается в преобразовании всех входных значений в единицы СИ, выполнении вычислений, а затем преобразовании выходных значений в соответствующие единицы.
Второй способ: проводить расчеты в различных смешанных системах измерения.
Всегда выбирайте второй метод.
26. Еще один малоизвестный трюк.Исключения вызывают слишком много проблем.
Хорошо написанный код никогда вас не подведет, поэтому в принципе можно обойтись и без исключений: нет смысла тратить на них время.
Подклассы исключений предназначены для невежественных людей, которые знают, что их программа не будет работать.
Вы можете значительно упростить свою программу, если оставите во всем приложении (в основном методе) единственный оператор try-catch, который вызывает метод System.exit().
27. Компиляторы C преобразуют выражение myArray[i] в *(myArray + i), что эквивалентно *(i + myArray), что, в свою очередь, эквивалентно i[myArray].Настоящие профессионалы знают, как использовать эту возможность в своих целях.
Этот метод, к сожалению, применим только в «родных» классах.
28. Если у вас есть массив из 100 элементов, старайтесь как можно чаще присваивать переменным определенное значение 100. Не используйте статические, ненаследуемые константы вместо этого значения и не ссылайтесь на него через свойство myArray.length. Чтобы еще больше затруднить изменение этого значения, вы можете использовать другие конкретные числа, например 50 вместо 100/2 или 99 вместо 100-1. Вы можете еще больше замаскировать это значение, заменив выражение a > 100 на a == 101 или выражение a > = 100 на a > 99. Вам также необходимо учитывать такие особенности, как формат страницы, который состоит из заголовка x, основного текста y и примечаний z. Вы сами можете создать путаницу как в каждой из частей кода, так и в их сочетании или общем количестве.Эти проверенные временем методы работают наиболее эффективно, когда в программе есть два несвязанных массива, по счастливой случайности содержащих по 100 элементов каждый.
Для таких случаев существуют еще более изощренные методы.
Чтобы облегчить задачу сопровождающему, создайте именованную константу и случайно используйте конкретное значение 100 вместо именованной константы снова и снова.
Но, кроме всего этого, худшим, что можно сделать в этой ситуации, было бы использование время от времени других отдельных констант, которые в данный момент времени чисто случайно окажутся равными 100. Итак, вам действительно следует избегать использования конкретная система именования, которая связывала бы имя массива с константой, равной его длине.
29. Избегайте любой формы табличной логики.Это только кажется безобидным, но вскоре приводит к тому, что конечные пользователи начинают в этом разбираться, а затем корректируют таблицы под себя.
30. Постарайтесь сделать структуру кода максимально сложной.Опытные программисты могут вставлять до 10 пар круглых скобок ( ) в одну строку и до 20 пар фигурных скобок { } в одном методе.
Настоящие мастера умеют размещать начало и конец блока на разных страницах объявления.
По возможности в условных циклах используйте тернарные операторы [?:] вместо if.
31. Ищите руководства по программированию, авторы которых скорее пишут книги, чем программы.Вы можете пойти в местный книжный магазин и выбрать книги, в которых много сложных диаграмм и нет ни одного примера написания кода.
Взгляните на эти книги и выделите в них слишком умные слова.
Этими словами можно отпугнуть наглых новичков, претендующих на ваше место.
Ваш код должен впечатлять других.
Если другие не понимают терминологию, которую вы используете, они подумают, что вы очень умны, а ваши алгоритмы слишком сложны.
Не используйте примитивную лексику при объяснении того, как работает ваш алгоритм.
32. Постоянно вносите изменения в свой код и заставляйте пользователей обновляться как можно чаще: ведь никто не хочет работать с устаревшей версией вашей программы.Независимо от того, насколько пользователям нравится ваша программа, просто представьте, сколько удовольствия они получат после того, как вы ее «улучшите».
Никому не говорите, что именно вы изменили в новой версии, если от вас этого не требуют: зачем говорить о багах в старой версии, которых никто бы не заметил?
33. В разделе «О программе» следует указать только название программы, имена разработчиков и предупреждение об авторских правах, состоящее только из юридических терминов.В идеале он должен содержать ссылку на несколько мегабайт кода, создающего красивую анимацию.
Но ни в коем случае нельзя указывать фактическое назначение программы, дополнительный номер версии, дату последнего изменения в программе, сайт, на котором можно получать обновления, или электронную почту автора программы.
При таком подходе вскоре все пользователи будут работать с разными версиями программы и попытаются установить сразу версию N+2 вместо версии N+1.
34. Чем больше изменений вы сможете внести, тем лучше.Вы не хотите, чтобы вашим пользователям все больше наскучил один и тот же API или пользовательский интерфейс.
Кроме того, если вы можете вносить изменения так, чтобы пользователи этого не заметили, это тоже плюс: это не заставит их чувствовать, что их ждут.
35. Если вам нужно написать классы, которые будет использовать другой разработчик, вставьте код проверки переменных среды (getenv() в C++, System.getProperty() в Java) в статические безымянные инициализаторы ваших классов, а затем передайте все свои аргументы классам.как таковой.
, вместо того, чтобы делать то же самое в конструкторе.
Преимущество такого подхода в том, что инициализаторы вызываются во время загрузки программных модулей, до создания экземпляров классов, то есть, как правило, они будут выполняться до функции main().
Другими словами, программа не сможет изменить эти параметры до того, как они попадут в ваши классы: значения переменных среды пользователя должны быть такими же, как вы их установили.
36. Выбирайте имена для переменных, не связанные с соответствующими метками, появляющимися после запуска программы.Например, если одно из полей на экране помечено как Почтовый индекс, вы можете назвать соответствующую переменную zip.
37. Java позволяет создавать методы, имеющие те же имена, что и классы.Более того, сами методы могут не быть конструкторами.
Используйте эту возможность, чтобы запутать всех.
38. Никогда не используйте менеджеров по размещению.Таким образом, когда специалист по сопровождению продукта добавляет новое поле, ему придется вручную находить абсолютные координаты каждого элемента на экране.
Если ваш начальник заставляет вас использовать менеджеры макетов, используйте один режим GridBagLayout и выберите определенные значения в качестве координат сетки.
39. Забудьте об интерфейсе Java. Если ваше руководство жалуется вам, скажите ему, что интерфейсы Java заставляют вас копировать код в разные классы, которые реализуют один и тот же интерфейс одинаковым образом, и ваше руководство знает, насколько сложно будет поддерживать этот код. Следуйте примеру разработчиков Java AWT — наполните свои классы богатой функциональностью, которую смогут использовать только унаследованные классы, и выполняйте частые проверки своих методов с помощью оператора экземпляра.Получается, что любому, кто захочет использовать ваш код несколько раз, придется расширить ваши классы.
Если кто-то захочет использовать ваш код из двух разных классов, то ему не повезло — он не сможет расширить оба класса одновременно.
40. Определите все ваши классы, у которых нет дочерних элементов, используя ключевое слово Final. Ведь свою работу над проектом вы выполнили, и никто не сможет улучшить его, расширив ваши классы.Более того, это может вызвать проблемы с безопасностью.
Вот почему класс java.lang.String объявлен финальным.
Если другие разработчики вашего проекта начнут жаловаться, скажите им, что вы работаете над улучшением производительности программы.
41. Объявите как можно больше переменных как статические.Если вам в этой программе достаточно одного экземпляра класса, то и всем остальным хватит. Если другие разработчики вашего проекта начнут жаловаться, также сообщите им, что вы решаете проблему с производительностью.
42. Сохраняйте в своем коде все неиспользуемые или устаревшие методы и переменные.Если в них была необходимость однажды в 1976 году, что, если они были нужны и на этот раз? Конечно, с тех пор программа сильно изменилась, но все эти изменения можно легко обратить вспять: велосипед не «изобретаешь» (любимая поговорка менеджеров).
Если вы оставите все эти методы и переменные нетронутыми и не совсем понятными в комментариях, то все, кто будет поддерживать ваш код, тоже будут бояться что-либо менять.
43. Добавьте один комментарий к методу makeSnafucated: /* make snafucated */.Нигде не уточняйте, что на самом деле означает слово snafucated. Пришло время всем знать такие элементарные вещи.
44. Измените порядок параметров в методе drawRectangle(height, width) на drawRectangle(width, height), не меняя имя самого метода.Затем, после нескольких релизов, измените этот порядок обратно.
С первого раза очень сложно угадать, какая опция используется в программе.
Оставим задачу разобраться в том, как работает метод, нашим коллегам.
45. Вместо передачи параметров одному методу создайте как можно больше отдельных методов.Например, в методе setAlignment(int выравнивание) переменная выравнивания представляет собой константу, определяющую выравнивание по левому, правому краю и центру.
Вместо этого одного метода вы можете создать три: setLeftAlignment, setRightAlignment и setCenterAlignment. Для еще большего эффекта можно скопировать общий код в каждый из методов, чтобы их было сложнее координировать между собой.
46. Метод Камасутры имеет особое преимущество: он позволяет отвлечь внимание как пользователей и авторов документации, так и сопровождающих программного обеспечения.Создайте множество перегруженных вариантов одного и того же метода, отличающихся друг от друга мелкими деталями.
Кажется, Оскар Уайльд как-то заметил, что в Камасутре позы 47 и 115 отличаются только тем, что в позе 115 женщина скрестила пальцы.
Пользователям приходится тщательно просматривать длинный список методов, чтобы выбрать наиболее подходящий вариант. Кроме того, этот метод увеличивает объем документации и, как следствие, вероятность ее устаревания.
Если ваш начальник спросит, зачем вы это делаете, объясните, что вы делаете это исключительно для удобства пользователей.
Для еще большего эффекта, как и в других случаях, можно скопировать общую часть кода в каждый из методов.
47. Объявите все методы и переменные общедоступными.Рано или поздно это кому-то понадобится.
Если метод публичный, от него будет сложно избавиться: это затруднит внесение корректировок в работу программы.
Кроме того, такой подход позволяет скрыть настоящую цель занятия.
Если ваш начальник начнет к вам придираться, скажите ему, что вы всего лишь следуете традиционным принципам проектирования «прозрачных» интерфейсов.
48. Перегрузите функции из библиотеки C++ с помощью директивы #define. В этом случае будет выглядеть так, будто вы используете функцию, хранящуюся в библиотеке, хотя на самом деле она не имеет к этому никакого отношения.
49. В C++ вы также можете перегрузить операторы «+», «-», «*», «/» для выполнения операций, совершенно отличных от сложения, вычитания, умножения и деления.Если бы Страуструп подумал об использовании оператора сдвига для ввода-вывода, не могли бы вы придумать что-то подобное? При перегрузке оператора «+» убедитесь, что выражение i = i + 5; не имеет ничего общего с выражением i += 5;
50. При написании документации для файла лучше всего выбирать случайное имя, например file, а не очевидные имена, такие как Charlie.dat или Frodo.txt. Вообще говоря, старайтесь выбирать в своих примерах случайные имена, максимально похожие на зарезервированные ключевые слова.Например, для имени параметра или переменной отлично подойдут такие параметры, как банк, пустой, класс, константа, константа, вход, ключ, ключевое слово, вид, выход, параметр, параметр, система, тип, значение, var и переменная.
.
Если вы используете в именах зарезервированные слова, которые не принимает командный процессор или компилятор, это даже лучше.
Если вам это удастся, пользователи просто будут сбиты с толку выбранными вами ключевыми словами и именами, и вы не будете иметь к этому никакого отношения, потому что вы всего лишь хотели помочь им связать назначение каждой переменной с ее именем.
51. Всегда используйте определенный синтаксис команд, определенный вашей собственной версией BNF (форма Бэкуса-Наура).Никогда не объясняйте смысл вашего синтаксиса, представленного в виде набора правильных и невалидных команд: это будет свидетельствовать о низком уровне вашей компетентности.
То же самое относится и к использованию синтаксических диаграмм.
Убедитесь, что разницу между конечными символами (теми, которые можно набирать) и промежуточными символами (которые представляют собой одну из синтаксических фраз) увидеть не так-то просто.
Избегайте использования визуальных подсказок, таких как шрифт, цвет или использование заглавных букв, которые могут помочь рецензенту определить разницу.
В своем BNF используйте те же знаки препинания, что и в командном языке: те, кто попытается понять ваш код, будут долго думать, есть ли символы (.
), [.
], {.
} и " .
.
» часть вводимой вами команды, или их цель — показать, какие элементы вашего синтаксиса BNF являются обязательными, какие необязательными, а какие просто повторяются.
Ведь если они настолько глупы, что не могут понять суть вашего БНФ, то продолжать работать с вашей программой им просто нет смысла.
52. Препроцессор макросов предлагает множество возможностей запутать тех, кто читает ваш код. Основная хитрость заключается в том, чтобы поместить макрос на несколько уровней вглубь кода, чтобы другим приходилось искать различные его компоненты в разных *.hpp файлах.
Если вы поместите исполняемый код в макросы, а затем поместите эти макросы в каждый из файлов *.
cpp (даже в файлы, которые не используют макросы), вы максимизируете количество перекомпиляций, если в код будут внесены изменения.
53. Массивы можно объявлять в Java по-разному.Вы можете объявить массив старым способом, как в языках C, как String x[] (круглые скобки идут после имени переменной) или новым способом как String[] x (круглые скобки идут после типа данных).
Если вы хотите полностью запутать других, вы можете смешать оба метода.
54. Java позволяет запутать других во время преобразования типов переменных.Простой пример: можно, конечно, преобразовать double в String напрямую через Double.toString(d), но лучше сделать это по-другому, написав новый Double(d).
toString. Если хотите, можете придумать что-то более изысканное.
Однако не используйте методы преобразования, рекомендованные в руководстве по ручному преобразованию.
Чем больше временных объектов заполнит кучу после преобразования, тем лучше.
55. Используйте темы как можно чаще.
P.S. Больше материалов на тему стартапов в наших блогах по адресу Гиктаймс И Мегамозг .Теги: #разработка #лучшие практики #разработка сайтов #программирование #разработка игр
-
Как Лучше Всего Сравнивать Цены В Интернете?
19 Oct, 24 -
Теория Жидкостей
19 Oct, 24 -
Как Написать Статью, Используя Ux И Gtd
19 Oct, 24 -
1С:битрикс, Защита Свободных Форм От Спама
19 Oct, 24 -
Домашняя Страница Дональда Кнута
19 Oct, 24