Операции над объектами. Команды формирования объектов 1c общую команду обычные формы

Технологическая платформа «1С:Предприятие 8» используется для автоматизации решения широкого круга управленческих и учетных задач на самых разных предприятиях. При такой обширной сфере применения, естественно, могут возникать ситуации, когда функциональности стандартных команд недостаточно.
Для реализации дополнительной функциональности используется встроенный язык «1С:Предприятия». В основном эта функциональность реализована в обработчиках событий. Однако возникает и необходимость предоставить пользователям возможность интерактивного обращения к части реализованных функций из интерфейса.


Для решения этих задач в «1С:Предприятии» существует возможность создавать произвольную команду. В конфигурации произвольные команды представлены новым объектом конфигурации Команда . Объект конфигурации Команда предназначен для реализации в прикладном решении нестандартных функций с возможностью использования стандартных механизмов включения реализованного функционала
в командный интерфейс.
Технологическая платформа ничем не ограничивает состав произвольных команд и реализуемые ими функции. Все определяется требованиями к конкретному прикладному решению. При создании произвольной команды разработчик должен установить ее свойства, определяющие правила включения команды в интерфейс, и написать программный код, определяющий выполняемые командой действия. Этим произвольные команды отличаются от стандартных. Для последних и свойства, и выполняемые действия определены самой платформой.
В конфигурации произвольные команды могут быть реализованы или как независимые объекты – общие команды, или как подчиненные другим объектам.

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

Произвольные общие команды

Для примера рассмотрим, как сделать и настроить все свойства команду “Настроить сканер штрих-кодов” . На торговых предприятиях часто требуется автоматизировать процесс регистрации продаваемого товара. Для этого используются сканеры штрих-кода. Однако технологическая платформа ничего «не знает» об этих устройствах и не имеет средств работы с ними. Следовательно, для работы со сканером штрих-кода требуется подключать специальную программу – драйвер. Для подключения такого драйвера реализована общая произвольная команда Установить сканер штрих-кодов. Установив определенные значения свойств этой команды, разработчик обеспечил ее доступность пользователям.

Произвольная общая команда «Настроить сканер»

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

Еще одной распространенной задачей является получение печатных копий электронных документов. Состав документов, их структура определяются автоматизируемой прикладной задачей. Естественно, в платформе невозможно предусмотреть все многообразие документов и варианты их печатных форм. Для того чтобы «научить» документ «переносить» себя на бумагу, можно воспользоваться произвольной командой.
В демонстрационной базе для получения печатной формы документа РасходТовара создана подчиненная команда ПечатьРасходнойНакладной

Произвольная подчиненная команда «Печать расходной накладной»

Значения свойств команды Группа и Тип параметра команды определили расположение команды в командном интерфейсе – меню Печать командной панели формы документа, а процедура на встроенном языке обеспечила формирование печатной формы конкретного документа, ссылка на который передается в параметре команды.
Еще одним, достаточно специфическим вариантом использования произвольных команд является расширение или переопределение стандартной функциональности стандартной команды. Такие задачи возникают, например, вследствие требования сократить количество ручных операций или изменить стандартное поведение объектов.
Например, в нашей базе реализован объект-обработка Административный сервис . Команду открытия основной формы необходимо было расположить в панели навигации, а саму форму открывать в рабочей области основного окна приложения. Но стандартная функциональность этого объекта отличается от требуемой – команда открытия формы располагается в панели действий, а форма открывается в новом вспомогательном окне.
Для обеспечения требуемой функциональности у обработки снято свойство Использовать стандартные команды – стандартные команды нас не устраивают.

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

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

Произвольная подчиненная команда «Административный сервис»

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

Размещение по умолчанию в командном интерфейсе для произвольных команд определяется:

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

ВНИМАНИЕ!
Свойство команды Группа обязательно должно быть заполнено. В противном случае возникнет ошибка при обновлении конфигурации базы данных, и обновление не выполнится.

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

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

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

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

Состав допустимых типов параметров для параметризуемой команды определяется ее свойством «Тип параметра команды»

Сравнивая состав типов, заданных в свойстве команды, с типами реквизитов формы, система принимает решение о включении команды в ту или иную форму.

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


  • печать ценников на все товары по всем существующим видам цен,
  • печать ценников на все товары по одному виду цен.

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

Общая независимая команда
Функционал печати всех ценников на первый взгляд должен расширять возможности справочника Товары. Однако этот объект конфигурации описывает множество объектов данных информационной базы. Если мы реализуем команду как подчиненную справочнику, то мы «научим» каждый из объектов данных печатать ценники на все товары. А это уже лишнее. Объект данных должен быть ответственен только за себя. Поэтому команда будет общей.
Для добавления произвольной общей команды используем пункт Добавить контекстного меню узла Общие команды дерева конфигурации.

