Случалось ли вам писать массивный вложенный оператор if и думать: 🤮? Если да, то, возможно, этот пост для вас.
Сначала поговорим о проблеме. При разработке систем нередко возникает необходимость реализовать некоторую бизнес-логику, которую можно абстрактно представить в виде блок-схемы или дерева решений. Ниже приведен пример довольно простого дерева/схемы, который можно использовать в качестве примера. Конечно, вполне вероятно, что ваша собственная проблема может быть гораздо сложнее этой, но, надеюсь, она проиллюстрирует суть:
Это дерево решений представляет собой бизнес-логику, определяющую, какой тип апострофа следует использовать (или не использовать) в той или иной ситуации. Нередко эксперты по доменам иллюстрируют свою бизнес-логику с помощью дерева решений, подобного этому.
Деревья решений можно напрямую сопоставить с моим предпочтительным способом отображения такого типа информации — таблицей решений. В таблице решений мы берем узлы дерева вопросов и располагаем их в верхней строке. Затем мы создаем дополнительный столбец в правой части таблицы и заполняем его «действиями», которые мы должны предпринять, — это листовые узлы нашего дерева решений. Затем мы заполняем все возможные варианты ответов на наш вопрос, в нашем случае истинный (✔️) ложный (❌) или любой из них (🤷).
Таблицы решений (и деревья) являются составными, поэтому их можно вложить в более мелкие подтаблицы (или деревья). Вот три таблицы, которые представляют дерево решений выше.
Множественное число? | Одиночная буква? | Аббревиатура? | |
---|---|---|---|
❌ | 🤷 | 🤷 | См. таблицу поссессивов |
✔️ | ✔️ | 🤷 | Апостроф + s |
✔️ | ❌ | ✔️ | Апостроф + s |
✔️ | ❌ | ❌ | Без апострофа |
Таблица множественного числа
Обладающий? | Это «it»? | «оно есть» или «оно имеет»? | имя, оканчивающееся на s? | имя во множественном числе? | заканчивается на s? | |
---|---|---|---|---|---|---|
❌ | 🤷 | 🤷 | 🤷 | 🤷 | 🤷 | См. таблицу сокращений |
✔️ | ✔️ | ✔️ | 🤷 | 🤷 | 🤷 | Апостроф + s |
✔️ | ✔️ | ❌ | 🤷 | 🤷 | 🤷 | Без апострофа |
✔️ | ❌ | 🤷 | ✔️ | 🤷 | 🤷 | Апостроф в конце |
✔️ | ❌ | 🤷 | ❌ | ✔️ | 🤷 | Апостроф в конце |
✔️ | ❌ | 🤷 | ❌ | ❌ | ✔️ | Апостроф в конце |
✔️ | ❌ | 🤷 | ❌ | ❌ | ❌ | Апостроф + s |
Таблица поссессивов
Сокращение? | С презенсом? | с глаголом? | |
---|---|---|---|
❌ | 🤷 | 🤷 | Без апострофа |
✔️ | ✔️ | ✔️ | Использовать апостроф |
✔️ | ❌ | 🤷 | Использовать апостроф |
✔️ | ✔️ | ❌ | Без апострофа |
Таблица сокращений
Если вы работаете с экспертом по доменам, который любит использовать деревья решений, попросите его перейти на использование таблиц.
Итак, у нас есть логика для нашего домена, теперь как нам реализовать ее в коде? Мы можем написать довольно большой вложенный оператор if. Или же, если нам дано дерево, почему бы не представить эту информацию в виде дерева? Мы можем создать древовидную структуру с узлами, ребрами и листьями и смоделировать ее напрямую.
Эти варианты могли бы работать, но есть пара проблем. Если наш эксперт по доменам вернется и скажет нам, что бизнес-логика изменилась, и предоставит новое дерево. Не так-то просто перевести новую логику в код с помощью любого из этих двух методов. Нет прямого сопоставления, и в целом бывает трудно взглянуть на большой вложенный оператор if (при условии, что он помещается на одном экране) и легко определить стоящую за ним логику.
Сопоставление шаблонов FTW!
Я предпочитаю использовать сопоставление шаблонов для решения этой проблемы. Мы можем взять нашу таблицу (таблицы) решений и напрямую сопоставить их с кодом таким образом, чтобы они выглядели и ощущались очень похожими на исходную информацию.
Возьмем приведенные выше таблицы и проиллюстрируем их в качестве примера. Я собираюсь использовать язык Rust, потому что в нем есть отличная система типов и встроенная функция сопоставления с образцом. Большинство языков сейчас поддерживают такой синтаксис (я не думаю, что JavaScript поддерживает… 🤨).
Форматирование пробельных символов я выбрал для того, чтобы проиллюстрировать, насколько тесно связаны эти два понятия, и облегчить чтение таблицы, хотя это личное предпочтение.
Если вы используете язык с хорошей системой типов и компилятором, то дополнительное преимущество использования сопоставления по образцу заключается в том, что вы получаете проверку вашей логики во время компиляции. В отличие от серии операторов if, вы будете предупреждены, если один из ваших шаблонов (строки таблицы) недостижим или если вы пропустили какие-либо случаи. В C# вы получите предупреждение компилятора с красивой подсказкой о том, какие случаи вы пропустили. Нередко в конце включается промежуточный случай, где вы можете решить, что делать — возможно, выдать ошибку.
Вот, собственно, и все! Дайте мне знать в комментариях, что вы думаете об этом патерне, использовали ли вы его раньше? Стали бы вы его использовать? Есть ли другие решения, которые вы нашли, которые работают лучше?