Программируйте на языке предметной области
(В оригинале - Code in the Language of the Domain)
Представьте себе два исходных кода. В первом вы видите что-то подобное:
if (portfolioIdsByTraderId.get(trader.getId()).containsKey(portfolio.getId())) {...}
Вы чешете затылок, пытаясь догадаться, что может делать этот код. Выглядит как взятие ID из объекта trader и использование его для получения другого ID, после чего – поиск еще одного ID из объекта portfolio… Почесывание затылка почему-то не помогает. Вы ищете определение portfolioIdsByTraderId и находите следующее:
Map<int, Map<int, int>> portfolioIdsByTraderId;
Постепенно вы выясняете, что этот код делает что-то похожее на выяснение того, имеет ли трейдер доступ к портфолио. И конечно же, этот (или похожий на него) фрагмент кода будет повторяться везде, где будет требоваться выяснить, есть ли у трейдера доступ к портфолио.
А в другом исходном коде вы находите вот такую строчку:
if (trader.canView(portfolio)) {...}
И никакого почесывания затылка не требуется. Вам не нужно знать подробности, что у этой функции внутри. Вероятнее всего, там будет подобное приведенному выше фрагменту, но это уже дело трейдера, а не ваше.
А теперь скажите, с каким кодом вы бы предпочли работать?
Когда-то давно были только простые структуры данных: биты, байты и символы (на самом деле те же байты, но нам больше нравилось считать их буквами, цифрами и знаками). С числами было уже чуть сложнее – наша десятичная система не очень хорошо «ложилась» на двоичную, и поэтому было несколько различных типов с плавающей запятой. Потом появились массивы и строки (на самом деле такие же массивы). Потом – стеки, очереди, хеши, связанные списки и другие замечательные структуры, не существующие в реальном мире. И «программируя», мы тратили значительную часть ресурсов на то, чтобы поставить в соответствие объекты из реального мира и эти достаточно ограниченные структуры данных. Настоящие же гуру могли даже запомнить то, как именно они это сделали.
Потом появились типы, определенные пользователем! Это серьезно изменило правила игры. Если в вашей области есть сущности «трейдер» и «портфолио», то вы можете смоделировать их при помощи типов данных с именами Trader и Portfolio. Но еще более важно то, что вы можете моделировать отношения между ними, тоже используя термины из предметной области.
Если вы программируете, не используя термины предметной области, то тем самым вы создаете тайное знание о том, что например вот этот int означает идентификатор трейдера, а вот этот – идентификатор портфолио. (И лучше их не перепутать!). А если вы представляете какую-либо концепцию («Некоторые трейдеры не могут просматривать отдельные портфолио») при помощи алгоритма, подобного поиску соответствия в таблице идентификаторов, то вы не даете тем, кому придется с этим разбираться, вообще никакой подсказки.
Программист, пришедший за вами, не будет иметь того тайного знания, которое имели вы. Так зачем делать его тайным? Использовать ключ для поиска другого ключа, используемого для проверки наличия соответствия – не самый распространенный прием. Каким образом кто-то должен догадаться о том, какое именно правило из предметной области тут скрыто?
Используя имена концепций из предметной области, вы даете возможность другим программистам разобраться в коде гораздо более простым способом, чем пытаться найти обратное отображение от ничего не говорящих алгоритмов к концепциям предметной области. Это также означает, что по мере изменения предметной области (а меняться она будет обязательно, это лишь вопрос времени) вам будет гораздо проще вносить соответствующие изменения в код. При хорошо построенной инкапсуляции шансы, что каждое правило реализовано лишь в одном месте, достаточно высоки, и тогда не придется вносить изменение в множество мест.
Программист, пришедший в ваш проект несколькими месяцами позже, будет очень рад такому подходу. И этим пришедшим через несколько месяцев программистом можете быть вы!
Автор оригинала - Dan North