Результате будет добавлена общая команда, и для нее откроется окно свойств и окно редактирования модуля команды.

Свойства и модуль общей произвольной команды

В группе свойств Основные зададим значения свойств команды:


  • Имя – «ПечатьЦенниковТовары»;
  • Синоним – оставим автоматически сформированный синоним;
  • Комментарий – заполнять не будем.

Следующий шаг – выбор категории команды и группы для ее размещения по умолчанию. Наша команда для своего исполнения не требует параметров – она независимая. Команда выполняет действия по обработке данных, хранимых в информационной базе, с целью получения набора ценников, а не изменяет контекст решения какой-либо задачи. Следовательно, для команды установим категорию Панель действий. А в какой группе она будет отображаться? Логичнее всего поместить ее в группу Сервис .
Поэтому для свойства Группа открываем окно со списком групп и выбираем элемент панель действий.Сервис .

ПРИМЕЧАНИЕ
Обратите внимание на свойства Тип параметра команды , Режим использования параметра и изменяет данные – они недоступны для заполнения. Свойства предназначены для описания параметризуемой команды и становятся доступны только при выборе группы с категориями Панель навигации формы или Командная панель формы.

Задание места размещения произвольной общей команды

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

Включение произвольной общей команды в подсистемы

Наша команда доступна пользователям с ролью Администратор за счет установленного свойства роли Устанавливать права для новых объектов. От нас требуется обеспечить ее доступность и для роли Менеджер по ценам.
Как и для других объектов конфигурации, для общей команды настройку доступности можно выполнить в окне редактирования роли.
А можно – в уже открытом окне Дополнительно на закладке Права. В списке Роли выбираем настраиваемую роль и в списке Права устанавливаем право Просмотр для созданной команды

Настройка доступности команды для роли «Менеджер по ценам»

Настройка видимости команды по ролям для произвольной независимой команды выполняется аналогично настройке для стандартных команд – в редакторе командного интерфейса.
Наша команда по умолчанию должна быть видима пользователю с ролью Менеджер по ценам, а от пользователя с ролью Администратор ее необходимо скрыть. Для этого в редакторе командного интерфейса подсистемы Ценообразование снимем флажок общей видимости в колонке Видимость . Это обеспечит нам невидимость команды для всех ролей, в том числе и вновь создаваемых. А для роли Менеджер по ценам явным образом установим флажок в соответствующей колонке.
Сохраним конфигурацию, запустим приложение от имени пользователя Менеджер по ценам и выберем раздел Управление ценами.

Настройка видимости произвольной общей команды

В командном интерфейсе команда Печать ценников товары доступна в разделе Управление ценами (за счет указания принадлежности к подсистеме Ценообразование). Команда размещена в группе Сервис панели действий (за счет указания соответствующего значения свойства Группа).

Таким образом, для произвольной общей независимой команды:

  • размещение в командном интерфейсе по умолчанию определяется значением свойства Группа;
  • включение в раздел командного интерфейса определяется принадлежностью к соответствующей подсистеме;
  • доступность для пользователя определяется значением права Просмотр.

Команду мы создали. А как рассказать пользователю о том, какие действия выполняет произвольная команда? Ответ очевиден – описать назначение команды в документации к прикладному решению. Также назначение команды можно описать во встроенной электронной справке. Для работы со справочной информацией предназначены свойства общей команды из группы Справочная информация (рис.

Справочная информация по произвольной общей команде

Однако поиск описания команды в документации или встроенной справке – процесс длительный. Можно помочь пользователю быстро вспомнить назначение команды, выбрав для нее говорящее представление. Произвольная общая команда в командном интерфейсе представляется своим свойством Синоним. Сейчас команда представлена текстом «Печать ценников товары» , и это представление достаточно информативно. Но в дальнейшем мы добавим в прикладное решение еще одну команду печати ценников – по виду цен. Поэтому стоит подумать над таким представлением команды, которое подскажет пользователю, какой вариант печати ценников будет выполнен. Скажем, это будет «Печать всех ценников» .
Еще одним способом напоминания пользователю о назначении команды является использование свойства Подсказка. Текст, заданный в этом свойстве, отображается во всплывающей подсказке при наведении указателя мыши на команду. Для свойства Подсказка зададим текст «Печать ценников на все товары по всем видам цен» . В результате изменения значений свойств Синоним и Подсказка представление команды
в командном интерфейсе изменилось.

Измененное представление команды, подсказка, справка

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

Паттерн Command (Команда)

Название и классификация паттерна

Команда - паттерн поведения объектов.

Назначение паттерна Command

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

Используйте паттерн Command, если:

  • система управляется событиями. При появлении такого события (запроса) необходимо выполнить определенную последовательность действий;
  • необходимо параметризировать объекты выполняемым действием, ставить запросы в очередь или поддерживать операции отмены (undo) и повтора (redo) действий;
  • нужен объектно-ориентированный аналог функции обратного вызова в процедурном программировании.

Пример событийно-управляемой системы - приложение с пользовательским интерфейсом. При выборе некоторого пункта меню пользователем вырабатывается запрос на выполнение определенного действия (например, открытия файла).

Описание паттерна Command

Паттерн Command преобразовывает запрос на выполнение действия в отдельный объект-команду. Такая инкапсуляция позволяет передавать эти действия другим функциям и объектам в качестве параметра, приказывая им выполнить запрошенную операцию. Команда - это объект, поэтому над ней допустимы любые операции, что и над объектом.

Интерфейс командного объекта определяется абстрактным базовым классом Command и в самом простом случае имеет единственный метод execute(). Производные классы определяют получателя запроса (указатель на объект-получатель) и необходимую для выполнения операцию (метод этого объекта). Метод execute() подклассов Command просто вызывает нужную операцию получателя.

В паттерне Command может быть до трех участников:

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

Структура паттерна Command

Структура паттерна Command показана на рис. 63.

Сначала клиент создает объект ConcreteCommand, конфигурируя его получателем запроса. Этот объект также доступен инициатору. Инициатор использует его при отправке запроса, вызывая метод execute(). Этот алгоритм напоминает работу функции обратного вызова в процедурном программировании - функция регистрируется, чтобы быть вызванной позднее.

Паттерн Command отделяет объект, инициирующий операцию, от объекта, который знает, как ее выполнить. Единственное, что должен знать инициатор, это как отправить команду. Это придает системе

receiver->action();

Рис. 63. UML-диаграмма паттерна Команда

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

Участники

Command - Команда: объявляет интерфейс для выполнения операции.

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

Client - клиент: создает объект класса ConcreteCommand и устанавливает его получателя.

Invoker - инициатор: обращается к команде для выполнения запроса.

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

Отношения

Клиент создает объект ConcreteCommand и устанавливает для него получателя.

Инициатор Invoker сохраняет объект ConcreteCommand.

Инициатор отправляет запрос, вызывая операцию команды Execute. Если поддерживается отмена выполненных действий, то ConcreteCommand перед вызовом Execute сохраняет информацию о состоянии, достаточную для выполнения отката.

aReceiver aClient

Г"| newCommand(aReceiver)

StoreCommand(aCommand)

Рис. 64. UML-диаграмма последовательностей паттерна Команда

Объект ConcreteCommand вызывает операции получателя для выполнения запроса.

На рис. 64 видно, как Command разрывает связь между инициатором и получателем (а также запросом, который должен выполнить последний).

Результаты применения паттерна Команда

Команда разрывает связь между объектом, инициирующим операцию, и объектом, имеющим информацию о том, как ее выполнить.

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

Из простых команд можно собирать составные. В общем случае составные команды описываются паттерном Компоновщик.

Добавлять новые команды легко, поскольку никакие существующие классы изменять не нужно.

Реализация паттерна Command

Рассмотрим реализацию паттерна Command на примере игры «Шахматы». Имитируем возможность выполнения следующих операций:

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

void open(string file) { cout

void save(string file) { cout

void make_move(string move) { cout

string getPlayerInput(string prompt) { string input; cout > input; return input;

// Базовый класс class Command {

virtual ~Command() 0 virtual void execute() = 0; protected:

Command(Game* p): pgame(p) {} Game * pgame;

class CreateGameCommand: public Command

CreateGameCommand(Game * p): Command(p) {} void execute() { pgame->create();

class OpenGameCommand: public Command

OpenGameCommand(Game * p) : Command(p) {} void execute() { string file_name;

file_name = getPlayerInput("Enter file name:"); pgame->open(file_name);

class SaveGameCommand: public Command

SaveGameCommand(Game * p) : Command(p) {} void execute() { string file_name;

file_name = getPlayerInput("Enter file name:"); pgame->save(file_name);

class MakeMoveCommand: public Command

MakeMoveCommand(Game * p): Command(p) {} void execute() {

// Сохраним игру для возможного последующего отката pgame->save("TEMP F1LE"); string move;

move = getPlayerInput("Enter your move:"); pgame->make_move(move);

class UndoCommand: public Command

UndoCommand(Game * p) : Command(p) {} void execute() {

// Восстановим игру из временного файла pgame->open("TEMP_FILE");

// Имитация действий игрока vector v;

// Создаем новую игру

v.push_back(new CreateGameCommand(&game));

// Делаем несколько ходов

v.push_back(new MakeMoveCommand(&game));

// Последний ход отменяем v.push_back(new UndoCommand(&game));

// Сохраняем игру

v.push_back(new SaveGameCommand(&game));

for (size_t i=0; i execute();

for (size_t i=0; i

Вывод программы:

Save game in TEMPFILE Enter your move: E2-E4 Make move E2-E4 Save game in TEMP FILE Enter your move: D2-D3 Make move D2-D3 Open game from TEMP_FILE Enter file name: gamel.sav Save game in game 1 .sav

Достоинства паттерна Command

Придает системе гибкость, отделяя инициатора запроса от его получателя.

Родственные паттерны

Паттерн Компоновщик можно использовать для реализации макрокоманд.

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

Команда, которую нужно копировать перед помещением в список истории, ведет себя, как Прототип.

Общие команды — механизм платформы, предназначенный для описания часто используемых команд в конфигурации 1С 8.3.

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

В команду можно передать параметры, например, из формы какого объекта он вызывается.

Настройка и свойства общей команды в 1С

Добавить новую команду и настроить общую форму достаточно просто, рассмотрим этот процесс подробнее:

Получите 267 видеоуроков по 1С бесплатно:

  • Группа — расположение будущей команды на интерфейсе.
  • Тип параметра команды — определяет набор объектов, в котором будет отображаться будущая команда.
  • Режим использования параметра — задает возможность передать в качестве параметра команды одно или несколько значений.
  • Изменяет данные — если галка установлена, то при выполнение команды форма будет пересчитана с сервера.
  • Модуль команды — обработчик выполнения команды, выполняется на клиенте.

Пример модуля команды:

&НаКлиенте Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды) Если ЗначениеЗаполнено(ПараметрКоманды) Тогда ОткрытьФорму("ОбщаяФорма.СтруктураПодчиненности" , Новый Структура("ОбъектОтбора" , ПараметрКоманды) , ПараметрыВыполненияКоманды. Источник, ПараметрыВыполненияКоманды. Источник. КлючУникальности, ПараметрыВыполненияКоманды. Окно) ; КонецЕсли ; КонецПроцедуры

Здесь ПараметрКоманды — объект, вызывающий команду. А в ПараметрыВыполненияКоманды структура, в которой описан Источник (вызываемая Форма), Окно (ОкноКлиентскогоПриложения), Уникальность, указывает, искать ли уже открытую форму или нет.

Группы команд 1С

Большая часть современного ПО разрабатывается группами программистов. Кто-то отвечает за пользовательский интерфейс, кто-то за ядро, а кто-то за дополнительные модули. Чтобы работа всех этих людей не пропала даром, нужно грамотно объединить разные части проекта, не забыв при этом о возможном его расширении. Для этого нам пригодится паттерн проектирования «Команда», который инкапсулирует в себе исполнителя задачи и ее условия.

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

Казалось бы, всё просто: накодил большой блок switch, который при нажатии разных сочетаний кнопок вызывает ту или иную функцию какого-либо модуля, и радуйся. Но, во-первых, такой подход не отличается гибкостью. Когда проект пополнится новыми модулями или hotkeys, нам придется менять код этого раздутого оператора ветвления, из-за чего он впоследствии раздуется еще больше. А во-вторых, начальство большими красными буквами написало, что у пользователя должна быть возможность переназначить эти горячие клавиши. Таким образом, жестко забить команды в код switch у нас точно не выйдет.

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

Интерфейсы модулей, доступные для обращения через HotKeys class Calculator { public: void runCalc(); void closeCalc(); } class Printer { public: void printDocument(); void printImage(); void printEmail(); } class Browser { public: void runBrowser(); void closeBrowser(); } // И дальше много всяких разных классов

Паттерн «Команда»

Отбросив лирику, перейдем к изучению паттерна «Команда», который должен помочь нам в этом нелегком деле. Для начала следует разобраться, что мы имеем. У нас есть множество модулей с самыми разнообразными API. Также у нас есть окошко, в котором пользователь может выбрать из списка одну из команд, предоставляемых модулями, и закрепить ее за определенным сочетанием клавиш.

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

Для такой изоляции нам нужно определить объект команды, а следовательно, и соответствующий интерфейс. Он достаточно прост и определяет всего один метод execute(), который должен выполнять метод какого-либо из модулей.

Интерфейс объекта «Команды» class Command { public: void execute() = 0; }

Для определения конкретного объекта мы просто объявляем новый класс, который наследует интерфейс Command, и определяем его метод execute(). Допустим, мы хотим создать команду, запускающую калькулятор. Для этого мы создадим класс RunCalcCommand, который будет наследовать интерфейс Command, и переопределим execute() для вызова метода runCalc() модуля Calculator.

Команда запуска калькулятора class RunCalcCommand: public Command { Calculator *calc; public: RunCalcCommand(Calculator *excalc) { calc = excalc; } void execute() { calc->runCalc(); } }

Если внимательно присмотреться к коду команды, то можно заметить, что в конструкторе класса RunCalcCommand передается указатель на модуль Calculator. Это сделано для большей гибкости. В будущем у нас может появиться класс ModernCalculator, вызывающий продвинутую версию расчетной программы. Используя композицию, то есть не фиксируя исполнителя жестко в коде, мы увеличиваем изолированность кода, что в будущем позволит сэкономить время.

Теперь нужно связать команду с хоткеем. Для этого можно создать массив указателей на объекты класса Command. Размер массива будет равен количеству поддерживаемых горячих клавиш. Каждый элемент массива команд сопоставляется с определенным хоткеем. В нашем случае для этого можно преобразовать значение кнопки, которая вместе с Ctrl составляет какое-либо из возможных сочетаний, в числовое значение. Если бы мы использовали сочетания, например, от ctrl-a до ctrl-k, то нам бы потребовался немного другой подход.

Получив код нажатого сочетания клавиш и преобразовав его в соответствующий индекс для массива команд, мы можем смело вызывать метод execute() объекта, указатель на который находится в нужной ячейке массива.

Назначение команды на hotkey и ее запуск // Код инициализации команды и хоткея const int comCount = 10; Command* commands; Calculator *calc = new Calculator(); commands = new RunCalcCommand(calc); // Код в обработчике нажатий клавиатуры // Получаем нажатые клавиши hotkey = catchHotKey(); // Преобразовываем их в индекс и запускаем команду int index = hotkey2index(hotkey); commands->execute();

Всё довольно-таки просто. Можно определить еще несколько наследников Command, которые будут выполнять определенные действия, и связать их с горячими клавишами. Такая архитектура позволяет полностью отделить клиент от исполнителей. Обработчик клавиатуры понятия не имеет, какой модуль обрабатывает команду и что именно он делает, а модули, в свою очередь, не подозревают, что обращение к ним осуществляется с помощью hotkeys, а не каким-то другим способом.

Отмена команды

Вроде бы всё хорошо, но мы совсем забыли про отмену. Сочетание клавиш ctrl-z должно откатывать действие последней команды. Реализовать отмену довольно просто, хотя на первый взгляд может показаться, что это совсем не так. Для этого мы немного изменим интерфейс Command.

Класс Command, поддерживающий отмену class Command { public: void execute() = 0; void undo() = 0; } class RunCalcCommand: public Command { Calculator *calc; public: RunCalcCommand(Calculator *excalc) { calc = excalc; } void execute() { calc->runCalc(); } void undo() { calc->closeCalc(); } }

Мы просто добавили метод undo(), который должен быть переопределен в классах-наследниках. Программист сам решает, какую именно функцию модуля будет использовать метод отмены. Так, метод undo() для RunCalcCommand будет обращаться к функции closeCalc() модуля Calculator. Нам также потребуется немного подправить код обработчика клавиатуры.


Wikipedia про паттерн «Команда». Обработчик клавиатуры с поддержкой отмены // Код инициализации команды и хоткея const int comCount = 10; Command* commands; Command *lastCommand = new NoCommand(); Calculator *calc = new Calculator(); commands = new RunCalcCommand(calc); // Код в обработчике нажатий клавиатуры // Получаем нажатые клавиши HotKey *hotkey = catchHotKey(); // Если это отмена, то вызываем соответствующий метод if (hotkey->str() == "ctrl-z") { lastCommand->undo(); } // Обработка остальных сочетаний

Здесь мы просто запоминаем в переменной lastCommand указатель на последнюю использованную команду и при нажатии ctrl-z вызываем соответствующий метод. Дополнительно мы прибегаем к небольшому трюку, используя объект пустой команды NoCommand. Код этого класса выглядит так:

Пустая команда NoCommand class NoCommand: public Command { public: void execute() {}; void undo() {}; }

Такие объекты-заглушки используются довольно часто. Они нужны, чтобы уменьшить количество проверок нулевого указателя. Если бы lastCommand был равен NULL, то перед вызовом метода undo() нам пришлось бы проверять корректность значения этого указателя, что нежелательно, так как однажды мы можем забыть это сделать, в результате чего программа с грохотом упадет. Такие же объекты-заглушки рекомендуется использовать и для остальных хоткеев, которым не назначены соответствующие команды.

Кстати, код обработчика клавиатуры можно модифицировать так, чтобы он поддерживал отмену не только последней операции, но и вообще всех цепочек выполненных команд. Для этого вместо простого указателя на последнюю команду следует использовать стек. При обработке какого-либо хоткея в стек будет добавляться указатель на соответствующую команду. Таким образом, мы получим полную историю вызовов команд, что позволит нам последовательно все отменить при помощи ctrl-z.


Макрокоманды

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

Макрокоманда class MacroCommand: public Command { Command *commands; int comCount; public: MacroCommand(Command *comArray, int elemCount) { commands = comArray; comCount = elemCount; } void execute() { for (int i = 0; i < comCount; i++) { commands[i]->execute(); } } void undo() { for (int i = 0; i < comCount; i++) { commands[i]->undo(); } } }

Как видно, класс MacroCommand является наследником Command и переопределяет всё те же методы execute и undo. В интерфейсе от обычной команды он отличается лишь конструктором. Этот класс принимает не указатель на исполняющий модуль, а массив указателей на простые команды. Код execute() просто проходит по элементам массива и вызывает каждый из них. Так же ведет себя и undo(). Обработчик клавиатуры при обращении к объекту команды понятия не имеет, макрос это или обычная единичная операция, - главное, что все они предоставляют функции execute() и undo().

Расширенные возможности паттерна «Команда»

Наш паттерн можно использовать не только для обработки горячих сочетаний клавиш. С помощью него можно организовывать очереди запросов. Допустим, что у нас есть пул потоков, который должен выполнять некоторые задания. Все задания представляют собой объекты, реализующие уже знакомый нам интерфейс Command. Команды выстраиваются в очередь, к которой последовательно обращаются потоки. Они забирают команды из этой очереди и запускают их методы execute(). Потокам не важно, что делают эти объекты - главное, чтобы они поддерживали вызов execute(). Команды можно сохранять на жестком диске и восстанавливать их оттуда. Для этого следует немного расширить их базовый интерфейс.

Command с поддержкой сохранения и загрузки class Command { public: void execute() = 0; void undo() = 0; void load() = 0; void store() = 0; }

Метод load() предназначен для сохранения команды в журнале, а store() - для ее восстановления оттуда. Код этих методов может использовать механизмы сериализации языка программирования, если таковые в нем есть. Такая функциональность нужна для работы с большими объемами данных, которые невозможно сохранять после совершения с ними каждого действия. При сбое программы мы сможем загрузить сохраненные команды и последовательно применить их к имеющейся копии данных для приведения этих данных в актуальное состояние.

Заключение

С помощью паттерна «Команда» нам удалось полностью отделить разношерстные модули-исполнители от клиента - обработчика клавиатуры. Если в будущем в программе появятся новые модули, мы сможем легко добавить соответствующие команды и назначить им горячие клавиши. Это будет простое, изящное и эффективное решение.

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

Войдите на сайт как ученик

Войдите как ученик, чтобы получить доступ к материалам школы

Создание конфигураций 1С: добавляем команду

Продолжаем изучение азов создания конфигураций на 1С.

Вернёмся в конфигуратор и откроем дерево конфигурации:

Где находится форма у обработки

Откроем окно настроек обработки "УдалениеУволенныхСотрудников":

Открылось окно с закладками во многом повторяющими закладки из справочника "Сотрудники". Это совершенно нормально, ведь настройки объектов в конфигураторе во многом похожи друг на друга.

На этот раз нас интересует закладка "Формы" - откроем её:

Найдите на этой закладке объект с именем "Форма" - это и есть наше визуальное представление обработки:

Давайте откроем его двойным щелчком:

Открылось окно со множеством панелей. И нам сейчас очень важно для будущих уроков разобраться что здесь за что отвечает.

Меняем код на встроенном языке 1С для формы

Ну прежде всего обратим внимание в самый низ открывшегося окна. Там мы найдём две закладки "Форма" и "Модуль".

Закладка "Форма" - это и есть визуальное представление. Сейчас на форме находится всего лишь одна кнопка "Удалить сотрудников".

Закладка "Модуль" - это код на встроенном языке 1С, который содержит процедуры и функции, определяющие поведение формы для пользователя.

Попробуем переключиться на закладку "Модуль":

Здесь присутствует всего одна процедура с именем "УдалитьСотрудников". Очевидно именно она и вызывается при нажатии на кнопку.

Код процедуры сейчас свёрнут - нажмём на плюсик, чтобы развернуть его (не поместился на рисунке справа):

Точно, так и есть. Вот оно место, где выдаётся сообщение о том, что я не стал писать код этой обработки

Изменим этот код следующим образом:

Снова запустим режим 1С:Предприятие (меню "Отладка"->"Начать отладку"), откроем обработку и нажмём кнопку "Удалить сотрудников":

И получим то самое сообщение, которое написали только что:

Закладка "Элементы" у формы

Вернёмся в конфигуратор в нашу форму на закладку "Форма":

Обратите внимание на закладку "Элементы" в верхней части формы. Содержимое этой закладки дублирует визуальное представление формы. Вы читаете ознакомительную версию урока, полноценные уроки находятся . Можно сказать ещё, что всё, что вы видите в визуальной части формы можно найти на закладке "Элементы".

Например, чтобы открыть свойства кнопки "Удалить сотрудников" на форме - найдём эту кнопку на закладке "Элементы" и сделаем на ней двойной щелчок:

Откроется окно со свойствами кнопки:

Давайте установим для кнопки заголовок "БАХ":

Форма теперь будет выглядеть так:

Закладка "Реквизиты" у формы

Теперь перейдём к закладке "Реквизиты":

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

Но если бы мы перешли на аналогичную закладку в форме справочника "Сотрудники", то обнаружили бы тут реквизиты "ДатаРождения", "НомерПаспорта" и "СерияПаспорта". Это означало бы, что мы можем их использовать в коде модуля формы.

Закладка "Команды" у формы

Заключительная закладка, которую мы рассмотрим сегодня это закладка "Команды":

Вообще все команды, которые вы видите на одной из закладок ("Команды формы", "Стандартные команды" и "Глобальные команды"), можно смело перетаскивать на закладку "Элементы" и они "волшебным образом" превратятся в кнопки на форме.

Как вы понимаете, нажатие на эти кнопки, будет приводить к выполнению этих команд.

Ну, например, перейдём на закладку "Стандартные команды" и перетащим на закладку "Элементы" команду "Закрыть":

На форме появилась кнопка закрыть. Запустим 1С:Предприятие (меню "Отладка"->"Начать отладку"), откроем обработку и убедимся, что кнопка работает:


Вернёмся в конфигуратор в форму обработки и перейдём на закладку "Команды формы":

На этой закладке мы видим команды формы, которые мы определили сами. В том числе мы можем видеть здесь команду, которую я определил ещё в самом начале с именем "УдалитьСотрудников".

Откроем свойства этой команды (двойным щелчком).

Нас интересует прежде всего поле "Действие", нажмите на кнопку с лупой рядом с ним:

Нас перенесло в процедуру "УдалитьСотрудников" в модуле формы. Это означает, эта команда и эта процедура связаны между собой. И выполнение команды (например, при нажатии на кнопку, в которую она превратилась) приведёт к выполнению кода процедуры.

Добавляем новую команду для формы

Давайте создадим ещё одну команду формы. Для этого вернёмся на закладку "Команды формы" и нажмём зелёную кнопку с плюсом:

Откроем её свойства и установим имя "Привет", а затем нажмём на лупу рядом с полем "Действие":

Нас спрашивают какой именно вид обработчика мы хотим создать.

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

Выбираем вариант "НаКлиенте" и нажимаем "ОК":

Нас перенесло в модуль формы в автоматически созданную процедуру "Привет". Теперь эта процедура связана с командой формы "Привет":

Давайте напишем в ней вывод строки привет пользователю:

Но как нам теперь заставить выполнять команду (а значит и процедуру) "Привет"? Для этого вернёмся на закладку "Команды формы" и перетащим наш "Привет" на форму, так как мы это сделали ранее с командой "Закрыть":

На форме появилась ещё одна кнопка. Запустим 1С:Предприятие, откроем обработку и нажмём на кнопку "Привет". Должно получиться вот так:

Вводим от пользователя имя и говорим ему привет

А теперь давайте поставим себе такую задачу. Нужно, чтобы пользователь ввёл своё имя, мы нажали на кнопку и вывелось, например, "Привет, Алексей".

Чтобы мы смогли разместить на форме элементы для ввода данных нам потребуется реквизит формы (закладка "Реквизиты") с которым этот элемент будет связан.
Так как закладка "Реквизиты" у нас практически пустая - создадим новый реквизит.

Переходим на закладку "Реквизиты" и нажимаем зелёную кнопку плюс:

В окне свойств этого реквизита установим имя "Имя" и тип "Строка":

После этого перетащим уже привычным образом реквизит "Имя" на закладку "Элементы":

Ага, на форме появился элемент для ввода строки! Что нам и требовалось

Запустим 1С:Предприятие, откроем обработку и попробуем туда ввести своё имя:

Всё получилось, но вот нажатие на кнопку "Привет" пока работает по-прежнему.

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

Перепишем её таким образом, чтобы к строке "Привет, " добавлялось значение реквизита "Имя", который связан с элементом ввода на форме:

Теперь снова запустим 1С:Предприятие, обработку, введём своё имя и нажмём кнопку "Привет":

То что надо!

Команды, элементы, реквизиты, объект... вы ещё не запутались?

Думаю запутались Спешу вас успокоить, что не стоит переживать по этому поводу. Со временем ситуация прояснится.

А пока я попробую более простыми словами описать вам эти составные части любой формы. А вы после этого можете ещё раз перечитать урок - уверен, многое станет более понятным.

Итак, форма - это визуальное представление нашей программы: кнопки, надписи, рисунки, списки... да много чего! Всё это ЭЛЕМЕНТЫ формы.

Кнопка - элемент. Надпись - элемент. Поле ввода - тоже элемент

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

Элементы позволяют нам некоторым образом взаимодействовать с формой: читать, нажимать, проматывать и т.п.

Вот например.

Кнопка

Очевидно, что кнопка не может быть сама по себе. Когда пользователь нажимает на неё - должно произойти некоторое действо, задуманное программистом.

Это действо и называется командой

Команды бывают встроенные (закладки "Стандартные команды" и "Глобальные команды") и те, что программист придумывает сам (закладка "Команды формы").

Ну встроенные команды - они на то и встроенные. Что их действие придумано уже до нас. Мы можем лишь перетащить эти команды на форму и превратить их в кнопки. К таким командам относится, например, команда закрытия формы. Нам не нужно ничего программировать - достаточно перетащить стандартную команду "Закрыть" на форму и всё

А команда формы - это команда придуманная нами самими. Это та команда, которую мы сами добавили на закладку "Команды формы", затем нашли в её свойствах пункт "Действие", ткнули на него и запрограммировали код на встроенном языке в автоматически созданном обработчике в модуле формы (например, команда "Привет" из этого урока).

Ну в общем вы поняли: команда - это некоторое действие, запрограммированное на языке 1С (или уже встроенное в программу). А кнопка - это визуальный элемент формы, который при своём нажатии запускает связанную с собой команду.

Надпись

Это просто текст на форме. У такого элемента есть свойство "Заголовок", значение которого мы задаём в редакторе и оно отображается в виде текста.

Поле

А вот это уже интересно. Потому что это такой особый элемент, который не сам по себе (как надпись), а обязательно должен быть связан с какими-то данными или по-другому РЕКВИЗИТАМИ (закладка "Реквизиты").

Можно сказать, что реквизит - это переменная формы, которую мы объявляем на закладке "Реквизиты", а элемент связанный с реквизитом ("Поле") это его представление на форме. Но у самого реквизита есть только имя , тип и значение.

Ну вот представьте, что у нас на форме есть поле для ввода числа. Если бы не было реквизитов - как бы мы из кода узнали, какое число ввёл пользователь? Мы бы обратились к элементу ввода по имени и прочитали некоторое его свойство, отвечающее за значение введённое пользователем.

Так вот в 1С так нельзя. Тут (начиная с "управляемых" форм) представление данных отделено от самих данных.

Получается, что элемент ввода - это элемент формы. А число, которое вводит пользователь, хранится не в самом элементе, а в реквизите, который связан с этим элементом.

Ещё раз. Реквизит - это именно данные (строка, число, дата) . Не визуальное представление (надпись с текстом, поле для ввода числа, поле для ввода даты). Визуальным представлением реквизита является как раз элемент формы "Поле".

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

И наоборот. Пользователь вводит на форме значения в элементы ввода (числа, текст, даты) и значения реквизитов также меняются автоматически.

Какие преимущества даёт такое отделение элементов формы от данных (реквизитов)? Большие! Программист создает нужные ему реквизиты (для хранения, отображения и ввода некоторых полей на форме) и пишет программный код работая только с этими реквизитами (данными). Он совершенно не задумывается пока как всё это будет выглядеть на форме. Оно ему и не надо! Он пока пишет только программный код.

А уже затем он перетаскивает эти реквизиты на форму, реквизиты превращаются в визуальные элементы формы, он их как-то настраивает, распихивает по закладкам и прочее. В общем на этом этапе (визуального проектирования формы) он работает уже только с элементами. При этом во много раз снижается вероятность сломать уже написанный код.

Ещё пример. Пусть у нас есть реквизит "Возраст" с типом "Число". Этот реквизит хранит только само число, больше ничего. Он не отвечает за то, как будет выглядеть это число, и в каком месте (или на какой закладке) будет располагаться элемент ввода на форме, связанный с этим числом. Реквизит - это только число! Обращаясь к реквизиту мы никак не сможем поменять размер элемента ввода на форме, цвет, видимость... За всё это отвечает не реквизит, а элемент! Меняя реквизит, мы меняем только число, которое отображается в элементе ввода на форме.

В общем: РЕКВИЗИТ - это переменная формы . Поэтому все данные храним в реквизитах (переменных), а для их вывода на форму (или ввода с формы) используем элементы. Именно это отделение логики от представления позволяет 1С с легкостью отображать одни и те же формы на разных клиентах: "толстый", "тонкий", "веб-браузер".

Чтобы обратиться к реквизиту "Возраст" из модуля формы достаточно сразу использовать его имя:

Что такое Объект?

Ну и, наконец, объект. Судя по-тому, что он находится на закладке "Реквизиты" - это тоже реквизит. Всё верно. Но он особенный.

Этот реквизит мы не создаём - он сам появляется на закладке "Реквизиты". В случае с обработкой он пустой, но если бы мы программировали форму какого-нибудь справочника, то реквизит объект как раз и представлял бы из себя поля этого справочника из базы.

Рядом с ним бы появился плюсик и мы могли бы его раскрывать и перетаскивать отдельные его части на форму и они бы также превращались в элементы.

Пройдите тест

Начать тест

1. Форма обработки может содержать

2. Форма у обработки находится на закладке

Просмотров