Модульность и именование
Осторожно: В К У С О В Щ И Н А
Я хочу чтобы люди именовали вещи хорошо. Попробую кратко написать как это делать, вместо того чтобы заставлять читать кучу книг.
Для начала поговорим о модульности. Это про декомпозицию программы. Есть несколько уровней модульности, они не обязательны. Возьмём к примеру python.
- Репозиторий
- Python-пакет
- Файл с исходным кодом
- Класс
- Функция
- Переменная
Что у них есть общего? У всего есть название. Каждый вышестоящий уровень модульности можно считать контекстом. Так вот, сущность уже в контексте, не нужно тащить контекст в её название.
Думаю, нужен пример:
# example/foo.py
class Foo(object):
def __init__(self):
self.counter = 0
def increase(self):
self.counter += 1
def decrease(self):
self.counter -= 1
- Есть python-пакет example. Внутри кода не упоминается то, что это example. Единственное что оно нам говорит — то, что это пакет с какими-то примерами.
- Есть файл с исходным кодом foo.py. Пусть класс внутри и называется точно так же — ничего страшного. Странно было бы если файл назывался foo.py, а единственный класс внутри — Math или FooMath. Сложно объяснить почему, но на мой взгляд у названия файла либо должен быть смысл, либо, если он содержит только одну сущность, оно должно совпадать с названием этой сущности.
- Есть класс Foo. Ну, Foo и Foo, просто бессмысленный набор символов. В реальных примерах смысл должен быть, само собой.
- Функции increase и decrease явно говорят что они увеличивают и уменьшают что-то. Никаких foo в названии нет, это просто глагол, то есть действие, которое мы производим. При вызове foo.increase() очевидно, что действие производится над foo, т.к. никаких других аргументов нет. foo.foo_decrease звучит дико, но это бросается в глаза потому что foo ничего не значит. Как только у foo появляется значение, людям почему-то хочется скорее запихать это значение везде.
- Переменная counter тоже самодостаточна в названии. Она не привязана ни к example, ни к foo, ни к increase, ни к decrease. Её название описывает предназначение объекта.
А как иначе-то вообще?
Казалось бы, а по другому-то и нельзя. Когда эти “правила” нарушаются?
Иногда у сущностей нет возможности быть разграниченными уровнями модульности. Например, все переменные только глобальные. Или все функции не могут быть сгруппированы по нэймспэйсам того или иногда рода (хранимые процедуры в firebird по крайней мере этим страдали). Тогда может быть использован костыль — префикс в названии.
Какие ещё проблемы есть
Ещё одна проблема — при пробрасывании сущности по цепочке функций, языков, api и т.д. её многократно переименовывают.
Мотивация за — соответствие стилю кода этого проекта, удобнее набирать.
К каким проблемам приводит — теряется прозрачность. Прозрачность даёт grep’абельность между несколькими уровнями, а код остаётся менее запутанным. Думаю нужен пример.
Допустим, мы хотим хранить статистику по сетевым интерфейсам и рисовать графики в веб-интерфейсе. Есть база данных “metrics.sqlite”, в ней есть таблица “network”, в которой есть поля:
- interface (eth0, eth1)
- direction (rx, tx)
- metric (packets, bytes)
- value (1,2,3,4,5)
Есть код в python, который типа API, который делает SQL-запрос и отдаёт набор средних значений прироста счётчиков за минуту в JSON. Здесь легко поддаться соблазну и что-нибудь переименовать, например вернуть:
{
device: eth0,
metric: packets,
direction: rx,
values: [4,5,2,4,7],
}
Если задуматься, то дело не столько в том, что что-то переименовали и оно стало отличаться, сколько в том, что результат запроса вообще трогали, хотя можно было бы вернуть как есть и написать меньше кода. Но дело даже не в времени потраченном на кодинг. Здесь повторно потратили время на именование. Решение по именованию сущности должно приниматься один раз. Если решение кажется плохим - добейтесь его переименования в точке оригинала. Если вам не удаётся этого сделать - смиритесь.
Запахи плохого кода
- Вы кодируете в названии сущности более высокий уровень абстракции.
- Вы переименовываете переменные или ключи ассоциативных массивов, которые к вам пришли извне.