diff --git a/app/config/sculpin_kernel.yml b/app/config/sculpin_kernel.yml index d749d51..2160184 100644 --- a/app/config/sculpin_kernel.yml +++ b/app/config/sculpin_kernel.yml @@ -11,6 +11,7 @@ sculpin_content_types: singular_name: album layout: internal permalink: albums/:basename/ + publish_drafts: false enabled: true taxonomies: - tags @@ -20,6 +21,7 @@ sculpin_content_types: singular_name: article layout: article permalink: articles/:year-:month-:day-:basename/ + publish_drafts: false enabled: true taxonomies: - tags diff --git a/source/_articles/2020-06-27-type-discriminant.md b/source/_articles/2020-06-27-type-discriminant.md new file mode 100644 index 0000000..c36b351 --- /dev/null +++ b/source/_articles/2020-06-27-type-discriminant.md @@ -0,0 +1,122 @@ +--- +title: О полях-дискриминаторах +description: Заметка о том, как записывать конфигурацию для сложных объектов +keywords: [чистый код, дискриминатор, php, конфигурация] +--- + +Поля-дискриминаторы - это удобный прием для обработки нескольких типов +данных со схожей структурой. + +Лучше начать с примера. + +Допустим, у нас есть объект-фильтр для целых чисел. Можно применить фильтр +к последовательности чисел и получить новую последовательность. +Его параметры выглядят следующим образом: + +```json +{ + "from": 0, + "to": 10 +} +``` + +Отлично. +Фильтр пропустит только числа от 0 до 10. +По такой конфигурации без проблем можно создать объект. + +Теперь добавим второй фильтр - он будет отсекать нечетные числа. + +```json +{ + "odd": false +} +``` + +## Создаем фильтр на основе структуры + +Теперь есть два фильтра, и нужно понимать какой их них создать. +Простое и наивное решение - смотреть на структуру полей. +Если есть поле `odd`, то фильтр нечетных чисел, иначе - фильтр по диапазону. + +```php +if (isset($config['odd'])) { + // фильтр нечетных чисел +} else { + // фильтр диапазона +} +``` + +У этого решения масса недостатков. +Например, если поле `odd` понадобится двум фильтрам сразу, то условие усложнится. +Или появятся поля с разными названиями, но одинаковым смыслом. + +## Добавляем поле-дискриминатор + +Решение проблемы в добавление специального поля, в котором содержится имя фильтра. +Назовем это поле `type`. Тогда конфигурации фильтров будут выглядеть следующим образом: + +```json +{ + "type": "range", + "from": 0, + "to": 10 +} +``` + +```json +{ + "type": "odd_control", + "odd": false +} +``` + +Поле `type` - это и есть поле-дискриминатор. +По нему можно точно определить какой фильтр перед нами. + +```php +switch ($config['type']) { + case 'range': + // фильтр диапазона + break; + case 'odd_control': + // фильтр нечетных чисел + break; + default: + throw new \LogicException(sprintf( + 'Unknown filter type "%s"', $config['type'])); +} +``` + +## Выделить зависимые поля + +Решение еще можно улучшить. +Сейчас на одном уровне в структуре конфига есть и обязательные поля, и необязательные поля. +Мы можем перенести все необязательные поля на дополнительный уровень, +например, в поле `params`. +Эти поля необязательны для всей конфигурации, но обязательны для конкретного фильтра. + +```json +{ + "type": "range", + "params": { + "from": 0, + "to": 10 + } +} +``` + +Что дает такой маневр? +На верхнем уровне конфигурации у нас будет постоянная структура. +Поле `params` можно до определенного времени рассматривать как черный ящик, +просто зная, что это некий словарь параметров. +Когда будет понятно какой фильтр создавать, тогда этот набор параметров +послужит базой для создания объекта-фильтра. + +## Заключение + +- Если структура конфигурации предполагает создание нескольких разных объектов, + то лучше использовать специальное поле-дискриминатор для точного указания + типа создаваемого объекта. +- Все поля, которые зависят от типа, лучше перенести на дополнительный уровень, + тем самым сохранив структуру верхнего уровня постоянной (на верхнем уровне + будет всегда один и тот же набор полей). diff --git a/tools/mkpost b/tools/mkpost new file mode 100755 index 0000000..c9c3884 --- /dev/null +++ b/tools/mkpost @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +touch "source/_articles/$(date +'%Y-%m-%d')-$1.md"