ID: v8cs:right-start-web-client

- Категория: `right`

В случае если в конфигурации для пользователей необходимо настраивать общие права работы с информационной базой (такие как «Тонкий клиент», «Толстый клиент», «Интерактивное открытие внешних обработок» и т.д.), то в конфигурации должны быть определены отдельные роли для предоставления этих прав. Такие роли не предназначены для самостоятельного использования, их следует назначать пользователям совместно с остальными ролями конфигурации.

#### Стандарт

- [#std488: Стандартные роли](https://v8std.ru/std/488/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.right/markdown/ru/right-start-web-client.md

Стандартные роли #std488

Право установлено: Веб-клиент (right-start-web-client)


ID: v8cs:right-update-database-configuration

- Категория: `right`

В случае если в конфигурации для пользователей необходимо настраивать общие права работы с информационной базой (такие как «Тонкий клиент», «Толстый клиент», «Интерактивное открытие внешних обработок» и т.д.), то в конфигурации должны быть определены отдельные роли для предоставления этих прав. Такие роли не предназначены для самостоятельного использования, их следует назначать пользователям совместно с остальными ролями конфигурации.

#### Стандарт

- [#std488: Стандартные роли](https://v8std.ru/std/488/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.right/markdown/ru/right-update-database-configuration.md

В случае если в конфигурации для пользователей необходимо настраивать общие права работы с информационной базой (такие как «Тонкий клиент», «Толстый клиент», «Интерактивное...

Стандартные роли #std488

Право установлено: Обновление конфигурации базы данных (right-update-database-configuration)


ID: v8cs:right-view-event-log

- Категория: `right`

В случае если в конфигурации для пользователей необходимо настраивать общие права работы с информационной базой (такие как «Тонкий клиент», «Толстый клиент», «Интерактивное открытие внешних обработок» и т.д.), то в конфигурации должны быть определены отдельные роли для предоставления этих прав. Такие роли не предназначены для самостоятельного использования, их следует назначать пользователям совместно с остальными ролями конфигурации.

#### Стандарт

- [#std488: Стандартные роли](https://v8std.ru/std/488/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.right/markdown/ru/right-view-event-log.md

Стандартные роли #std488

Право установлено: Журнал регистрации (right-view-event-log)


ID: v8cs:role-right-has-rls

- Категория: `right`

#### Стандарт

- Нет прямой привязки к v8std в описании проверки

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.right/markdown/ru/role-right-has-rls.md

Право роли содержит текст ограничения доступа (RLS) (role-right-has-rls). Категория: right. Нет прямой привязки к v8std в описании проверки


ID: v8cs:rollback-transaction

- Категория: `bsl`

Вызов "ОтменитьТранзакцию()" находится вне конструкции "Попытка... Исключение" Отсутствует вызов "НачатьТранзакцию()", хотя вызываются "ОтменитьТранзакцию()" Для вызова "НачатьТранзакцию()" отсутствует парный вызов "ЗафиксироватьТранзакцию()" Mежду "Исключение" и "ОтменитьТранзакцию()" есть исполняемый код, который может вызвать исключение

#### Стандарт

- [#std499: Перехват исключений в коде](https://v8std.ru/std/499/)
- [#std783: Транзакции: правила использования](https://v8std.ru/std/783/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/rollback-transaction.md

Вызов "ОтменитьТранзакцию()" находится вне конструкции "Попытка... Исключение" Отсутствует вызов "НачатьТранзакцию()", хотя вызываются "ОтменитьТранзакцию()" Для вызова...

Перехват исключений в коде #std499

Транзакции: правила использования #std783

Проверка нарушения схемы работы с транзакциями (rollback-transaction)


ID: v8cs:scheduled-job-periodicity-too-short

- Категория: `md`

Периодичность выполнения регламентного задания меньше одной минуты.

#### Стандарт

- [#std402: Настройка расписания регламентных заданий](https://v8std.ru/std/402/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.md/markdown/ru/scheduled-job-periodicity-too-short.md

Настройка расписания регламентных заданий #std402

Периодичность выполнения регламентного задания меньше одной минуты. (scheduled-job-periodicity-too-short)


ID: v8cs:server-execution-safe-mode

- Категория: `bsl`

Для исключения описанных уязвимостей, нужно в серверных процедурах и функциях вызов методов Выполнить или Вычислить предварять включением безопасного режима. В режиме сервиса, включение безопасного режима должно учитывать разделение данных.

#### Стандарт

- [#std770: Ограничения на использование Выполнить и Вычислить на сервере](https://v8std.ru/std/770/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/server-execution-safe-mode.md

Для исключения описанных уязвимостей, нужно в серверных процедурах и функциях вызов методов Выполнить или Вычислить предварять включением безопасного режима. В режиме сервиса,...

Ограничения на использование Выполнить и Вычислить на сервере #std770

Отсутствует включение безопасного режима перед вызовом метода "Выполнить" или "Вычислить" (server-execution-safe-mode)


ID: v8cs:statement-type-change

- Категория: `bsl`

Система строгой типизации кода проверяет что утверждение (строка присвоения значения) меняет тип

#### Стандарт

- Нет прямой привязки к v8std в описании проверки

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/statement-type-change.md

Утверждение меняет тип (statement-type-change)


ID: v8cs:structure-consructor-too-many-keys

- Категория: `bsl`

Проверка конструктора структуры, содержащего больше 3х ключей

#### Стандарт

- Нет прямой привязки к v8std в описании проверки

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/structure-consructor-too-many-keys.md

Конструктор структуры содержит слишком много ключей (structure-consructor-too-many-keys)


ID: v8cs:structure-consructor-value-type

- Категория: `bsl`

Проверяет строковый литерал в конструкторе структуры что каждый ключ имеет типзированное значение.

#### Стандарт

- Нет прямой привязки к v8std в описании проверки

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/structure-consructor-value-type.md

Типизация значений в конструкторе структуры (structure-consructor-value-type)


ID: v8cs:subsystem-synonym-too-long

- Категория: `md`

Общая длина названия раздела не должна превышать 35 знаков с учетом пробелов (это позволяет разместить название в 2 строки, при дальнейшем увеличении количества знаков появится многоточие). Лучше выбирать названия примерно одного размеры по ширине, чтобы они смотрелись единообразно и ровно.

#### Стандарт

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.md/markdown/ru/subsystem-synonym-too-long.md

Общая длина названия раздела не должна превышать 35 знаков с учетом пробелов (это позволяет разместить название в 2 строки, при дальнейшем увеличении количества знаков появится...

Длина названия раздела превышает 35 символов (subsystem-synonym-too-long)


ID: v8cs:typed-value-adding-to-untyped-collection

- Категория: `bsl`

Проверяет, что вызов метода  происходит для типизированной коллекции.

#### Стандарт

- Нет прямой привязки к v8std в описании проверки

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/typed-value-adding-to-untyped-collection.md

Добавление типизированного значения в не типизированную коллекцию (typed-value-adding-to-untyped-collection)


ID: v8cs:unknown-form-parameter-access

- Категория: `bsl`

Параметры формы следует объявлять явно на закладке Параметры редактора формы.  В таком случае в коде обработчика ПриСозданииНаСервере не требуется проверять  наличие свойств у структуры Параметры, а сам состав параметров формы явно задекларирован  (поэтому их не требуется восстанавливать, изучая весь код обработчика ПриСозданииНаСервере).

#### Стандарт

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/unknown-form-parameter-access.md

Параметры формы следует объявлять явно на закладке Параметры редактора формы. В таком случае в коде обработчика ПриСозданииНаСервере не требуется проверять наличие свойств у...

Обращение к несуществующему параметру формы (unknown-form-parameter-access)


ID: v8cs:unsafe-password-ib-storage

- Категория: `md`

Для сведения к минимуму возможности перехвата пароля злоумышленниками не следует хранить пароли и  другую конфиденциальную информацию в информационной базе.

#### Стандарт

- [#std740: Безопасное хранение паролей](https://v8std.ru/std/740/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.md/markdown/ru/unsafe-password-ib-storage.md

Безопасное хранение паролей #std740

Небезопасное хранение паролей в информационной базе (unsafe-password-ib-storage)


ID: v8cs:use-goto-operator

- Категория: `bsl`

В коде на встроенном языке не рекомендуется использовать оператор Перейти,  так как необдуманное использование данного оператора приводит к получению запутанных,  плохо структурированных модулей, по тексту которых затруднительно понять порядок  исполнения и взаимозависимость фрагментов. Вместо оператора Перейти рекомендуется использовать  другие конструкции встроенного языка.

#### Стандарт

- [#std547: Ограничение на использование оператора Перейти](https://v8std.ru/std/547/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/use-goto-operator.md

В коде на встроенном языке не рекомендуется использовать оператор Перейти, так как необдуманное использование данного оператора приводит к получению запутанных, плохо...

Ограничение на использование оператора Перейти #std547

Используется оператор Перейти (use-goto-operator)


ID: v8cs:use-non-recommended-method

- Категория: `bsl`

Существуют не рекомендуемые методы, вместо которых следует использовать либо методы БСП, либо другие методы.

#### Стандарт

- [#std643: Работа в разных часовых поясах](https://v8std.ru/std/643/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/use-non-recommended-method.md

Работа в разных часовых поясах #std643

Использование не рекомендуемых методов (use-non-recommended-method)


ID: v8cs:using-form-data-to-value

- Категория: `bsl`

В большинстве случаев в модулях форм следует использовать метод формы РеквизитФормыВЗначение вместо метода ДанныеФормыВЗначение.  Рекомендация обусловлена соображениями унификации прикладного кода и тем, что синтаксис метода РеквизитФормыВЗначение проще,  чем у ДанныеФормыВЗначение (а следовательно, меньше вероятность ошибки). В ДанныеФормыВЗначение необходимо дополнительно передавать тип значения, а для РеквизитФормыВЗначение это не обязательно,  а в практическом плане -
избыточно. Наличие в платформе 1С:Предприятие метода формы РеквизитФормыВЗначение (наряду с методом глобального контекста ДанныеФормыВЗначение)  объясняется только удобством его применения. С точки зрения эффективности и результата методы работают одинаково.

#### Стандарт

- [#std409: Использование РеквизитФормыВЗначение и ДанныеФормыВЗначение](https://v8std.ru/std/409/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/using-form-data-to-value.md

В большинстве случаев в модулях форм следует использовать метод формы РеквизитФормыВЗначение вместо метода ДанныеФормыВЗначение. Рекомендация обусловлена соображениями унификации...

Использование РеквизитФормыВЗначение и ДанныеФормыВЗначение #std409

Использование РеквизитФормыВЗначение и ДанныеФормыВЗначение (using-form-data-to-value)


ID: v8cs:using-isinrole

- Категория: `bsl`

Для проверки прав доступа в коде следует использовать метод ПравоДоступа.

#### Стандарт

- [#std689: Настройка ролей и прав доступа](https://v8std.ru/std/689/)
- [#std737: Проверка прав доступа](https://v8std.ru/std/737/)

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/using-isinrole.md

Настройка ролей и прав доступа #std689

Проверка прав доступа #std737

Использован метод "РольДоступна" (using-isinrole)


ID: v8cs:variable-value-type

- Категория: `bsl`

Система строгой типизации кода проверяет что каждая переменная имеет тип значения

#### Стандарт

- Нет прямой привязки к v8std в описании проверки

#### Источник

https://github.com/1C-Company/v8-code-style/blob/master/bundles/com.e1c.v8codestyle.bsl/markdown/ru/variable-value-type.md

Переменная имеет тип значения (variable-value-type)


# DRY (`Don't Repeat Yourself`)

`DRY` означает: не дублируйте одно и то же знание, правило или алгоритм в нескольких местах.

## Что означает в 1С

Для 1С это особенно важно в:

- расчетах;
- валидациях;
- текстах запросов;
- правилах заполнения документов;
- интеграционных маппингах.

Если одно бизнес-правило описано в трех местах, рано или поздно они разъедутся.

## Пример на 1С

#### До
```bsl
// In module A
VATAmount = Amount * 0.20;

// In module B
VATAmount = Amount * 0.20;
```

#### После
```bsl
Function CalculateVAT(Amount) Export

    Return Amount * 0.20;

EndFunction
```

## Когда полезен

- если повторяется именно правило, а не просто похожая строка;
- если изменение должно происходить в одном месте;
- если дублирование уже приводит к расхождениям.

## Когда применяют неправильно

- когда абстракцию вводят после первого же повтора;
- когда объединяют похожие, но не одинаковые сценарии;
- когда ради `DRY` код становится менее читаемым.

DRY означает: не дублируйте одно и то же знание, правило или алгоритм в нескольких местах.

DRY


Это раздел про практические инженерные правила, которые постоянно всплывают в ревью и проектировании: `DRY`, `KISS`, `YAGNI` и похожие.

Они не описывают объектную модель напрямую, как `SOLID`, и не распределяют ответственность, как `GRASP`. Их задача проще: не дать архитектуре и коду усложниться без причины.

## Что внутри

| Принцип | Что означает | На что смотреть |
| --- | --- | --- |
| [DRY](https://v8std.ru/patterns/engineering/dry/) (`Don't Repeat Yourself`) | Не дублируйте знание и правило в нескольких местах. | Не придется ли синхронно править один и тот же смысл в трех модулях. |
| [KISS](https://v8std.ru/patterns/engineering/kiss/) (`Keep It Simple, Stupid`) | Выбирайте простое решение, если оно уже закрывает задачу. | Не появился ли паттерн там, где достаточно процедуры или запроса. |
| [YAGNI](https://v8std.ru/patterns/engineering/yagni/) (`You Aren't Gonna Need It`) | Не стройте расширение заранее без подтвержденной потребности. | Не проектируете ли вы "на будущее", которого пока нет. |
| [Rule of Three](https://v8std.ru/patterns/engineering/rule-of-three/) | Обобщать стоит после устойчивого повторения. | Не выносите ли абстракцию слишком рано. |
| [Separation of Concerns](https://v8std.ru/patterns/engineering/separation-of-concerns/) | Разделяйте разные типы задач по слоям и контекстам. | Не смешаны ли UI, orchestration, доменная логика и интеграции. |

Это раздел про практические инженерные правила, которые постоянно всплывают в ревью и проектировании: DRY, KISS, YAGNI и похожие.

DRY

KISS

Rule of Three

Separation of Concerns

YAGNI

Инженерные принципы


# KISS (`Keep It Simple, Stupid`)

`KISS` требует выбирать самое простое решение, которое уже решает задачу.

## Что означает в 1С

В 1С это обычно защита от ненужной архитектурной роскоши:

- не вводить паттерн там, где хватает одной процедуры;
- не строить многоуровневый сервис вокруг простого запроса;
- не плодить лишние абстракции в прикладном коде.

## Пример на 1С

Если есть ровно один способ отправки уведомления, то простой модуль:

```bsl
Procedure SendNotification(User, MessageText) Export

    // Send by email.

EndProcedure
```

часто лучше, чем преждевременно вводить `Strategy`, фабрики и контейнер из трех слоев.

## Когда полезен

- в небольших задачах;
- в прикладном коде без устойчивой вариативности;
- в ранней фазе реализации, когда система еще не требует расширения.

## Когда применяют неправильно

- когда под видом простоты игнорируют реальные точки изменения;
- когда "простое" решение уже перестало помещаться в голове;
- когда `KISS` используют как оправдание для плохой структуры.

KISS требует выбирать самое простое решение, которое уже решает задачу.

KISS


`Rule of Three` говорит: обобщать и выносить абстракцию обычно стоит после устойчивого третьего повторения.

## Что означает в 1С

Для 1С это удобный практический фильтр между `DRY` и `YAGNI`:

- первый повтор еще может быть случайным;
- второй уже настораживает;
- третий обычно показывает устойчивый шаблон.

После этого абстракция уже основана не на догадке, а на повторяющемся факте.

## Пример на 1С

- первый документ получил похожую валидацию;
- второй документ получил почти ту же валидацию;
- третий документ повторяет тот же подход.

После этого уже разумно выделить общий сервис проверки или общий контракт, а не держать три почти одинаковых куска кода.

## Когда полезен

- при выносе общих модулей и сервисов;
- при решении, пора ли делать паттерн или еще рано;
- при борьбе с преждевременным обобщением.

## Когда применяют неправильно

- когда ждут третьего случая там, где обобщение очевидно уже сейчас;
- когда считают число повторов механически, без анализа смысла;
- когда упускают момент и оставляют четыре-пять копий "на потом".

Rule of Three говорит: обобщать и выносить абстракцию обычно стоит после устойчивого третьего повторения.

Rule of Three


форма не должна содержать бизнес-логику

форма не должна содержать доступ к данным

форма не должна содержать бизнес-логику и доступ к данным

бизнес-логика в форме

доступ к данным в форме

разделение ответственности

разделение ответственностей

`Separation of Concerns` требует разделять разные типы задач по отдельным слоям и контекстам.

## Что означает в 1С

В 1С это обычно означает, что стоит различать:

- форму и UI;
- orchestration сценария;
- доменную логику;
- интеграцию;
- инфраструктурные детали.

Если все это смешано в одном модуле формы или в одном общем модуле, код быстро перестает быть управляемым.

## Пример на 1С

#### CommonForms.SalesOrderForm
```bsl
&AtClient
Procedure PostCommand(Command)

    CommonModules.SalesOrderController.Post(Object.Ref);

EndProcedure
```

#### CommonModules.SalesOrderController
```bsl
Procedure Post(SalesOrderRef) Export

    SalesOrder = SalesOrderRef.GetObject();
    SalesOrderValidation.CheckBeforePosting(SalesOrder);
    SalesOrder.Write(DocumentWriteMode.Posting);

EndProcedure
```

Здесь форма отвечает за UI, контроллер за orchestration, а объект документа и сервисы проверки — за свою часть доменной логики.

## Когда полезен

- в формах с большим количеством действий;
- в интеграционных сценариях;
- в больших прикладных модулях, где все перемешалось.

## Когда применяют неправильно

- когда разделение слоев становится формальным и бесполезным;
- когда между слоями появляется слишком много пустых прокладок;
- когда из-за "чистоты" теряется понимание сквозного сценария.

Separation of Concerns требует разделять разные типы задач по отдельным слоям и контекстам.

Separation of Concerns


# YAGNI (`You Aren't Gonna Need It`)

`YAGNI` говорит: не реализуйте то, в чем нет подтвержденной потребности прямо сейчас.

## Что означает в 1С

Для 1С это особенно полезно в прикладной архитектуре, где легко начать проектировать:

- "универсальный" механизм;
- набор расширений "на будущее";
- поддержку пяти каналов, хотя сейчас есть только один;
- сложную иерархию сервисов без живого сценария использования.

## Пример на 1С

Если в системе есть только один провайдер печати, то:

- один модуль печати часто достаточно;
- фабрика провайдеров, реестр расширений и стратегия выбора могут подождать до реального второго варианта.

## Когда полезен

- когда команда начинает проектировать на годы вперед;
- когда требования пока не подтверждают вариативность;
- когда хочется сократить объем лишнего кода и поддержки.

## Когда применяют неправильно

- когда игнорируют уже известные, подтвержденные требования;
- когда "не нужно сейчас" путают с "точно никогда не понадобится";
- когда из-за `YAGNI` убирают даже простую точку расширения, которая уже назрела.

YAGNI говорит: не реализуйте то, в чем нет подтвержденной потребности прямо сейчас.

YAGNI


# Абстрактная фабрика (`Abstract Factory`)

`Abstract Factory` создает не один объект, а семейство совместимых объектов через общий контракт.

## Опора на ООП

Этот паттерн в первую очередь опирается на [полиморфизм, интерфейсы и инкапсуляцию](https://v8std.ru/patterns/principles/): клиент знает только общий контракт фабрики, а выбор конкретного семейства реализаций спрятан внутри

## Что показывает пример на 1С

- `CommonModules.WindowsFactory` и `CommonModules.LinuxFactory` экспортируют одинаковые методы `CreateProcess()` и `CreateTempFile()`.
- `DataProcessors.Application` принимает фабрику в `Init()` и потом работает только через `Factory.CreateProcess()` и `Factory.CreateTempFile()`.
- Клиентский код переключает сразу целое семейство реализаций, а не выбирает каждый объект по отдельности.

## Пример

#### CommonModules.WindowsFactory
```bsl
#Region Public

Function CreateProcess() Export

    Return True; // TODO: Implement it! Return concreate object

EndFunction

Function CreateTempFile() Export

    Return False; // TODO: Implement it! Return concreate object

EndFunction

#EndRegion
```

#### CommonModules.LinuxFactory
```bsl
#Region Public

Function CreateProcess() Export

    Return False; // TODO: Implement it! Return concreate object

EndFunction

Function CreateTempFile() Export

    Return True; // TODO: Implement it! Return concreate object

EndFunction

#EndRegion
```

#### DataProcessors.Application
```bsl
Var Factory;

Function Init(Factory_) Export

    Factory = Factory_;

EndFunction

Function RunTempProcess() Export

    Process = Factory.CreateProcess();
    TempFile = Factory.CreateTempFile();

EndFunction
```

## Где полезен в 1С

- когда нужно подменять набор интеграционных объектов под разную среду;
- когда один и тот же сценарий должен работать с разными поставщиками инфраструктуры;
- когда важно держать совместимость объектов внутри одной "платформы" или режима работы.

## Когда паттерн лишний

- если семейство реализаций всего одно;
- если меняется только один объект и достаточно `Factory Method`;
- если клиенту проще прямо создать нужный объект без дополнительного слоя.

## Источник примера

- [zeegin/DesignPatterns: AbstractFactory](https://github.com/zeegin/DesignPatterns/tree/master/AbstractFactory)

Abstract Factory создает не один объект, а семейство совместимых объектов через общий контракт.

Принципы ООП

Abstract Factory


# Адаптер (`Adapter`)

`Adapter` переводит неудобный или устаревший интерфейс в тот контракт, который нужен клиентскому коду.

## Опора на ООП

`Adapter` в первую очередь опирается на [интерфейсы и инкапсуляцию](https://v8std.ru/patterns/principles/): несовместимый контракт прячется внутри адаптера, а наружу отдается ожидаемый API. Полиморфизм появляется, когда адаптер подменяет обычную реализацию по тому же контракту

## Что показывает пример на 1С

- `CommonModules.Adapter` экспортирует понятный клиенту метод `BeautyMethod2WTF(Parameters)`.
- Внутри адаптер преобразует входные данные через `Convert()`, вызывает `Legacy.WTFMethod(Input, Output)` и потом собирает ответ через `Extract(Output)`.
- Код, который использует адаптер, не знает деталей старого контракта и не размазывает их по системе.

## Пример

#### CommonModules.Adapter
```bsl
#Region Public

Function BeautyMethod2WTF(Parameters) Export

    Input = Convert(Parameters);
    Output = Undefined;

    Legacy.WTFMethod(Input, Output);

    Result = Extract(Output);
    Return Result;

EndFunction

#EndRegion

#Region Private

Function Convert(Parameters)

    Return Parameters;

EndFunction

Function Extract(Output)

    Return Output;

EndFunction

#EndRegion
```

#### CommonModules.Legacy
```bsl
#Region Public

Procedure WTFMethod(Input, Output) Export

    Output = "Shit";

EndProcedure

#EndRegion
```

## Где полезен в 1С

- при оборачивании старых общих модулей с неудачным API;
- при интеграции с внешними компонентами, COM или HTTP-клиентами;
- когда нужно перейти на новый интерфейс без массовой переписи прикладного кода.

## Когда паттерн лишний

- если старый код можно безопасно переделать без слоя совместимости;
- если различия между интерфейсами минимальны;
- если адаптер начинает содержать слишком много бизнес-логики и превращается в фасад или сервис.

## Источник примера

- [zeegin/DesignPatterns: Adapter](https://github.com/zeegin/DesignPatterns/tree/master/Adapter)

Adapter переводит неудобный или устаревший интерфейс в тот контракт, который нужен клиентскому коду.

Принципы ООП

Adapter


# Мост (`Bridge`)

`Bridge` разделяет абстракцию и реализацию так, чтобы их можно было развивать независимо.

## Опора на ООП

`Bridge` опирается на [композицию, интерфейсы и полиморфизм](https://v8std.ru/patterns/principles/): абстракция не вшивает реализацию внутрь себя, а держит ссылку на отдельный объект с общим контрактом

## Что показывает пример на 1С

- `DataProcessors.DocumentExporter` отвечает за прикладной сценарий экспорта, но не знает деталей конкретного формата.
- `CommonModules.PdfRenderer` и `CommonModules.HtmlRenderer` реализуют единый контракт `Render(Data)`.
- Клиент может менять реализацию рендера независимо от того, какой именно объект он экспортирует.

## Пример

#### DataProcessors.Application
```bsl
Exporter = DataProcessors.DocumentExporter.Create();
Exporter.SetRenderer(CommonModules.PdfRenderer);

PdfResult = Exporter.Export(SalesOrderRef);

Exporter.SetRenderer(CommonModules.HtmlRenderer);
HtmlResult = Exporter.Export(SalesOrderRef);
```

#### DataProcessors.DocumentExporter
```bsl
Var Renderer;

#Region Public

Procedure SetRenderer(Renderer_) Export

    Renderer = Renderer_;

EndProcedure

Function Export(DocumentRef) Export

    Data = PrepareData(DocumentRef);
    Return Renderer.Render(Data);

EndFunction

#EndRegion

#Region Private

Function PrepareData(DocumentRef)

    Data = New Structure;
    Data.Insert("Number", DocumentRef.Number);
    Data.Insert("Date", DocumentRef.Date);
    Return Data;

EndFunction

#EndRegion
```

#### CommonModules.PdfRenderer
```bsl
Function Render(Data) Export

    Return "PDF:" + Data.Number;

EndFunction
```

#### CommonModules.HtmlRenderer
```bsl
Function Render(Data) Export

    Return "<html><body>" + Data.Number + "</body></html>";

EndFunction
```

## Где полезен в 1С

- когда прикладной объект и способ его представления меняются по разным причинам;
- когда один и тот же сценарий должен работать с несколькими форматами вывода;
- когда не хочется размножать комбинации вида `SalesOrderPdfExporter`, `SalesOrderHtmlExporter`, `InvoicePdfExporter`.

## Когда паттерн лишний

- если реализация одна и не будет меняться;
- если второй оси вариативности пока нет;
- если композиция добавляет больше абстракции, чем дает реальной гибкости.

Bridge разделяет абстракцию и реализацию так, чтобы их можно было развивать независимо.

Принципы ООП

Bridge


# Строитель (`Builder`)

`Builder` отделяет сценарий сборки объекта от конкретного представления результата.

## Опора на ООП

`Builder` опирается на [инкапсуляцию](https://v8std.ru/patterns/principles/): процесс сборки и промежуточное состояние объекта прячутся внутри строителя. Когда у строителей есть общий контракт шагов, подключается и полиморфизм

## Что показывает пример на 1С

- `DataProcessors.Director` хранит ссылку на строителя и в `Make(Type, Manager)` выполняет общий сценарий: `Reset()`, `SetTelephone()`, `SetManager()`, при необходимости `SetDebt()`.
- `LeadsBuilder` и `PartnersBuilder` реализуют одинаковые шаги, но собирают разные объекты метаданных.
- В результате один и тот же алгоритм сборки порождает разные прикладные объекты без `Если ... Тогда` по типам во внешнем коде.

## Пример

#### DataProcessors.Director
```bsl
Var Builder;

#Region Public

Procedure Init(Builder_) Export

    Builder = Builder_;

EndProcedure

Function Make(Type, Manager) Export

    Builder.Reset();
    Builder.SetTelephone();
    Builder.SetManager(Manager);

    If Type = "CleanDebt" Then
        Builder.SetDebt(0);
    EndIf;

    Return Builder.GetResult();

EndFunction

#EndRegion
```

#### DataProcessors.LeadsBuilder
```bsl
Var Object;

#Region Public

Procedure Reset() Export

    Object = Catalogs.Leads.CreateItem();

EndProcedure

Procedure SetTelephone(Tel) Export

    Object.Telephone = Tel;
    Object.CountryCode = ExtractCountryCode(Tel);

EndProcedure

Procedure SetManager(Manager) Export

    Object.ResponsiblePerson = Manager;

EndProcedure

Function GetResult()

    Return Object;

EndFunction

#EndRegion
```

#### DataProcessors.PartnersBuilder
```bsl
Var Object;

#Region Public

Procedure Reset() Export

    Object = Catalogs.Partners.CreateItem();

EndProcedure

Procedure SetTelephone(Tel) Export

    Object.Tel = Tel;

EndProcedure

Procedure SetManager(Manager) Export

    Object.Manager = Manager;

EndProcedure

Procedure SetDebt(Debt) Export

    Object.Debt = Debt;

EndProcedure

Function GetResult()

    Return Object;

EndFunction

#EndRegion
```

## Вариации

У `Builder` есть несколько распространенных форм.

### С директором и без директора

- `С директором`:
  отдельный объект управляет шагами сборки и знает правильный порядок вызовов. Именно такой вариант показан в примере через `DataProcessors.Director`.
- `Без директора`:
  клиент сам вызывает шаги строителя в нужном порядке. Это проще по структуре, если сценарий сборки короткий и не требует отдельной оркестрации.

### Классический и fluent-builder

- `Классический builder`:
  методы вроде `Reset()`, `SetTelephone()` и `SetManager()` ничего не возвращают, а результат забирается отдельным `GetResult()`.
- `Fluent builder`:
  каждый шаг возвращает самого строителя, чтобы можно было писать цепочки вызовов.

Пример fluent-подхода на 1С:

```bsl
Builder
    .Reset()
    .SetTelephone(Телефон)
    .SetManager(Менеджер)
    .SetDebt(0);

Result = Builder.GetResult();
```

В 1С fluent-вариант удобен для компактных сценариев, но классический builder обычно проще читать и отлаживать, особенно если шаги сборки связаны с побочными эффектами или клиент-серверным разделением.

## Где полезен в 1С

- когда объект собирается в несколько этапов из разных источников данных;
- когда есть несколько вариантов итогового результата при общем сценарии подготовки;
- когда важно централизовать обязательные шаги создания объекта.

## Когда паттерн лишний

- если объект создается одной строкой;
- если различия между вариантами минимальны;
- если строители только проксируют присваивания и не дают архитектурной пользы.

## Источник примера

- [zeegin/DesignPatterns: Builder](https://github.com/zeegin/DesignPatterns/tree/master/Builder)

Builder отделяет сценарий сборки объекта от конкретного представления результата.

Принципы ООП

Builder


# Цепочка обязанностей (`Chain of Responsibility`)

`Chain of Responsibility` передает запрос по цепочке обработчиков, пока один из них не возьмет его на себя.

## Опора на ООП

`Chain of Responsibility` опирается на [полиморфизм и интерфейсы](https://v8std.ru/patterns/principles/): каждое звено реализует общий контракт обработки, а клиент не знает, какое именно звено сработает. Инкапсуляция помогает локализовать отдельный шаг обработки внутри конкретного звена

## Что показывает пример на 1С

- `DataProcessors.BaseHandler` хранит `NextHandler`, умеет делать `SetNext()` и по умолчанию делегирует `Handle(Request)` дальше.
- `DeserializePartnerHandler`, `DeserializeProductsHandler` и `DeserializeWarehouseHandler` проверяют, их ли это запрос, и либо обрабатывают его, либо отдают следующему звену.
- Такая структура особенно понятна в сценариях разбора входящих сообщений или каскадной валидации.

## Пример

#### DataProcessors.BaseHandler
```bsl
Var NextHandler;

#Region Public

Function SetNext(Handler) Export

    NextHandler = Handler;
    Return Handler;

EndFunction

Function Handle(Request) Export

    If NextHandler <> Undefined Then
        NextHandler.Handle(Request);
    EndIf;

    Return Undefined;

EndFunction

#EndRegion
```

#### DataProcessors.DeserializePartnerHandler
```bsl
Var Super;

#Region Public

Function Init(Super_) Export

    Super = Super_;
    Return ThisObject;

EndFunction

Function Handle(Request) Export

    If IsPartner(Request) Then
        Return Deserialize(Request);
    EndIf;

    Return Super.Handle(Request);

EndFunction

#EndRegion
```

## Вариации

У `Chain of Responsibility` на практике есть как минимум две очень разные формы.

### 1. Цепочка выбора обработчика

Это классический вариант: каждый обработчик решает, его это запрос или нет.

- если обработчик подходит, он забирает запрос себе;
- если не подходит, передает дальше;
- в нормальном случае реальную работу выполняет только одно звено цепочки.

Именно такой вариант показан в текущем примере с `DeserializePartnerHandler`, `DeserializeProductsHandler` и `DeserializeWarehouseHandler`: цепочка ищет, кто именно должен разобрать входящие данные.

Такой стиль полезен, когда:

- нужно определить тип входящего сообщения;
- выбор зависит от правил или формата данных;
- важна расширяемость без большого `Если ... ИначеЕсли`.

### 2. Цепочка обязательного pipeline

Во второй вариации звенья не выбирают "кто из нас обработает запрос", а последовательно прогоняют его через обязательные шаги.

- каждое звено делает свою часть работы;
- после этого почти всегда передает управление дальше;
- итоговый результат формируется после прохождения всей цепочки.

Для 1С это может быть, например:

- валидация данных;
- нормализация входного пакета;
- обогащение служебными полями;
- проверка прав;
- только потом основная обработка или запись.

Такой вариант ближе не к маршрутизации, а к pipeline. Он полезен, когда все условия обязательны и их нужно проходить в фиксированном порядке до основной операции.

Частный случай такого pipeline в 1С можно увидеть в обработке подписок на события объектов метаданных. По сути, объект последовательно проходит через несколько обязательных обработчиков, каждый из которых выполняет свою часть проверки, подготовки или синхронизации данных.

### Как различать эти варианты

- `Цепочка выбора` отвечает на вопрос: кто должен это обработать?
- `Цепочка-pipeline` отвечает на вопрос: какие обязательные шаги нужно пройти до обработки?

Снаружи они могут выглядеть похоже, но архитектурно это разные сценарии. В первом случае обычно ищется одно подходящее звено, во втором почти вся цепочка должна отработать целиком.

## Где полезен в 1С

- при маршрутизации входящих пакетов из интеграций;
- при многошаговой проверке данных перед записью;
- при последовательной попытке разных способов обработки.

## Когда паттерн лишний

- если обработчиков один-два и порядок не меняется;
- если проще и яснее оставить таблицу соответствий или `Если ... ИначеЕсли`;
- если поведение цепочки непрозрачно и трудно отлаживается.

## Источник примера

- [zeegin/DesignPatterns: ChainOfResponsibility](https://github.com/zeegin/DesignPatterns/tree/master/ChainOfResponsibility)

Chain of Responsibility передает запрос по цепочке обработчиков, пока один из них не возьмет его на себя.

Принципы ООП

Chain of Responsibility


# Команда (`Command`)

`Command` превращает действие в отдельный объект или значение, которое можно передать, сохранить и выполнить позже.

## Опора на ООП

`Command` в первую очередь опирается на [инкапсуляцию](https://v8std.ru/patterns/principles/): действие и его контекст упаковываются в отдельный объект или описание вызова. Полиморфизм появляется там, где разные команды исполняются через единый механизм

## Что показывает пример на 1С

- `CommonModules.BackgroundWorker` выступает как `Invoker`: создает структуру работника и потом вызывает `ExecuteNotifyProcessing()` для команд завершения и прогресса.
- В `CommonModules.Initialize` команды собираются через `NotifyDescription`, причем одна из них получает дополнительный `Context`.
- В результате код фонового исполнителя не знает, что именно будет происходить по завершении или во время прогресса.

## Пример

#### CommonModules.Initialize
```bsl
#Region Public

Procedure Init() Export

    // Note! NotifyDescription is a command object!

    UserName = "Harry"; // Some specifics context

    Worker = BackgroundWorker.NewWorker();
    Worker.OnFinish = NewCommandOnFinish();
    Worker.OnProgress = NewCommandOnProgress(UserName);

    BackgroundWorker.Run(Worker);

EndProcedure

#EndRegion

#Region Internal

#Region CommandOnFinish

Function NewCommandOnFinish() Export

    Return New NotifyDescription("ExecCommandOnFinish", ThisObject);

EndFunction

Procedure ExecCommandOnFinish(Result, Context) Export

    // Do something...

EndProcedure

#EndRegion

#Region CommandOnProgress

Function NewCommandOnProgress(UserName) Export

    Context = New Structure;
    Context.Insert("UserName", UserName);

    Return New NotifyDescription("ExecCommandOnProgress", ThisObject, Context);

EndFunction

Procedure ExecCommandOnProgress(Result, Context) Export

    // Do something with Context.UserName

EndProcedure

#EndRegion

#EndRegion
```

#### CommonModules.BackgroundWorker
```bsl
#Region Public

Function NewWorker() Export

    Self = New Structure;
    Self.Insert("OnFinish", Undefined);
    Self.Insert("OnProgress", Undefined);

    Return Self;

EndFunction

Procedure Run(Worker) Export

    // Invoker!
    ExecuteNotifyProcessing(Worker.OnProgress, "Specific Args 1");
    ExecuteNotifyProcessing(Worker.OnFinish, "Specific Args 2");

EndProcedure

#EndRegion
```

## Где полезен в 1С

- в фоновых заданиях и асинхронных сценариях;
- в UI-обработчиках, где нужно передать действие вместе с контекстом;
- в очередях операций, отложенных вызовах и повторном выполнении действий.

## Когда паттерн лишний

- если можно прямо вызвать одну процедуру;
- если команды ничем не отличаются и не живут дольше одного вызова;
- если передача действий только усложняет трассировку.

## Источник примера

- [zeegin/DesignPatterns: Command](https://github.com/zeegin/DesignPatterns/tree/master/Command)

Command превращает действие в отдельный объект или значение, которое можно передать, сохранить и выполнить позже.

Принципы ООП

Command


# Компоновщик (`Composite`)

`Composite` позволяет работать с одиночными объектами и деревьями объектов через единый контракт.

## Опора на ООП

`Composite` опирается на [полиморфизм и интерфейсы](https://v8std.ru/patterns/principles/): и лист, и группа реализуют один и тот же контракт, поэтому клиенту не нужно знать, перед ним один узел или целая ветка

## Что показывает пример на 1С

- `DataProcessors.ValidationGroup` хранит дочерние узлы и сам реализует тот же метод `Validate(Context)`, что и листья.
- `RequiredFieldRule` и `PositiveAmountRule` выступают как листья дерева правил.
- Клиент может запускать одну и ту же проверку и для одного правила, и для целой группы правил.

## Пример

#### DataProcessors.Application
```bsl
Rules = DataProcessors.ValidationGroup.Create();

RequiredRule = DataProcessors.RequiredFieldRule.Create();
RequiredRule.Init("Partner");

AmountRule = DataProcessors.PositiveAmountRule.Create();
AmountRule.Init("Amount");

Rules.Add(RequiredRule);
Rules.Add(AmountRule);

If Rules.Validate(DocumentData) Then
    Message("Validation passed");
EndIf;
```

#### DataProcessors.ValidationGroup
```bsl
Var Children;

#Region Public

Procedure Add(Node) Export

    Children.Add(Node);

EndProcedure

Function Validate(Context) Export

    For Each Child In Children Do
        If Not Child.Validate(Context) Then
            Return False;
        EndIf;
    EndDo;

    Return True;

EndFunction

#EndRegion

Children = New Array;
```

#### DataProcessors.RequiredFieldRule
```bsl
Var FieldName;

#Region Public

Procedure Init(FieldName_) Export

    FieldName = FieldName_;

EndProcedure

Function Validate(Context) Export

    Return Context.Property(FieldName)
        And Context.Get(FieldName) <> Undefined;

EndFunction

#EndRegion
```

#### DataProcessors.PositiveAmountRule
```bsl
Var FieldName;

#Region Public

Procedure Init(FieldName_) Export

    FieldName = FieldName_;

EndProcedure

Function Validate(Context) Export

    Return Context.Get(FieldName) > 0;

EndFunction

#EndRegion
```

## Где полезен в 1С

- для деревьев проверок, фильтров и маршрутов согласования;
- для меню, настроек и иерархий команд;
- когда одинаковая операция должна работать и на листе, и на группе элементов.

## Когда паттерн лишний

- если структура не образует дерево;
- если листья и группы ведут себя слишком по-разному;
- если плоский список правил читается проще.

Composite позволяет работать с одиночными объектами и деревьями объектов через единый контракт.

Принципы ООП

Composite


# Декоратор (`Decorator`)

`Decorator` добавляет объекту новое поведение через обертку, не меняя исходную реализацию.

## Опора на ООП

`Decorator` опирается на [интерфейсы, композицию и полиморфизм](https://v8std.ru/patterns/principles/): декоратор реализует тот же контракт, что и оборачиваемый компонент, и делегирует ему базовую работу, добавляя свой слой поведения

## Что показывает пример на 1С

- `CommonModules.HttpSender` реализует базовую отправку запроса.
- `LoggingSenderDecorator` и `RetrySenderDecorator` оборачивают отправителя и добавляют логирование и повторные попытки.
- Клиент собирает нужную цепочку поведения без изменения исходного отправителя.

## Пример

#### DataProcessors.Application
```bsl
Sender = CommonModules.HttpSender;

SenderWithLog = DataProcessors.LoggingSenderDecorator.Create();
SenderWithLog.Init(Sender);

SenderWithRetry = DataProcessors.RetrySenderDecorator.Create();
SenderWithRetry.Init(SenderWithLog);

Response = SenderWithRetry.Send(Request);
```

#### CommonModules.HttpSender
```bsl
Function Send(Request) Export

    Return Request.Body;

EndFunction
```

#### DataProcessors.LoggingSenderDecorator
```bsl
Var Component;

#Region Public

Procedure Init(Component_) Export

    Component = Component_;

EndProcedure

Function Send(Request) Export

    WriteLogEvent("Integration", EventLogLevel.Information, , , "Sending request");
    Return Component.Send(Request);

EndFunction

#EndRegion
```

#### DataProcessors.RetrySenderDecorator
```bsl
Var Component;

#Region Public

Procedure Init(Component_) Export

    Component = Component_;

EndProcedure

Function Send(Request) Export

    For Counter = 1 To 3 Do
        Try
            Return Component.Send(Request);
        Except
        EndTry;
    EndDo;

    Raise "Request failed";

EndFunction

#EndRegion
```

## Близость к Proxy

`Decorator` очень похож на [Proxy](https://v8std.ru/patterns/gof/proxy/), потому что оба паттерна строятся как обертка вокруг компонента с тем же контрактом

Из-за этого их легко спутать:

- и там, и там есть внутренний компонент;
- и там, и там вызов проходит через дополнительный слой;
- и клиент часто не видит разницы по сигнатуре методов.

Но смысл различается.

- `Decorator` наращивает поведение;
- `Proxy` контролирует доступ, создание или способ обращения к объекту.

Поэтому для 1С полезно разделять их по намерению:

- если слой добавляет логирование, ретраи, метрики или кэш вокруг вызова, это обычно `Decorator`;
- если слой проверяет права, скрывает удаленный вызов или лениво создает тяжелый объект, это обычно `Proxy`.

## Где полезен в 1С

- для логирования, кэширования, ретраев и профилирования вокруг сервиса;
- для постепенного наращивания технического поведения без переписывания базовой логики;
- когда один и тот же компонент нужно оборачивать разными комбинациями технических слоев.

## Когда паттерн лишний

- если поведение проще явно вызвать рядом;
- если оберток становится слишком много и цепочка трудно читается;
- если декоратор начинает менять бизнес-смысл исходного компонента.

Decorator добавляет объекту новое поведение через обертку, не меняя исходную реализацию.

Proxy

Принципы ООП

Decorator


# Фасад (`Facade`)

`Facade` дает простой внешний вход к сложной подсистеме.

## Опора на ООП

`Facade` опирается прежде всего на [инкапсуляцию](https://v8std.ru/patterns/principles/): сложная координация нескольких модулей и объектов прячется за компактным внешним API

## Что показывает пример на 1С

- `CommonModules.SalesOrderFacade` собирает в одной точке создание документа, заполнение, контроль остатков и проведение.
- Клиентский код вызывает один понятный метод вместо серии обращений к разным подсистемам.
- При этом фасад не отменяет внутренние сервисы, а только упрощает типовой сценарий работы с ними.

## Пример

#### DataProcessors.Application
```bsl
SalesOrder = CommonModules.SalesOrderFacade.CreateAndPost(Partner, Items);
```

#### CommonModules.SalesOrderFacade
```bsl
Function CreateAndPost(Partner, Items) Export

    SalesOrder = Documents.SalesOrder.CreateDocument();

    SalesOrderFilling.FillHeader(SalesOrder, Partner);
    SalesOrderFilling.FillItems(SalesOrder, Items);

    StockControl.CheckAvailability(SalesOrder);

    SalesOrder.Write(DocumentWriteMode.Posting);
    Return SalesOrder;

EndFunction
```

#### CommonModules.StockControl
```bsl
Procedure CheckAvailability(SalesOrder) Export

    // Check stock balances.

EndProcedure
```

## Где полезен в 1С

- для типовых сценариев работы с документами, обменами и расчетами;
- когда внутренняя подсистема состоит из нескольких сервисов и объектов;
- когда внешнему коду нужен короткий и устойчивый API.

## Когда паттерн лишний

- если фасад просто переименовывает один вызов;
- если под фасадом начинает скапливаться вся бизнес-логика системы;
- если разным клиентам нужны слишком разные сценарии и один вход их только запутывает.

Facade дает простой внешний вход к сложной подсистеме.

Принципы ООП

Facade


# Фабричный метод (`Factory Method`)

`Factory Method` делегирует создание конкретного объекта специальному методу.

## Опора на ООП

`Factory Method` классически построен прежде всего на [полиморфизме](https://v8std.ru/patterns/principles/): клиент вызывает общий способ создания, а конкретная реализация сама решает, какой объект вернуть. В этом смысле паттерн напрямую вырастает из идеи полиморфизма

## Что показывает пример на 1С

- `CommonModules.ClientSpecific` экспортирует `CreateWrapper()` и возвращает `DataProcessors.COMWrapper.Create()`.
- Идея в том, что другой создатель, например для веб-клиента, может вернуть другую обертку при том же клиентском контракте.
- Внешний код просит "создай подходящий wrapper", а не выбирает конкретный `DataProcessor` сам.

## Пример

#### CommonModules.ClientSpecific
```bsl
#Region Public

Function CreateWrapper() Export

    Return DataProcessors.COMWrapper.Create();

EndFunction

#EndRegion
```

#### DataProcessors.COMWrapper
```bsl
#Region Public

Function Init(COMObject) Export

    Return "Transformation";

EndFunction

#EndRegion
```

## Где полезен в 1С

- когда создание зависит от типа клиента или канала интеграции;
- когда нужно изолировать логику выбора конкретной реализации;
- когда важен единый вход для создания однотипных оберток и адаптеров.

## Когда паттерн лишний

- если тип реализации не меняется;
- если выбор делается один раз и проще описывается простым условием;
- если фабричный метод превращается в склад разрозненных `Если`.

## Источник примера

- [zeegin/DesignPatterns: FactoryMethod](https://github.com/zeegin/DesignPatterns/tree/master/FactoryMethod)

Factory Method делегирует создание конкретного объекта специальному методу.

Принципы ООП

Factory Method


# Приспособленец (`Flyweight`)

`Flyweight` выносит общую часть состояния, чтобы не хранить одинаковые данные в большом количестве однотипных объектов.

## Опора на ООП

`Flyweight` опирается на [инкапсуляцию и композицию](https://v8std.ru/patterns/principles/): неизменяемая общая часть состояния живет в разделяемом объекте, а внешний контекст передается отдельно в момент использования

## Что показывает пример на 1С

- `CommonModules.CellStyleFactory` кэширует небольшое число объектов стиля по коду.
- `ReportCellPainter` получает стиль как разделяемую часть, а конкретный текст и ячейку как внешнее состояние.
- Один и тот же объект стиля можно переиспользовать для множества строк отчета или табличного документа.

## Пример

#### DataProcessors.Application
```bsl
ErrorStyle = CommonModules.CellStyleFactory.GetStyle("Error");
WarningStyle = CommonModules.CellStyleFactory.GetStyle("Warning");

Painter = DataProcessors.ReportCellPainter.Create();
Painter.DrawCell(Cell1, ErrorStyle, "Over limit");
Painter.DrawCell(Cell2, ErrorStyle, "Missing data");
Painter.DrawCell(Cell3, WarningStyle, "Close to threshold");
```

#### CommonModules.CellStyleFactory
```bsl
Var Cache;

#Region Public

Function GetStyle(Code) Export

    If Cache.ContainsKey(Code) Then
        Return Cache.Get(Code);
    EndIf;

    Style = CreateStyle(Code);
    Cache.Insert(Code, Style);
    Return Style;

EndFunction

#EndRegion

#Region Private

Function CreateStyle(Code)

    If Code = "Error" Then
        Return New Structure("Color,Bold", "Red", True);
    EndIf;

    Return New Structure("Color,Bold", "Orange", False);

EndFunction

#EndRegion

Cache = New Map;
```

#### DataProcessors.ReportCellPainter
```bsl
Procedure DrawCell(Cell, Style, Text) Export

    Cell.Text = Text;
    Cell.TextColor = Style.Color;
    Cell.FontBold = Style.Bold;

EndProcedure
```

## Где полезен в 1С

- при большом количестве однотипных настроек оформления;
- в кэшах метаданных, описаний колонок, шаблонов и справочных структур;
- когда общих комбинаций мало, а объектов применения много.

## Когда паттерн лишний

- если объектов немного и экономия несущественна;
- если разделяемое состояние часто меняется;
- если внешнее состояние становится слишком сложным и неудобным для передачи.

Flyweight выносит общую часть состояния, чтобы не хранить одинаковые данные в большом количестве однотипных объектов.

Принципы ООП

Flyweight


`GOF` — это набор из 23 классических паттернов проектирования из книги *Design Patterns: Elements of Reusable Object-Oriented Software*.

## Как читать раздел

1. Сначала понять намерение паттерна, а не копировать форму.
2. Потом посмотреть, как эта идея раскладывается на объекты метаданных, общие модули, формы и обработчики в 1С.
3. И только после этого решать, нужен ли паттерн в прикладной задаче или достаточно более простого кода.

## Порождающие паттерны

| Паттерн | Коротко | Пример |
| --- | --- | --- |
| [Абстрактная фабрика](https://v8std.ru/patterns/gof/abstract-factory/) (`Abstract Factory`) | Создает семейства связанных объектов через единый интерфейс. | [AbstractFactory](https://github.com/zeegin/DesignPatterns/tree/master/AbstractFactory) |
| [Строитель](https://v8std.ru/patterns/gof/builder/) (`Builder`) | Разделяет процесс пошаговой сборки сложного объекта и его итоговое представление. | [Builder](https://github.com/zeegin/DesignPatterns/tree/master/Builder) |
| [Фабричный метод](https://v8std.ru/patterns/gof/factory-method/) (`Factory Method`) | Делегирует создание объектов специализированному методу или подклассу. | [FactoryMethod](https://github.com/zeegin/DesignPatterns/tree/master/FactoryMethod) |
| [Прототип](https://v8std.ru/patterns/gof/prototype/) (`Prototype`) | Создает новые объекты копированием заранее настроенного экземпляра. | [Prototype](https://github.com/zeegin/DesignPatterns/tree/master/Prototype) |
| [Одиночка](https://v8std.ru/patterns/gof/singleton/) (`Singleton`) | Ограничивает создание класса одним экземпляром и дает глобальную точку доступа. | [Singleton](https://github.com/zeegin/DesignPatterns/tree/master/Singleton) |

## Структурные паттерны

| Паттерн | Коротко | Пример |
| --- | --- | --- |
| [Адаптер](https://v8std.ru/patterns/gof/adapter/) (`Adapter`) | Приводит несовместимый интерфейс к ожидаемому клиентом виду. | [Adapter](https://github.com/zeegin/DesignPatterns/tree/master/Adapter) |
| [Мост](https://v8std.ru/patterns/gof/bridge/) (`Bridge`) | Разделяет абстракцию и реализацию, чтобы их можно было развивать независимо. | |
| [Компоновщик](https://v8std.ru/patterns/gof/composite/) (`Composite`) | Позволяет работать с одиночными объектами и их деревьями единообразно. | |
| [Декоратор](https://v8std.ru/patterns/gof/decorator/) (`Decorator`) | Добавляет объекту поведение через обертку, не меняя исходный класс. | |
| [Фасад](https://v8std.ru/patterns/gof/facade/) (`Facade`) | Дает простой внешний вход к сложной подсистеме. | |
| [Приспособленец](https://v8std.ru/patterns/gof/flyweight/) (`Flyweight`) | Выносит общую часть состояния, чтобы экономить память на большом числе однотипных объектов. | |
| [Заместитель](https://v8std.ru/patterns/gof/proxy/) (`Proxy`) | Подставляет специальный объект вместо реального и контролирует доступ к нему. | |

## Поведенческие паттерны

| Паттерн | Коротко | Пример |
| --- | --- | --- |
| [Цепочка обязанностей](https://v8std.ru/patterns/gof/chain-of-responsibility/) (`Chain of Responsibility`) | Передает запрос по цепочке обработчиков, пока кто-то не возьмет его на себя. | [ChainOfResponsibility](https://github.com/zeegin/DesignPatterns/tree/master/ChainOfResponsibility) |
| [Команда](https://v8std.ru/patterns/gof/command/) (`Command`) | Представляет действие как отдельный объект. | [Command](https://github.com/zeegin/DesignPatterns/tree/master/Command) |
| [Интерпретатор](https://v8std.ru/patterns/gof/interpreter/) (`Interpreter`) | Описывает грамматику языка и интерпретирует выражения этого языка. | |
| [Итератор](https://v8std.ru/patterns/gof/iterator/) (`Iterator`) | Дает способ обходить коллекцию, не раскрывая ее внутреннее устройство. | [Iterator](https://github.com/zeegin/DesignPatterns/tree/master/Iterator) |
| [Посредник](https://v8std.ru/patterns/gof/mediator/) (`Mediator`) | Централизует взаимодействие между объектами и уменьшает прямые связи между ними. | [Mediator](https://github.com/zeegin/DesignPatterns/tree/master/Mediator) |
| [Снимок](https://v8std.ru/patterns/gof/memento/) (`Memento`) | Сохраняет и восстанавливает внутреннее состояние объекта. | [Memento](https://github.com/zeegin/DesignPatterns/tree/master/Memento) |
| [Наблюдатель](https://v8std.ru/patterns/gof/observer/) (`Observer`) | Подписчики получают уведомление об изменении состояния издателя. | [Observer](https://github.com/zeegin/DesignPatterns/tree/master/Observer) |
| [Состояние](https://v8std.ru/patterns/gof/state/) (`State`) | Перекладывает поведение по состояниям в отдельные объекты-состояния. | [State](https://github.com/zeegin/DesignPatterns/tree/master/State) |
| [Стратегия](https://v8std.ru/patterns/gof/strategy/) (`Strategy`) | Инкапсулирует взаимозаменяемые алгоритмы за единым интерфейсом. | [Strategy](https://github.com/zeegin/DesignPatterns/tree/master/Strategy) |
| [Шаблонный метод](https://v8std.ru/patterns/gof/template-method/) (`Template Method`) | Задает каркас алгоритма, оставляя отдельные шаги на расширение. | [TemplateMethod](https://github.com/zeegin/DesignPatterns/tree/master/TemplateMethod) |
| [Посетитель](https://v8std.ru/patterns/gof/visitor/) (`Visitor`) | Выносит операции над структурой объектов в отдельный объект-посетитель. | [Visitor](https://github.com/zeegin/DesignPatterns/tree/master/Visitor) |

GOF — это набор из 23 классических паттернов проектирования из книги Design Patterns: Elements of Reusable Object-Oriented Software.

Abstract Factory

Adapter

Bridge

Builder

Chain of Responsibility

Command

Composite

Decorator

Facade

Factory Method

Flyweight

Interpreter

Iterator

Mediator

Memento

Observer

Prototype

Proxy

Singleton

State

Strategy

Template Method

Visitor

GOF


# Интерпретатор (`Interpreter`)

`Interpreter` описывает грамматику небольшого языка и выполняет выражения этого языка над заданным контекстом.

## Опора на ООП

`Interpreter` опирается на [полиморфизм и композицию](https://v8std.ru/patterns/principles/): каждое выражение реализует общий контракт интерпретации, а сложные выражения собираются из более простых

## Что показывает пример на 1С

- `EqualsStatusExpression` и `AmountGreaterExpression` интерпретируют простые правила над одним и тем же контекстом.
- `AndExpression` собирает из них составное выражение.
- Клиент работает уже не с длинным `Если`, а с объектным представлением простого DSL.

## Пример

#### DataProcessors.Application
```bsl
Context = New Structure;
Context.Insert("Status", "Ready");
Context.Insert("Amount", 1500);

StatusExpression = DataProcessors.EqualsStatusExpression.Create();
StatusExpression.Init("Ready");

AmountExpression = DataProcessors.AmountGreaterExpression.Create();
AmountExpression.Init(1000);

Rule = DataProcessors.AndExpression.Create();
Rule.Init(StatusExpression, AmountExpression);

If Rule.Interpret(Context) Then
    Message("Rule matched");
EndIf;
```

#### DataProcessors.EqualsStatusExpression
```bsl
Var ExpectedStatus;

#Region Public

Procedure Init(ExpectedStatus_) Export

    ExpectedStatus = ExpectedStatus_;

EndProcedure

Function Interpret(Context) Export

    Return Context.Get("Status") = ExpectedStatus;

EndFunction

#EndRegion
```

#### DataProcessors.AmountGreaterExpression
```bsl
Var Limit;

#Region Public

Procedure Init(Limit_) Export

    Limit = Limit_;

EndProcedure

Function Interpret(Context) Export

    Return Context.Get("Amount") > Limit;

EndFunction

#EndRegion
```

#### DataProcessors.AndExpression
```bsl
Var LeftExpression;
Var RightExpression;

#Region Public

Procedure Init(LeftExpression_, RightExpression_) Export

    LeftExpression = LeftExpression_;
    RightExpression = RightExpression_;

EndProcedure

Function Interpret(Context) Export

    Return LeftExpression.Interpret(Context)
        And RightExpression.Interpret(Context);

EndFunction

#EndRegion
```

## Где полезен в 1С

- для небольших DSL правил, фильтров и формул;
- для интерпретации настроек маршрутов, условий скидок и выражений отбора;
- когда предметная область уже оперирует собственным маленьким языком.

## Когда паттерн лишний

- если правило проще записать обычным кодом;
- если язык быстро становится слишком большим и требует полноценного парсера;
- если объектная модель выражений сложнее самой задачи.

Interpreter описывает грамматику небольшого языка и выполняет выражения этого языка над заданным контекстом.

Принципы ООП

Interpreter


# Итератор (`Iterator`)

`Iterator` позволяет обходить коллекцию, не раскрывая клиенту ее внутреннее устройство.

## Опора на ООП

`Iterator` опирается на [инкапсуляцию и интерфейсы](https://v8std.ru/patterns/principles/): внутренняя структура коллекции скрыта, а наружу выставлен только контракт обхода. Полиморфизм полезен, когда у одной коллекции есть несколько взаимозаменяемых обходчиков

## Что показывает пример на 1С

- `DataProcessors.WordsCollection` скрывает внутренний `Array` и отдает обходчик через `Iterator(Reverse = False)`.
- `DataProcessors.AlphabeticalOrderIterator` хранит `Collection`, `Position`, `Reverse` и предоставляет методы `Next()` и `Value()`.
- Клиент работает с обходом как с отдельным объектом и не зависит от способа хранения коллекции.

## Пример

#### DataProcessors.Application
```bsl
WordsCollection = DataProcessors.WordsCollection.Create();
WordsCollection.Add("Gamma");
WordsCollection.Add("Alpha");
WordsCollection.Add("Beta");

Iterator = WordsCollection.Iterator();

While Iterator.Next() Do
    Message(Iterator.Value());
EndDo;
```

#### DataProcessors.WordsCollection
```bsl
Var Collection;

#Region Public

Procedure Add(Value) Export

    Collection.Add(Value);

EndProcedure

Function Iterator(Reverse = False) Export

    Iterator = DataProcessors.AlphabeticalOrderIterator.Create();
    Iterator.Init(Collection, Reverse);
    Return Iterator;

EndFunction

#EndRegion

Collection = New Array;
```

#### DataProcessors.AlphabeticalOrderIterator
```bsl
Var Collection;
Var Reverse;
Var Position;

#Region Public

Procedure Init(Collection_, Reverse_) Export

    Collection = Collection_;
    Reverse = Reverse_;
    Position = ?(Reverse, -1, 0);

EndProcedure

Function Next() Export

    Try
        Position = Position + ?(Reverse, -1, 1);
    Except
        Return False;
    EndTry;

    Return True;

EndFunction

Function Value() Export

    Return Collection[Position];

EndFunction

#EndRegion
```

## Вариации

У `Iterator` есть близкая вариация, которую часто называют `генератором`.

### Классический итератор

В классическом варианте коллекция уже существует целиком, а итератор только знает, как по ней пройти.

- данные уже лежат в `Array`, `ValueTable`, выборке или другой структуре;
- итератор хранит позицию обхода;
- следующий элемент берется из уже подготовленного источника.

Именно такой вариант показан в примере выше: `WordsCollection` хранит массив, а `AlphabeticalOrderIterator` только управляет позицией и направлением обхода.

### Генератор

`Генератор` похож на итератор по внешнему контракту, но отличается источником данных.

- элементы не обязательно существуют заранее;
- следующий элемент может вычисляться, подгружаться или строиться на лету;
- объект хранит состояние не только обхода, но и самого процесса порождения значений.

Если упростить:

- `Iterator` отвечает на вопрос: как пройти уже существующую коллекцию?
- `Generator` отвечает на вопрос: как выдавать следующий элемент по требованию?

Для 1С это особенно полезно, когда не хочется сначала собирать всю коллекцию в память, а потом отдельно обходить ее.

Например:

- построчное чтение файла;
- поэтапная обработка большой выборки;
- чтение данных из внешнего API страницами;
- последовательное построение команд, задач или событий.

В 1С нет встроенного `yield`, как в Python или C#, поэтому генератор обычно оформляется как обычный объект с состоянием. Снаружи он нередко выглядит почти так же, как итератор:

```bsl
While Generator.Next() Do
    Process(Generator.Value());
EndDo;
```

Практический пример для 1С: получение списка через HTTP API с пагинацией.

Представим, что внешний сервис отдает клиентов не целиком, а страницами по `100` записей:

- первый запрос получает первую страницу и `nextPageToken`;
- второй запрос получает следующую страницу по этому токену;
- и так далее, пока страницы не закончатся.

Если собирать все страницы заранее, то внешний код сначала скачает весь список, потом сложит его в `Array` или `ValueTable`, и только после этого начнет обработку. Для больших выборок это лишняя память и лишняя задержка.

Генератор позволяет оформить это лениво:

- объект хранит `HttpClient`, `NextPageToken`, текущий буфер элементов и признак завершения;
- при `Next()` он проверяет, есть ли элемент в текущем буфере;
- если буфер пуст, делает следующий HTTP-запрос, заполняет буфер новой страницей и только потом отдает очередной элемент;
- если сервис больше не вернул `nextPageToken`, генератор понимает, что данные закончились.

Снаружи клиентский код при этом остается простым:

```bsl
While CustomersGenerator.Next() Do
    ProcessCustomer(CustomersGenerator.Value());
EndDo;
```

В этом и ценность генератора: клиент обходит поток элементов одинаково, хотя внутри объект может в любой момент:

- сходить в HTTP;
- дочитать следующую страницу;
- остановиться после последней страницы;
- прекратить работу раньше, если клиенту уже достаточно данных.

Но внутри он уже не берет элемент из готового массива, а сам:

- вычисляет следующее значение;
- дочитывает очередную порцию данных;
- при необходимости завершает выдачу, когда источник исчерпан.

Поэтому генератор можно считать ленивой вариацией итератора. В терминах архитектуры разница простая: итератор обходит, генератор порождает.

## Где полезен в 1С

- когда у одной коллекции нужно несколько вариантов обхода;
- когда коллекция сложная и не хочется раскрывать ее внутреннюю структуру наружу;
- когда обход должен быть независимым объектом с собственным состоянием.

## Когда паттерн лишний

- если достаточно обычного `Для Каждого`;
- если коллекция не меняет способ хранения;
- если отдельный объект обхода выглядит тяжелее самой задачи.

## Источник примера

- [zeegin/DesignPatterns: Iterator](https://github.com/zeegin/DesignPatterns/tree/master/Iterator)

Iterator позволяет обходить коллекцию, не раскрывая клиенту ее внутреннее устройство.

Принципы ООП

Iterator


# Посредник (`Mediator`)

`Mediator` централизует взаимодействие между объектами, чтобы они меньше зависели друг от друга напрямую.

## Опора на ООП

`Mediator` в первую очередь опирается на [инкапсуляцию](https://v8std.ru/patterns/principles/): связи и координация выносятся из участников в отдельный объект. Если посредников несколько и они взаимозаменяемы, подключается и полиморфизм общего контракта

## Что показывает пример на 1С

- В `CommonForms.Initialize` обработчики `UserOnChange`, `EmailOnChange`, `CountryOnChange`, `OK()` и `Cancel()` не вызывают друг друга напрямую, а только посылают `Notify(...)`.
- `NotificationProcessing(EventName, Parameter, Source)` - это обработчик стандартного события формы, который принимает уведомления и решает, как на них реагировать.
- `Notify(...)` в этом примере - не вспомогательная функция из проекта, а платформенный метод 1С.
- Это типичный 1С-сценарий для сложной формы, где много элементов могут влиять друг на друга.

## Пример

#### CommonForms.Initialize
```bsl
#Region FormEventHandlers

&AtClient
Procedure NotificationProcessing(EventName, Parameter, Source)

    If Source <> ThisObject Then
        Return;
    EndIf;

    If EventName = "UserChanged" Then
        // Do something...
    EndIf;

EndProcedure

#EndRegion

#Region FormHeaderItemsEventHandlers

&AtClient
Procedure UserOnChange(Item)

    Notify("UserChanged", Item, ThisObject);

EndProcedure

#EndRegion
```

## Особенность 1С

Для 1С здесь важен практический момент: паттерн `Mediator` во многих UI-сценариях уже реализован самой платформой.

- `Notify(...)` отправляет уведомление;
- `NotificationProcessing(...)` как стандартный обработчик события формы централизованно его обрабатывает;
- элементы формы не обязаны знать друг о друге напрямую.

То есть в управляемых формах 1С посредника часто не нужно проектировать с нуля отдельными классами. Обычно его уже дает платформа, и задача разработчика - использовать этот механизм по назначению:

- развязывать взаимозависимые элементы формы;
- не строить прямые вызовы между обработчиками;
- держать координацию в одном понятном месте.

Поэтому `Mediator` в 1С - это не только учебный паттерн из GOF, но и практический встроенный инструмент, который уже есть в платформе и которым нужно пользоваться осознанно.

## Где полезен в 1С

- в формах с большим количеством взаимосвязанных реквизитов;
- в мастерах, редакторах и настройках с несколькими зависимыми областями;
- когда хаотичные прямые вызовы между элементами формы уже стали неуправляемыми.

## Когда паттерн лишний

- если в форме только одна-две зависимости;
- если посредник превращается в огромный `NotificationProcessing` на сотни строк;
- если взаимодействие проще локализовать в одном обработчике.

## Источник примера

- [zeegin/DesignPatterns: Mediator](https://github.com/zeegin/DesignPatterns/tree/master/Mediator)

Mediator централизует взаимодействие между объектами, чтобы они меньше зависели друг от друга напрямую.

Принципы ООП

Mediator


# Снимок (`Memento`)

`Memento` сохраняет состояние объекта так, чтобы потом можно было откатиться назад, не раскрывая наружу все внутренние детали.

## Опора на ООП

`Memento` опирается прежде всего на [инкапсуляцию](https://v8std.ru/patterns/principles/): внутреннее состояние сохраняется во внешнем снимке без раскрытия всей внутренней структуры объекта

## Что показывает пример на 1С

- `DataProcessors.Editor` в `CreateSnapshot()` сам снимает свое состояние в `Structure`: простые значения, массив сериализованных строк таблицы и остальные данные состояния.
- `DataProcessors.SnapshotSpace` хранит снимки и умеет восстанавливать объект из выбранного источника снимка.
- Снимок хранит не ссылки на внутренние структуры редактора, а отдельное представление, пригодное для восстановления.
- Для 1С это особенно полезно там, где пользователь долго редактирует объект и ожидает отката или истории изменений.

## Пример

#### DataProcessors.Application
```bsl
Editor = DataProcessors.Editor.Create();
SnapshotSpace = DataProcessors.SnapshotSpace.Create();

SnapshotSpace.Save("draft-1", Editor.CreateSnapshot());

Editor.SetValue("Changed value");
Editor.SetData("Changed data");

SnapshotSpace.Save("draft-2", Editor.CreateSnapshot());

SnapshotSpace.Restore(Editor, "draft-1");
```

#### DataProcessors.Editor
```bsl
#Region Public

Procedure SetValue(NewValue) Export

    Value = NewValue;

EndProcedure

Procedure SetData(NewData) Export

    Data = NewData;

EndProcedure

Function CreateSnapshot() Export

    Self = New Structure;
    Self.Insert("Value", Value);
    Self.Insert("Data", Data);

    SerializedTable = New Array;
    For Each RowTable In Table Do
        Row = New Structure;
        Row.Insert("Key", RowTable.Key);
        Row.Insert("Val", RowTable.Val);
        SerializedTable.Add(Row);
    EndDo;

    Self.Insert("Table", SerializedTable);

    Return Self;

EndFunction

Procedure Restore(Snapshot) Export

    Value = Snapshot.Value;
    Data = Snapshot.Data;

    Table = New ValueTable;
    Table.Columns.Add("Key");
    Table.Columns.Add("Val");

    For Each SnapshotRow In Snapshot.Table Do
        Row = Table.Add();
        Row.Key = SnapshotRow.Key;
        Row.Val = SnapshotRow.Val;
    EndDo;

EndProcedure

#EndRegion
```

#### DataProcessors.SnapshotSpace
```bsl
Var Snapshots;

#Region Public

Procedure Save(Key, Snapshot) Export

    Snapshots.Insert(Key, Snapshot);

EndProcedure

Function GetSnapshot(Key) Export

    Return Snapshots.Get(Key);

EndFunction

Procedure Restore(Target, Key) Export

    Snapshot = GetSnapshot(Key);
    If Snapshot = Undefined Then
        Return;
    EndIf;

    Target.Restore(Snapshot);

EndProcedure

#EndRegion

Snapshots = New Map;
```

## Как обычно распределяются роли

У `Memento` важно не только хранение снимка, но и распределение ответственности.

- `Originator` сам снимает снимок с себя, потому что только он знает, какие данные действительно составляют его корректное внутреннее состояние;
- `Caretaker` или пространство снимков не лезет внутрь объекта, а только хранит версии и решает, какую из них нужно вернуть;
- восстановление часто инициируется именно из пространства истории, а не из самого объекта.

Поэтому на практике часто получается такая схема:

- объект делает `CreateSnapshot()`;
- пространство истории сохраняет снимок;
- позже пространство истории вызывает `Restore(...)` для выбранной версии.

Именно это показано в примере выше через `SnapshotSpace.Restore(Editor, "draft-1")`.

## Платформенный пример в 1С: история данных

У платформы 1С этот паттерн во многом уже реализован встроенным механизмом [История данных](https://v8.1c.ru/platforma/istoriya-dannyh/). Официальное описание платформы прямо говорит, что механизм хранит версии данных, позволяет сравнивать версии и восстанавливать данные в состояние выбранной версии.

Архитектурно это очень близко к `Memento`:

- прикладной объект выступает источником состояния;
- платформа хранит версии в собственном пространстве истории;
- восстановление идет не "изнутри" прикладного кода, а через механизм истории и переход на выбранную версию.

В документации это видно по нескольким точкам расширения и вызовам:

- в версии `8.3.11` появился глобальный доступ к механизму через свойство `ИсторияДанных` / `DataHistory`, то есть пространство снимков стало доступно из встроенного языка ([1C:DN](https://1c-dn.com/library/v8update_2079252602_new_functionality_and_changes/));
- для анализа двух версий платформа использует вызовы вида `ИсторияДанных.ПолучитьРазличиеВерсий(...)`, что прямо показывает работу не с живым объектом, а с двумя сохраненными снимками ([V8Update 8.3.14](https://downloads.v8.1c.ru/content/Platform/8_3_14_1494/1cv8upd_8_3_14_1494.htm));
- при переходе на выбранную историческую версию разработчик может подключиться через обработчик `ОбработкаФормированияПоВерсииИсторииДанных()`, а номер версии передается в форму через параметр `НомерВерсииПереходаНаВерсиюИсторииДанных` ([1C:DN, версия 8.3.13](https://1c-dn.com/library/v8update_2079252604_new_functionality_and_changes/));
- для конкретного объекта документация также описывает принудительное обновление истории через метод `DataHistory.UpdateHistory()`, если нужно добрать версии именно для выбранного объекта ([1C:DN, версия 8.3.13](https://1c-dn.com/library/v8update_2079252604_new_functionality_and_changes/)).

Из этого следует важный практический вывод для 1С: сам снимок обычно относится к объекту, а выбор версии и восстановление чаще относятся уже к пространству истории. Это ровно та роль, которую в паттерне `Memento` играет внешний хранитель снимков.

## Где полезен в 1С

- в редакторах документов и настроек;
- в мастерах, где нужен шаг назад;
- в сценариях локального undo без записи промежуточных состояний в базу.

## Когда паттерн лишний

- если состояние дешево пересчитать заново;
- если история не нужна;
- если копирование больших структур становится слишком дорогим.

## Источник примера

- [zeegin/DesignPatterns: Memento](https://github.com/zeegin/DesignPatterns/tree/master/Memento)

Memento сохраняет состояние объекта так, чтобы потом можно было откатиться назад, не раскрывая наружу все внутренние детали.

Принципы ООП

Memento


# Наблюдатель (`Observer`)

`Observer` позволяет издателю уведомлять подписчиков об изменениях, не зная деталей их реакции.

## Опора на ООП

`Observer` опирается на [интерфейсы и полиморфизм](https://v8std.ru/patterns/principles/): издатель работает с общим контрактом подписчика, а конкретные реакции распределены по разным наблюдателям. Инкапсуляция при этом удерживает слабую связанность между сторонами

## Что показывает пример на 1С

- `DataProcessors.EventManager` хранит подписчиков в `Map`, умеет `Subscribe()`, `Unsubscribe()` и `NotifySubscribers(Data)`.
- `DataProcessors.Subject` выполняет свою логику и затем вызывает `EventManager.NotifySubscribers("Success")`.
- `Documents.SalesOrder` и `Catalogs.Partner` реализуют метод `Update(Data)` и могут выступать подписчиками.

## Пример

#### DataProcessors.Application
```bsl
Subject = DataProcessors.Subject.Create();
SalesOrder = Documents.SalesOrder.CreateDocument();
Partner = Catalogs.Partner.CreateItem();

Subject.EventManager.Subscribe(SalesOrder);
Subject.EventManager.Subscribe(Partner);

Subject.BusinessLogic();
```

#### DataProcessors.EventManager
```bsl
Var Listeners;

Procedure Subscribe(Subscriber) Export

    Listeners.Insert(Subscriber, True);

EndProcedure

Procedure Unsubscribe(Subscriber) Export

    Listeners.Delete(Subscriber);

EndProcedure

Procedure NotifySubscrbers(Data) Export

    For Each Listener In Listeners Do
        Listener.Update(Data);
    EndDo;

EndProcedure

Listeners = New Map;
```

#### DataProcessors.Subject
```bsl
Var EventManager Export;

#Region Public

Function BusinessLogic() Export

    // Some logic

    EventManager.NotifySubscribers("Success");

EndFunction

#EndRegion

EventManager = DataProcessors.EventManager.Create();
```

#### Documents.SalesOrder
```bsl
Procedure Update(Data) Export

    Message("Sales order received event: " + Data);

EndProcedure
```

#### Catalogs.Partner
```bsl
Procedure Update(Data) Export

    Message("Partner received event: " + Data);

EndProcedure
```

## Где полезен в 1С

- для реакций на доменные события;
- для отделения публикации события от действий подписчиков;
- для уведомлений, синхронизации витрин, логирования и вспомогательных процессов.

## Когда паттерн лишний

- если получатель всегда один и известен заранее;
- если цепочка подписчиков скрывает важную бизнес-логику;
- если нет дисциплины в управлении подписками и жизненным циклом объектов.

## Источник примера

- [zeegin/DesignPatterns: Observer](https://github.com/zeegin/DesignPatterns/tree/master/Observer)

Observer позволяет издателю уведомлять подписчиков об изменениях, не зная деталей их реакции.

Принципы ООП

Observer


# Прототип (`Prototype`)

`Prototype` создает новый объект копированием уже подготовленного образца.

## Опора на ООП

`Prototype` в первую очередь опирается на [инкапсуляцию состояния](https://v8std.ru/patterns/principles/): объект сам определяет, что именно будет копироваться как образец. Полиморфизм полезен, если разные прототипы клонируются через единый контракт

## Что показывает пример на 1С

- В `CommonModules.Initialize` собирается `ValueTable`-прототип с заранее настроенными колонками `Name`, `Value` и `Hash`.
- Затем новые таблицы создаются не с нуля, а через `Prototype.Copy()`.
- Даже такой компактный пример хорошо показывает главную идею: сначала готовим шаблон, потом быстро клонируем его под конкретные задачи.

## Пример

#### CommonModules.Initialize
```bsl
#Region Public

Function Init() Export

    Prototype = New ValueTable();
    Prototype.Columns.Add("Name");
    Prototype.Columns.Add("Value");
    Prototype.Columns.Add("Hash");

    Table1 = Prototype.Copy();
    Table2 = Prototype.Copy();

EndFunction

#EndRegion
```

## Особенность платформы

Для 1С это важное практическое замечание: `Prototype` не всегда нужно реализовывать вручную, потому что для некоторых объектов платформы он уже встроен.

Самый понятный пример - `ValueTable`.

- разработчик один раз подготавливает таблицу-образец;
- затем получает новые экземпляры через `Copy()` / `Скопировать()`;
- то есть механизм клонирования уже дан самой платформой.

Поэтому в ряде задач `Prototype` в 1С - это не отдельный самописный паттерн, а грамотное использование уже готового платформенного API.

Именно поэтому `ValueTable` так хорошо подходит для объяснения паттерна:

- таблица выступает как подготовленный образец;
- `Copy()` создает новый объект на основе этого образца;
- клиентский код работает с копией, а не пересобирает структуру заново.

Практически это означает простое правило: если платформа уже умеет безопасно копировать объект, сначала стоит использовать встроенный механизм, а не проектировать собственный `Clone()`.

## Где полезен в 1С

- для повторного создания однотипных `ValueTable`, структур и настроек;
- когда шаблон содержит много заранее определенных полей;
- когда копирование проще и безопаснее, чем повторная ручная сборка.

## Когда паттерн лишний

- если объект примитивен и собирается в несколько строк;
- если копия должна сильно отличаться от оригинала;
- если трудно контролировать глубокое и поверхностное копирование.

## Источник примера

- [zeegin/DesignPatterns: Prototype](https://github.com/zeegin/DesignPatterns/tree/master/Prototype)

Prototype создает новый объект копированием уже подготовленного образца.

Принципы ООП

Prototype


# Заместитель (`Proxy`)

`Proxy` подставляет специальный объект вместо реального и контролирует доступ к нему.

## Опора на ООП

`Proxy` опирается на [интерфейсы, композицию и инкапсуляцию](https://v8std.ru/patterns/principles/): клиент работает с тем же контрактом, что и с реальным объектом, а детали контроля доступа, ленивой инициализации или удаленного вызова скрыты внутри заместителя

## Что показывает пример на 1С

- `DataProcessors.SalesReportProxy` выглядит для клиента как обычный сервис отчета.
- Внутри proxy проверяет доступ и создает тяжелый сервис только при первом реальном обращении.
- Клиент не знает, работает ли он с реальным объектом напрямую или через заместителя.

## Пример

#### DataProcessors.Application
```bsl
ReportService = DataProcessors.SalesReportProxy.Create();
Data = ReportService.GetData(DateFrom, DateTo);
```

#### DataProcessors.SalesReportProxy
```bsl
Var RealService;

#Region Public

Function GetData(DateFrom, DateTo) Export

    EnsureAccess();
    EnsureInitialized();
    Return RealService.GetData(DateFrom, DateTo);

EndFunction

#EndRegion

#Region Private

Procedure EnsureAccess()

    If Not Rights.CanRead("SalesReport") Then
        Raise "Access denied";
    EndIf;

EndProcedure

Procedure EnsureInitialized()

    If RealService = Undefined Then
        RealService = DataProcessors.HeavySalesReportService.Create();
    EndIf;

EndProcedure

#EndRegion
```

#### DataProcessors.HeavySalesReportService
```bsl
Function GetData(DateFrom, DateTo) Export

    Return New ValueTable;

EndFunction
```

## Близость к Decorator

`Proxy` очень похож на [Decorator](https://v8std.ru/patterns/gof/decorator/), потому что снаружи оба паттерна выглядят как обертка над тем же самым контрактом

В обоих случаях:

- клиент работает с тем же интерфейсом;
- внутри есть ссылка на другой объект;
- вызов делегируется дальше.

Но задача у них разная.

- `Decorator` отвечает на вопрос: как добавить новое поведение поверх существующего объекта?
- `Proxy` отвечает на вопрос: как контролировать доступ к объекту или отложить работу с ним?

Для 1С это различие удобно помнить так:

- логирование, ретраи и кэширование вокруг отправителя - это чаще `Decorator`;
- проверка прав, ленивая инициализация тяжелого сервиса или проксирование удаленного вызова - это чаще `Proxy`.

## Где полезен в 1С

- для ленивой инициализации тяжелых сервисов;
- для проверки прав и технического контроля доступа;
- для удаленных, внешних и потенциально дорогих по времени вызовов.

## Когда паттерн лишний

- если заместитель ничего не добавляет поверх реального объекта;
- если прямой вызов и так безопасен и дешев;
- если proxy начинает превращаться в фасад с новой бизнес-логикой.

Proxy подставляет специальный объект вместо реального и контролирует доступ к нему.

Decorator

Принципы ООП

Proxy


# Одиночка (`Singleton`)

`Singleton` дает единственную точку доступа к экземпляру или состоянию, которое в приложении должно быть уникальным.

## Опора на ООП

`Singleton` опирается на [инкапсуляцию](https://v8std.ru/patterns/principles/): паттерн прячет управление жизненным циклом и единственную точку доступа к экземпляру. Полиморфизм здесь обычно не является основной опорой

## Что показывает пример на 1С

- `CommonModules.Users.CurrentUser()` возвращает `SessionParameters.CurrentUser`.
- В мире 1С это чаще выглядит не как классический объект-одиночка, а как общий сервис-доступ к уникальному для сеанса состоянию.
- Условно `stateless singleton` в 1С можно увидеть в общем модуле: у него нет собственного экземпляра с управляемым жизненным циклом, но есть единая точка входа в логику.
- Платформенный пример более "настоящего" singleton-подобного объекта - глобальный объект `Metadata`, который уже предоставлен платформой и используется как единая точка доступа к метаданным.
- Такой пример полезен именно как архитектурная оговорка: в 1С паттерн часто проявляется через общие модули, глобальные объекты платформы и параметры сеанса, а не через конструкторы классов.

## Пример

#### CommonModules.Users
```bsl
Function CurrentUser() Export

    SetPrivilegedMode(True);
    Return SessionParameters.CurrentUser;

EndFunction
```

#### GlobalContext.Metadata
```bsl
ProductsMetadata = Metadata.Catalogs.Products;
ProductAttributes = ProductsMetadata.Attributes;
```

## Вариации singleton в 1С

В 1С почти всегда нужно сначала договориться, в каком именно масштабе объект должен быть "одним".

- `Singleton уровня бизнеса`:
  например, текущая учетная политика, активный релиз обмена или единственный набор правил, который в предметной области должен существовать в одном экземпляре.
- `Singleton уровня сеанса`:
  текущий пользователь, параметры сеанса, технический контекст конкретного подключения.
- `Singleton уровня процесса`:
  объект или кэш, живущий только внутри одного рабочего процесса `rphost`.
- `Singleton уровня приложения в целом`:
  единый экземпляр для всей информационной базы или всего кластера.

Проблема в том, что это четыре очень разные трактовки "одиночки". Без такой оговорки слово `Singleton` в 1С слишком легко вводит в заблуждение.

## Особенность платформы

С практической точки зрения собственный строгий singleton в прикладном коде 1С обычно не создать.

Причина в архитектуре платформы: в клиент-серверном варианте кластер серверов состоит из одного или нескольких рабочих процессов, а серверный код выполняется внутри `rphost` ([архитектура кластера](https://v8.1c.ru/platforma/arkhitektura-klastera/)). Платформа распределяет клиентские соединения по рабочим процессам кластера.

Из этого следует важное ограничение:

- даже если вы где-то закешировали "единственный экземпляр" в серверной памяти;
- это будет singleton максимум уровня конкретного рабочего процесса;
- но не всего приложения и не всего кластера.

То есть архитектурно вы не контролируете единый глобальный процесс, в котором исполняется весь код. В многопроцессной схеме нельзя просто объявить объект и считать, что он единственный "на всю систему".

Поэтому в 1С чаще встречаются такие практические варианты:

- `общий модуль` как stateless singleton, то есть единая точка входа без собственного состояния экземпляра;
- `SessionParameters` как singleton уровня сеанса;
- `Metadata` как платформенный глобальный объект, который уже дан средой выполнения;
- отдельные данные в базе как "бизнес-singelton", если уникальность гарантируется не памятью процесса, а моделью данных.

Для `Metadata` это особенно наглядно: разработчик не создает его вручную, а получает от платформы готовую единую точку доступа к метаданным. Это прямо видно и в официальных рекомендациях по получению метаданных, где используется глобальный объект `Metadata` и его методы ([1C:DN](https://1c-dn.com/library/getting_object_metadata/)).

## Где полезен в 1С

- для доступа к состоянию сеанса;
- для инфраструктурных сервисов, которые действительно должны быть единственными в контексте приложения;
- как единая точка чтения технического контекста.

## Когда паттерн лишний

- если это просто глобальная переменная под другим именем;
- если не определен уровень уникальности: бизнес, сеанс, процесс или приложение;
- если объект лучше передавать явно как зависимость;
- если единый доступ начинает скрывать слишком много неявных связей.

## Источник примера

- [zeegin/DesignPatterns: Singleton](https://github.com/zeegin/DesignPatterns/tree/master/Singleton)

Singleton дает единственную точку доступа к экземпляру или состоянию, которое в приложении должно быть уникальным.

Принципы ООП

Singleton


# Состояние (`State`)

`State` выносит поведение по состояниям в отдельные объекты или модули, чтобы не держать всю логику в одном длинном условии.

## Опора на ООП

`State` в первую очередь опирается на [полиморфизм](https://v8std.ru/patterns/principles/): поведение выбирается не через длинные ветвления, а через объект или модуль текущего состояния. Общий контракт состояний обычно оформляется через интерфейс

## Что показывает пример на 1С

- `Documents.Stocktaking` в `OnWrite()` получает реализацию через `Enums.StocktakingState.GetModule(State)` и делегирует поведение конкретному модулю состояния.
- Для каждого состояния есть свой модуль: `StocktakingStateDraft`, `StocktakingStateReady`, `StocktakingStateInProcess`, `StocktakingStateLocked`.
- В итоге документ знает текущее состояние, но не держит всю матрицу поведения внутри одного `Если`.

## Пример

#### Documents.Stocktaking
```bsl
Procedure OnWrite()

    Module = Enums.StocktakingState.GetModule(State);
    Module.DeclareSomething(ThisObject);

EndProcedure
```

#### Enums.StocktakingState
```bsl
#If Server Then

#Region Public

Function GetModule(State) Export

    If State = Draft Then
        Return StocktakingStateDraft;
    ElsIf State = Ready Then
        Return StocktakingStateReady;
    ElsIf State = InProcess Then
        Return StocktakingStateInProcess;
    ElsIf State = Locked Then
        Return StocktakingStateLocked;
    Else
        Raise "Unknown state";
    EndIf;

EndFunction

#EndRegion

#EndIf
```

## Близость к Strategy

`State` очень близок к [Strategy](https://v8std.ru/patterns/gof/strategy/), и это не случайно: оба паттерна строятся на полиморфизме и общем контракте

Снаружи они часто выглядят почти одинаково:

- есть контекст;
- есть набор реализаций с общим интерфейсом;
- контекст делегирует поведение выбранному объекту или модулю.

Но смысл у них разный.

- `Strategy` отвечает на вопрос: какой алгоритм мы хотим выбрать?
- `State` отвечает на вопрос: в каком состоянии сейчас находится объект и как он должен вести себя из-за этого?

То есть `Strategy` чаще выбирается снаружи как способ выполнения задачи, а `State` обычно определяется внутренним жизненным циклом самого объекта.

Для 1С это различие особенно полезно:

- если документ ведет себя по-разному в статусах `Черновик`, `Готов`, `Заблокирован`, это `State`;
- если один и тот же сценарий можно выполнить через `Email`, `SMS` или `Telegram`, это `Strategy`.

Связанный стандарт для 1С: [#std603: Требования к проведению документов](https://v8std.ru/std/603/). В нем прямо описана модель, где непроведенный документ выступает как черновик, а после проведения дальнейшее поведение может уточняться статусами уже проведенного документа. Это очень близкий практический пример применения `State` в прикладной архитектуре 1С

Поэтому `State` можно считать очень близким родственником `Strategy`, но с акцентом не на внешнем выборе алгоритма, а на внутреннем состоянии объекта.

## Где полезен в 1С

- в жизненном цикле документов и бизнес-процессов;
- когда набор состояний стабилен, а правила по ним заметно отличаются;
- когда ветвления по состоянию уже мешают читать объектный модуль.

## Когда паттерн лишний

- если состояний мало и логика тривиальна;
- если переходы постоянно меняются и разъезжаются по модулям;
- если из-за паттерна простая проверка превращается в сеть маленьких модулей без выгоды.

## Источник примера

- [zeegin/DesignPatterns: State](https://github.com/zeegin/DesignPatterns/tree/master/State)

State выносит поведение по состояниям в отдельные объекты или модули, чтобы не держать всю логику в одном длинном условии.

Strategy

Принципы ООП

Требования к проведению документов #std603

State


# Стратегия (`Strategy`)

`Strategy` прячет несколько взаимозаменяемых алгоритмов за единым контрактом.

## Опора на ООП

`Strategy` — один из самых прямых паттернов на [полиморфизм и интерфейсы](https://v8std.ru/patterns/principles/): контекст вызывает общий контракт стратегии и не знает, какой конкретный алгоритм сейчас подставлен

## Что показывает пример на 1С

- `DataProcessors.Context` умеет `SetStrategy(Strategy_)` и затем в `SendMessage(Message)` делегирует вызовы объекту стратегии.
- `EmailStrategy`, `SMSStrategy` и `TelegramStrategy` реализуют общий набор действий: `SetRecipient()` и `SendMessage()`.
- Это прямой и очень узнаваемый для 1С пример выбора канала без условий в клиентском коде.

## Пример

#### DataProcessors.Context
```bsl
Var Strategy;

#Region Public

Procedure SetStrategy(Strategy_) Export

    Strategy = Strategy_;

EndProcedure

Function SendMessage(Message) Export

    Strategy.SetRecipient(Partner);
    Strategy.SendMessage(Message);

EndFunction

#EndRegion
```

#### DataProcessors.EmailStrategy
```bsl
Var EmailAdress;

#Region Public

Function SetRecipient(Recipient) Export

    EmailAdress = Recipient.GetEmailAdressr(); // Implement it!

EndFunction

Function SendMessage(Message) Export

    // Implement it!
    Return True;

EndFunction

#EndRegion
```

## Где полезен в 1С

- для выбора канала уведомлений;
- для разных алгоритмов расчета, экспорта или подбора данных;
- когда бизнес-сценарий стабилен, а способы его выполнения меняются.

## Когда паттерн лишний

- если вариантов алгоритма мало и они почти не отличаются;
- если стратегии знают слишком много о контексте;
- если обычная функция с параметром читается проще.

## Источник примера

- [zeegin/DesignPatterns: Strategy](https://github.com/zeegin/DesignPatterns/tree/master/Strategy)

Strategy прячет несколько взаимозаменяемых алгоритмов за единым контрактом.

Принципы ООП

Strategy


# Шаблонный метод (`Template Method`)

`Template Method` фиксирует общий каркас алгоритма и оставляет отдельные шаги на конкретную реализацию.

## Опора на ООП

Классический `Template Method` строится на [наследовании и полиморфизме](https://v8std.ru/patterns/principles/): базовый алгоритм задается один раз, а отдельные шаги переопределяются в потомках. В 1С эту идею часто имитируют несколькими модулями с одинаковым публичным контрактом

## Что показывает пример на 1С

- В репозитории рядом лежат `CommonModules.COMConnector` и `CommonModules.RASConnector` с одинаковым публичным контрактом `GetSessions()` и `GetLocks()`.
- Пример компактный, но он хорошо показывает направление: есть общий сценарий работы с коннектором, а конкретные детали скрыты в специализированном модуле.
- Для 1С это типичная основа для унификации разных поставщиков или транспортов под одним API.

## Пример

#### CommonModules.COMConnector
```bsl
#Region Public

Function GetSessions() Export

    Return "Specific"; // Implement it!

EndFunction

Function GetLocks() Export

    Return "Specific"; // Implement it!

EndFunction

#EndRegion
```

#### CommonModules.RASConnector
```bsl
#Region Public

Function GetSessions() Export

    Return "Specific"; // Implement it!

EndFunction

Function GetLocks() Export

    Return "Specific"; // Implement it!

EndFunction

#EndRegion
```

## Где полезен в 1С

- когда несколько интеграций проходят одинаковую последовательность шагов;
- когда хочется выровнять контракт для нескольких коннекторов;
- когда общая часть алгоритма стабильна, а детали различаются.

## Когда паттерн лишний

- если общего каркаса фактически нет;
- если отличий между реализациями больше, чем общего;
- если проще использовать `Strategy`.

## Источник примера

- [zeegin/DesignPatterns: TemplateMethod](https://github.com/zeegin/DesignPatterns/tree/master/TemplateMethod)

Template Method фиксирует общий каркас алгоритма и оставляет отдельные шаги на конкретную реализацию.

Принципы ООП

Template Method


# Посетитель (`Visitor`)

`Visitor` выносит операции над набором объектов в отдельный объект-посетитель, чтобы не распихивать новые операции по самим элементам.

## Опора на ООП

`Visitor` опирается на [полиморфизм и интерфейсы](https://v8std.ru/patterns/principles/), а в классическом виде еще и на двойную диспетчеризацию: элемент принимает посетителя по общему контракту, а конкретная операция выбирается по типу элемента

## Что показывает пример на 1С

- `CommonModules.ProductsTableVisitor.GetTable(DocumentObject)` вызывает `DocumentObject.Accept(ThisObject)`, а конкретные действия живут в `DoForSalesOrder()` и `DoForPicklist()`.
- `DataProcessors.ManagerVisitor` по тому же принципу назначает менеджера разным типам документов.
- Один и тот же набор документов можно обходить разными посетителями, не меняя код самих операций в каждом месте заново.

## Пример

#### CommonModules.ProductsTableVisitor
```bsl
#Region Public

Function GetTable(DocumentObject) Export

    Return DocumentObject.Accept(ThisObject);

EndFunction

#EndRegion

#Region Internal

Function DoForSalesOrder(DocumentObject) Export

    Table = New ValueTable;
    Table.Columns.Add("Products");
    Table.Columns.Add("Quantity");

    For Each Row In DocumentObject.Products Do

        TableRow = Table.Add();
        TableRow.Products = Row.Products;
        TableRow.Quantity = Row.Quantity;

    EndDo;

    Return Table;

EndFunction

#EndRegion
```

#### DataProcessors.ManagerVisitor
```bsl
Var Manager;

#Region Public

Function SetManager(DocumentObject, Manager_) Export

    Manager = Manager_;
    Return DocumentObject.Accept(ThisObject);

EndFunction

#EndRegion

#Region Internal

Function DoForSalesOrder(DocumentObject) Export

    DocumentObject.ResponsiblePerson = Manager;
    Return True;

EndFunction

Function DoForPicklist(DocumentObject) Export

    DocumentObject.Manager = Manager;
    Return True;

EndFunction

#EndRegion
```

## Где полезен в 1С

- когда есть стабильный набор типов документов или объектов;
- когда над ними нужно регулярно добавлять новые операции;
- когда хочется собрать логику преобразований и выгрузок в отдельные объекты.

## Когда паттерн лишний

- если типов элементов много и они часто меняются;
- если операция всего одна;
- если двойная диспетчеризация делает код сложнее, чем простой условный разбор.

## Источник примера

- [zeegin/DesignPatterns: Visitor](https://github.com/zeegin/DesignPatterns/tree/master/Visitor)

Visitor выносит операции над набором объектов в отдельный объект-посетитель, чтобы не распихивать новые операции по самим элементам.

Принципы ООП

Visitor


# Контроллер (`Controller`)

`Controller` рекомендует принимать внешний запрос через объект-координатор сценария, а не размазывать orchestration по низкоуровневым участникам.

Важно: это не то же самое, что контроллер в `MVC`. В `GRASP` речь идет не о конкретном UI-слое и не об элементе фреймворка, а о роли объекта, который принимает внешний запрос и координирует выполнение use case.

## Опора на ООП

`Controller` опирается на [инкапсуляцию и низкую связанность](https://v8std.ru/patterns/principles/): координация use case отделяется от доменной логики и от деталей интерфейса

## Что показывает пример на 1С

- форма вызывает `SalesOrderController.Post(...)`, а не пытается сама загрузить объект, проверить данные и провести документ.
- контроллер управляет сценарием целиком: загрузка, заполнение, проверка, запись.
- доменные объекты и форма остаются проще и не смешивают UI с orchestration.

## Пример

#### CommonForms.SalesOrderForm
```bsl
&AtClient
Procedure PostCommand(Command)

    CommonModules.SalesOrderController.Post(Object.Ref);

EndProcedure
```

#### CommonModules.SalesOrderController
```bsl
Procedure Post(SalesOrderRef) Export

    SalesOrder = SalesOrderRef.GetObject();

    SalesOrderValidation.CheckBeforePosting(SalesOrder);
    SalesOrder.Write(DocumentWriteMode.Posting);

EndProcedure
```

## Близость к MVC Controller

Термины похожи, но смысл разный.

- `MVC Controller` обычно относится к архитектуре пользовательского интерфейса и управляет взаимодействием между представлением и моделью.
- `GRASP Controller` отвечает на вопрос: кто должен принять внешний системный запрос и собрать сценарий выполнения.

В 1С это особенно важно, потому что роль `GRASP Controller` могут играть очень разные точки входа:

- общий модуль, который координирует проведение документа;
- обработчик HTTP-сервиса;
- серверная процедура команды формы;
- координатор фонового задания.

То есть `GRASP Controller` не обязан быть "контроллером экрана". Это просто объект или модуль, которому доверили orchestration конкретного сценария.

## Где полезен в 1С

- в командах форм, HTTP-сервисах и фоновых заданиях;
- для сценариев `создать / проверить / провести / отправить`;
- когда важно отделить use case от деталей интерфейса и хранилища.

## Когда принцип применяют неправильно

- если контроллер превращается в "бог-объект";
- если он начинает хранить доменное состояние вместо координации сценария;
- если вся бизнес-логика выносится только в контроллер, а доменные объекты пустеют.

Controller рекомендует принимать внешний запрос через объект-координатор сценария, а не размазывать orchestration по низкоуровневым участникам.

Принципы ООП

Controller