edit
This commit is contained in:
parent
e78e527866
commit
05a93ced80
212 changed files with 3213 additions and 3968 deletions
|
@ -1,35 +1,33 @@
|
|||
# Как писать неподдерживаемый код?
|
||||
|
||||
[warn header="Познай свой код"]
|
||||
```warn header="Познай свой код"
|
||||
Эта статья представляет собой мой вольный перевод [How To Write Unmaintainable Code](http://mindprod.com/jgloss/unmain.html) ("как писать неподдерживаемый код") с дополнениями, актуальными для JavaScript.
|
||||
|
||||
Возможно, в каких-то из этих советов вам даже удастся узнать "этого парня в зеркале".
|
||||
[/warn]
|
||||
|
||||
```
|
||||
|
||||
Предлагаю вашему вниманию советы мастеров древности, следование которым создаст дополнительные рабочие места для JavaScript-разработчиков.
|
||||
|
||||
|
||||
Если вы будете им следовать, то ваш код будет так сложен в поддержке, что у JavaScript'еров, которые придут после вас, даже простейшее изменение займет годы *оплачиваемого* труда! А сложные задачи оплачиваются хорошо, так что они, определённо, скажут вам "Спасибо".
|
||||
|
||||
|
||||
Более того, *внимательно* следуя этим правилам, вы сохраните и своё рабочее место, так как все будут бояться вашего кода и бежать от него...
|
||||
|
||||
...Впрочем, всему своя мера. При написании такого кода он не должен *выглядеть* сложным в поддержке, код должен *быть* таковым.
|
||||
|
||||
...Впрочем, всему своя мера. При написании такого кода он не должен *выглядеть* сложным в поддержке, код должен *быть* таковым.
|
||||
|
||||
Явно кривой код может написать любой дурак. Это заметят, и вас уволят, а код будет переписан с нуля. Вы не можете такого допустить. Эти советы учитывают такую возможность. Да здравствует дзен.
|
||||
|
||||
|
||||
|
||||
[cut]
|
||||
|
||||
## Соглашения -- по настроению
|
||||
|
||||
[quote author="Сериал \"Симпсоны\", серия Helter Shelter"]
|
||||
```quote author="Сериал \"Симпсоны\", серия Helter Shelter"
|
||||
Рабочий-чистильщик осматривает дом:<br>
|
||||
"...Вот только жук у вас необычный...<br>
|
||||
И чтобы с ним справиться, я должен жить как жук, стать жуком, думать как жук."<br>
|
||||
(грызёт стол Симпсонов)
|
||||
[/quote]
|
||||
```
|
||||
|
||||
Чтобы помешать другому программисту исправить ваш код, вы должны понять путь его мыслей.
|
||||
Чтобы помешать другому программисту исправить ваш код, вы должны понять путь его мыслей.
|
||||
|
||||
Представьте, перед ним -- ваш большой скрипт. И ему нужно поправить его. У него нет ни времени ни желания, чтобы читать его целиком, а тем более -- досконально разбирать. Он хотел бы по-быстрому найти нужное место, сделать изменение и убраться восвояси без появления побочных эффектов.
|
||||
|
||||
|
@ -39,15 +37,16 @@
|
|||
|
||||
Как затруднить задачу? Можно везде нарушать соглашения -- это помешает ему, но такое могут заметить, и код будет переписан. Как поступил бы ниндзя на вашем месте?
|
||||
|
||||
**...Правильно! Следуйте соглашениям "в общем", но иногда -- нарушайте их.**
|
||||
**...Правильно! Следуйте соглашениям "в общем", но иногда -- нарушайте их.**
|
||||
|
||||
Тщательно разбросанные по коду нарушения соглашений с одной стороны не делают код явно плохим при первом взгляде, а с другой -- имеют в точности тот же, и даже лучший эффект, чем явное неследование им!
|
||||
Тщательно разбросанные по коду нарушения соглашений с одной стороны не делают код явно плохим при первом взгляде, а с другой -- имеют в точности тот же, и даже лучший эффект, чем явное неследование им!
|
||||
|
||||
### Пример из jQuery
|
||||
|
||||
[warn header="jQuery / DOM"]
|
||||
```warn header="jQuery / DOM"
|
||||
Этот пример требует знаний jQuery/DOM, если пока их у вас нет -- пропустите его, ничего страшного, но обязательно вернитесь к нему позже. Подобное стоит многих часов отладки.
|
||||
[/warn]
|
||||
```
|
||||
|
||||
Во фреймворке jQuery есть метод [wrap](http://api.jquery.com/wrap/), который обёртывает один элемент вокруг другого:
|
||||
|
||||
```js
|
||||
|
@ -74,11 +73,11 @@ div.append('<span/>');
|
|||
|
||||
Как правило, методы jQuery работают с теми элементами, которые им переданы. Но не здесь!
|
||||
|
||||
Внутри вызова `img.wrap(div)` происходит клонирование `div` и вокруг `img` оборачивается не сам `div`, а его клон. При этом исходная переменная `div` не меняется, в ней как был пустой `div`, так и остался.
|
||||
Внутри вызова `img.wrap(div)` происходит клонирование `div` и вокруг `img` оборачивается не сам `div`, а его клон. При этом исходная переменная `div` не меняется, в ней как был пустой `div`, так и остался.
|
||||
|
||||
В итоге, после вызова получается два независимых `div'а`: первый содержит `img` (этот неявный клон никуда не присвоен), а второй -- наш `span`.
|
||||
|
||||
Объяснения не очень понятны? Написано что-то странное? Это просто разум, привыкший, что соглашения уважаются, не допускает мысли, что вызов `wrap` -- неявно клонирует элемент. Ведь другие jQuery-методы, кроме `clone` этого не делают.
|
||||
Объяснения не очень понятны? Написано что-то странное? Это просто разум, привыкший, что соглашения уважаются, не допускает мысли, что вызов `wrap` -- неявно клонирует элемент. Ведь другие jQuery-методы, кроме `clone` этого не делают.
|
||||
|
||||
Как говорил [Учитель](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D1%84%D1%83%D1%86%D0%B8%D0%B9): "В древности люди учились для того, чтобы совершенствовать себя. Нынче учатся для того, чтобы удивить других".
|
||||
|
||||
|
@ -99,13 +98,13 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
|
|||
|
||||
## Именование
|
||||
|
||||
Существенную часть науки о создании неподдерживаемого кода занимает искусство выбора имён.
|
||||
Существенную часть науки о создании неподдерживаемого кода занимает искусство выбора имён.
|
||||
|
||||
### Однобуквенные переменные
|
||||
|
||||
Называйте переменные коротко: `a`, `b` или `c`.
|
||||
|
||||
В этом случае никто не сможет найти её, используя фунцию "Поиск" текстового редактора.
|
||||
В этом случае никто не сможет найти её, используя фунцию "Поиск" текстового редактора.
|
||||
|
||||
Более того, даже найдя -- никто не сможет "расшифровать" её и догадаться, что она означает.
|
||||
|
||||
|
@ -113,73 +112,73 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
|
|||
|
||||
В тех местах, где однобуквенные переменные общеприняты, например, в счетчике цикла -- ни в коем случае не используйте стандартные названия `i`, `j`, `k`. Где угодно, только не здесь!
|
||||
|
||||
Остановите свой взыскательный взгляд на чём-нибудь более экзотическом. Например, `x` или `y`.
|
||||
Остановите свой взыскательный взгляд на чём-нибудь более экзотическом. Например, `x` или `y`.
|
||||
|
||||
Эффективность этого подхода особенно заметна, если тело цикла занимает одну-две страницы (чем длиннее -- тем лучше).
|
||||
Эффективность этого подхода особенно заметна, если тело цикла занимает одну-две страницы (чем длиннее -- тем лучше).
|
||||
|
||||
В этом случае заметить, что переменная -- счетчик цикла, без пролистывания вверх, невозможно.
|
||||
В этом случае заметить, что переменная -- счетчик цикла, без пролистывания вверх, невозможно.
|
||||
|
||||
### Русские слова и сокращения
|
||||
|
||||
Если вам *приходится* использовать длинные, понятные имена переменных -- что поделать.. Но и здесь есть простор для творчества!
|
||||
|
||||
**Назовите переменные "калькой" с русского языка или как-то "улучшите" английское слово.**
|
||||
**Назовите переменные "калькой" с русского языка или как-то "улучшите" английское слово.**
|
||||
|
||||
В одном месте напишите `var ssilka`, в другом `var ssylka`, в третьем `var link`, в четвёртом -- `var lnk`... Это действительно великолепно работает и очень креативно!
|
||||
В одном месте напишите `var ssilka`, в другом `var ssylka`, в третьем `var link`, в четвёртом -- `var lnk`... Это действительно великолепно работает и очень креативно!
|
||||
|
||||
Количество ошибок при поддержке такого кода увеличивается во много раз.
|
||||
|
||||
### Будьте абстрактны при выборе имени
|
||||
|
||||
[quote author="Лао-цзы"]Лучший кувшин лепят всю жизнь.<br>
|
||||
```quote author="Лао-цзы"
|
||||
Лучший кувшин лепят всю жизнь.<br>
|
||||
Высокая музыка неподвластна слуху.<br>
|
||||
Великий образ не имеет формы.[/quote]
|
||||
Великий образ не имеет формы.
|
||||
```
|
||||
|
||||
При выборе имени старайтесь применить максимально абстрактное слово, например `obj`, `data`, `value`, `item`, `elem` и т.п.
|
||||
При выборе имени старайтесь применить максимально абстрактное слово, например `obj`, `data`, `value`, `item`, `elem` и т.п.
|
||||
|
||||
<ul>
|
||||
<li>**Идеальное имя для переменной: `data`.** Используйте это имя везде, где можно. В конце концов, каждая переменная содержит *данные*, не правда ли?
|
||||
- **Идеальное имя для переменной: `data`.** Используйте это имя везде, где можно. В конце концов, каждая переменная содержит *данные*, не правда ли?
|
||||
|
||||
Но что делать, если имя `data` уже занято? Попробуйте `value`, оно не менее универсально. Ведь каждая переменная содержит *значение*.
|
||||
Но что делать, если имя `data` уже занято? Попробуйте `value`, оно не менее универсально. Ведь каждая переменная содержит *значение*.
|
||||
|
||||
Занято и это? Есть и другой вариант.
|
||||
</li>
|
||||
<li>**Называйте переменную по типу данных, которые она хранит: `obj`, `num`, `arr`...**
|
||||
Занято и это? Есть и другой вариант.
|
||||
- **Называйте переменную по типу данных, которые она хранит: `obj`, `num`, `arr`...**
|
||||
|
||||
Насколько это усложнит разработку? Как ни странно, намного!
|
||||
Насколько это усложнит разработку? Как ни странно, намного!
|
||||
|
||||
Казалось бы, название переменной содержит информацию, говорит о том, что в переменной -- число, объект или массив... С другой стороны, **когда непосвящённый будет разбирать этот код -- он с удивлением обнаружит, что информации нет!**
|
||||
Казалось бы, название переменной содержит информацию, говорит о том, что в переменной -- число, объект или массив... С другой стороны, **когда непосвящённый будет разбирать этот код -- он с удивлением обнаружит, что информации нет!**
|
||||
|
||||
Ведь как раз тип легко понять, запустив отладчик и посмотрев, что внутри. Но в чём смысл этой переменной? Что за массив/объект/число в ней хранится? Без долгой медитации над кодом тут не обойтись!
|
||||
</li>
|
||||
<li>**Что делать, если и эти имена кончились? Просто добавьте цифру:** `item1, item2, elem5, data1`...</li>
|
||||
</ul>
|
||||
Ведь как раз тип легко понять, запустив отладчик и посмотрев, что внутри. Но в чём смысл этой переменной? Что за массив/объект/число в ней хранится? Без долгой медитации над кодом тут не обойтись!
|
||||
- **Что делать, если и эти имена кончились? Просто добавьте цифру:** `item1, item2, elem5, data1`...
|
||||
|
||||
### Похожие имена
|
||||
|
||||
Только истинно внимательный программист достоин понять ваш код. Но как проверить, достоин ли читающий?
|
||||
Только истинно внимательный программист достоин понять ваш код. Но как проверить, достоин ли читающий?
|
||||
|
||||
**Один из способов -- использовать похожие имена переменных, например `data` и `date`.** Бегло прочитать такой код почти невозможно. А уж заметить опечатку и поправить её... Ммммм... Мы здесь надолго, время попить чайку.
|
||||
|
||||
### А.К.Р.О.Н.И.М
|
||||
|
||||
Используйте сокращения, чтобы сделать код короче.
|
||||
Используйте сокращения, чтобы сделать код короче.
|
||||
|
||||
Например `ie` (Inner Element), `mc` (Money Counter) и другие. Если вы обнаружите, что путаетесь в них сами -- героически страдайте, но не переписывайте код. Вы знали, на что шли.
|
||||
|
||||
### Хитрые синонимы
|
||||
|
||||
[quote author="Конфуций"]Очень трудно найти чёрную кошку в тёмной комнате, особенно когда её там нет.[/quote]
|
||||
```quote author="Конфуций"
|
||||
Очень трудно найти чёрную кошку в тёмной комнате, особенно когда её там нет.
|
||||
```
|
||||
|
||||
**Чтобы было не скучно -- используйте *похожие названия* для обозначения *одинаковых действий*.**
|
||||
**Чтобы было не скучно -- используйте *похожие названия* для обозначения *одинаковых действий*.**
|
||||
|
||||
Например, если метод показывает что-то на экране -- начните его название с `display..` (скажем, `displayElement`), а в другом месте объявите аналогичный метод как `show..` (`showFrame`).
|
||||
Например, если метод показывает что-то на экране -- начните его название с `display..` (скажем, `displayElement`), а в другом месте объявите аналогичный метод как `show..` (`showFrame`).
|
||||
|
||||
**Как бы намекните этим, что существует тонкое различие между способами показа в этих методах, хотя на самом деле его нет.**
|
||||
**Как бы намекните этим, что существует тонкое различие между способами показа в этих методах, хотя на самом деле его нет.**
|
||||
|
||||
По возможности, договоритесь с членами своей команды. Если Вася в своих классах использует `display..`, то Валера -- обязательно `render..`, а Петя -- `paint..`.
|
||||
По возможности, договоритесь с членами своей команды. Если Вася в своих классах использует `display..`, то Валера -- обязательно `render..`, а Петя -- `paint..`.
|
||||
|
||||
**...И напротив, если есть две функции с важными отличиями -- используйте одно и то же слово для их описания!** Например, с `print...` можно начать метод печати на принтере `printPage`, а также -- метод добавления текста на страницу `printText`.
|
||||
**...И напротив, если есть две функции с важными отличиями -- используйте одно и то же слово для их описания!** Например, с `print...` можно начать метод печати на принтере `printPage`, а также -- метод добавления текста на страницу `printText`.
|
||||
|
||||
А теперь, пусть читающий код думает: "Куда же выводит сообщение `printMessage`?". Особый шик -- добавить элемент неожиданности. Пусть `printMessage` выводит не туда, куда все, а в новое окно!
|
||||
|
||||
|
@ -187,15 +186,15 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
|
|||
|
||||
Ни в коем случае не поддавайтесь требованиям написать словарь терминов для проекта. Если же он уже есть -- не следуйте ему, а лучше проглотите и скажите, что так и былО!
|
||||
|
||||
Пусть читающий ваш код программист напрасно ищет различия в `helloUser` и `welcomeVisitor` и пытается понять, когда что использовать. Вы-то знаете, что на самом деле различий нет, но искать их можно о-очень долго.
|
||||
Пусть читающий ваш код программист напрасно ищет различия в `helloUser` и `welcomeVisitor` и пытается понять, когда что использовать. Вы-то знаете, что на самом деле различий нет, но искать их можно о-очень долго.
|
||||
|
||||
**Для обозначения посетителя в одном месте используйте `user`, а в другом `visitor`, в третьем -- просто `u`. Выбирайте одно имя или другое, в зависимости от функции и настроения.**
|
||||
|
||||
Это воплотит сразу два ключевых принципа ниндзя-дизайна -- *сокрытие информации* и *подмена понятий*!
|
||||
Это воплотит сразу два ключевых принципа ниндзя-дизайна -- *сокрытие информации* и *подмена понятий*!
|
||||
|
||||
### Повторно используйте имена
|
||||
|
||||
По возможности, повторно используйте имена переменных, функций и свойств. Просто записывайте в них новые значения.
|
||||
По возможности, повторно используйте имена переменных, функций и свойств. Просто записывайте в них новые значения.
|
||||
|
||||
Добавляйте новое имя только если это абсолютно необходимо.
|
||||
|
||||
|
@ -217,7 +216,7 @@ function ninjaFunction(elem) {
|
|||
}
|
||||
```
|
||||
|
||||
Программист, пожелавший добавить действия с `elem` во вторую часть функции, будет удивлён. Лишь во время отладки, посмотрев весь код, он с удивлением обнаружит, что оказывается имел дело с клоном!
|
||||
Программист, пожелавший добавить действия с `elem` во вторую часть функции, будет удивлён. Лишь во время отладки, посмотрев весь код, он с удивлением обнаружит, что оказывается имел дело с клоном!
|
||||
|
||||
Регулярные встречи с этим приемом на практике говорят: защититься невозможно. Эффективно даже против опытного ниндзи.
|
||||
|
||||
|
@ -231,16 +230,16 @@ function ninjaFunction(elem) {
|
|||
|
||||
### Покажите вашу любовь к разработке
|
||||
|
||||
Пусть все видят, какими замечательными сущностями вы оперируете! Имена `superElement`, `megaFrame` и `niceItem` при благоприятном положении звёзд могут привести к просветлению читающего.
|
||||
Пусть все видят, какими замечательными сущностями вы оперируете! Имена `superElement`, `megaFrame` и `niceItem` при благоприятном положении звёзд могут привести к просветлению читающего.
|
||||
|
||||
Действительно, с одной стороны, кое-что написано: `super..`, `mega..`, `nice..` С другой -- это не несёт никакой конкретики. Читающий может решить поискать в этом глубинный смысл и замедитировать на часок-другой оплаченного рабочего времени.
|
||||
|
||||
### Перекрывайте внешние переменные
|
||||
|
||||
[quote author="Гуань Инь-цзы"]
|
||||
```quote author="Гуань Инь-цзы"
|
||||
Находясь на свету, нельзя ничего увидеть в темноте.<br>
|
||||
Пребывая же в темноте, увидишь все, что находится на свету.
|
||||
[/quote]
|
||||
```
|
||||
|
||||
Почему бы не использовать одинаковые переменные внутри и снаружи функции? Это просто и не требует придумывать новых имён.
|
||||
|
||||
|
@ -261,11 +260,11 @@ function render() {
|
|||
|
||||
## Мощные функции!
|
||||
|
||||
Не ограничивайте действия функции тем, что написано в её названии. Будьте шире.
|
||||
Не ограничивайте действия функции тем, что написано в её названии. Будьте шире.
|
||||
|
||||
Например, функция `validateEmail(email)` может, кроме проверки e-mail на правильность, выводить сообщение об ошибке и просить заново ввести e-mail.
|
||||
|
||||
**Выберите хотя бы пару дополнительных действий, кроме основного назначения функции.**
|
||||
**Выберите хотя бы пару дополнительных действий, кроме основного назначения функции.**
|
||||
|
||||
Главное -- они должны быть неочевидны из названия функции. Истинный ниндзя-девелопер сделает так, что они будут неочевидны и из кода тоже.</li>
|
||||
|
||||
|
@ -273,12 +272,11 @@ function render() {
|
|||
|
||||
Представьте, что другому разработчику нужно только проверить адрес, а сообщение -- не выводить. Ваша функция `validateEmail(email)`, которая делает и то и другое, ему не подойдёт. Работодатель будет вынужден оплатить создание новой.
|
||||
|
||||
|
||||
## Внимание.. Сюр-при-из!
|
||||
|
||||
Есть функции, название которых говорит о том, что они ничего не меняют. Например, `isReady`, `checkPermission`, `findTags`... Предполагается, что при вызове они произведут некие вычисления, или найдут и возвратят полезные данные, но при этом их не изменят. В трактатах это называется "отсутствие сторонних эффектов".
|
||||
Есть функции, название которых говорит о том, что они ничего не меняют. Например, `isReady`, `checkPermission`, `findTags`... Предполагается, что при вызове они произведут некие вычисления, или найдут и возвратят полезные данные, но при этом их не изменят. В трактатах это называется "отсутствие сторонних эффектов".
|
||||
|
||||
**По-настоящему красивый приём -- делать в таких функциях что-нибудь полезное, заодно с процессом проверки. Что именно -- совершенно неважно.**
|
||||
**По-настоящему красивый приём -- делать в таких функциях что-нибудь полезное, заодно с процессом проверки. Что именно -- совершенно неважно.**
|
||||
|
||||
Удивление и ошеломление, которое возникнет у вашего коллеги, когда он увидит, что функция с названием на `is..`, `check..` или `find...` что-то меняет -- несомненно, расширит его границы разумного!
|
||||
|
||||
|
@ -290,12 +288,10 @@ function render() {
|
|||
|
||||
## Заключение
|
||||
|
||||
Все советы выше пришли из реального кода... И в том числе от разработчиков с большим опытом.
|
||||
Все советы выше пришли из реального кода... И в том числе от разработчиков с большим опытом.
|
||||
|
||||
Возможно, даже больше вашего, так что не судите опрометчиво ;)
|
||||
|
||||
<ul>
|
||||
<li>Следуйте нескольким из них -- и ваш код станет полон сюрпризов.</li>
|
||||
<li>Следуйте многим -- и ваш код станет истинно вашим, никто не захочет изменять его.</li>
|
||||
<li>Следуйте всем -- и ваш код станет ценным уроком для молодых разработчиков, ищущих просветления.</li>
|
||||
</ul>
|
||||
- Следуйте нескольким из них -- и ваш код станет полон сюрпризов.
|
||||
- Следуйте многим -- и ваш код станет истинно вашим, никто не захочет изменять его.
|
||||
- Следуйте всем -- и ваш код станет ценным уроком для молодых разработчиков, ищущих просветления.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue