Интеграция программного обеспечения с 1С не всегда ограничивается работой только с одной информационной базой. Нередко возникает необходимость реализовать поддержку нескольких конфигураций или даже различных версий одной и той же конфигурации.
Если при работе хотя бы с двумя конфигурациями выполняются действия схожие между собой по содержанию, целесообразно прибегнуть к паттерну «Стратегия».
Паттерн «Стратегия» позволяет не просто определить некое семейство алгоритмов для решения некоторых задач. Его основное преимущество в том, что при его использовании эти алгоритмы становятся взаимозаменяемыми и их можно изменять независимо от тех объектов, которые их используют.
Паттерн «Стратегия» имеет три составляющих:
- Strategy (Стратегия). Базовый класс для Concrete Strategy. Объявляет интерфейс общий для всех поддерживаемых алгоритмов. Класс Context использует этот интерфейс для вызова конкретного алгоритма, реализованного в классе Concrete Strategy;
- Concrete Strategy (Конкретная стратегия). Реализует алгоритм использующий интерфейс, объявленный в классе Strategy;
- Context (Контекст). Класс, который использует алгоритмы «Стратегии». Хранит ссылку на объект класса Strategy и конфигурируется объектом класса Concrete Strategy. Также может определять интерфейс позволяющий объекту Strategy получить доступ к данным контекста.
Для того чтобы рассмотреть работу паттерна «Стратегия» применительно к нескольким конфигурациям 1С обратимся к примерам из цикла статей посвящённых работе с 1С в C# (часть 1 и часть 2).
Суть этих примеров подробно изложена в указанных статьях. Поэтому не станем их здесь описывать и перейдём сразу к постановке задачи и её решению с помощью паттерна «Стратегия».
Допустим необходимо реализовать в программе поддержку одновременно двух версий одной и той же конфигурации. При этом более новая версия имеет API для работы и, следовательно, работать с ней предпочтительно с его помощью, а более старая нет и работа с ней возможна только через объектную модель.
В обоих случаях выполняются одинаковые по своей сути действия получения и добавления данных, но используемые алгоритмы существенно отличаются. Исключение составляет только подключение к 1С через COM-сервер.
Таким образом, реализация получения и добавления данных для каждой конкретной конфигурации может быть выделена в отдельную «конкретную стратегию». В свою очередь сигнатуры соответствующих методов и подключение к 1С можно вынести в базовый класс «стратегию». В качестве «контекста» выступает класс программы, который отвечает за работу с 1С. В данном конкретном примере «контекстом» будет служить класс формы приложения.
Создадим абстрактный класс, который будет базовым для будущих «конкретных стратегий».
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ protected string _infoBase; public Strategy1C(string infoBase) { _infoBase = infoBase; } protected static dynamic Get1CConnection(string infoBase) { COMConnector comConnector = new COMConnector(); dynamic connection = comConnector.Connect("File=\""+infoBase+"\""); return connection; } abstract public DataTable Load(); abstract public void Add(string author); } |
Этот класс уже включает реализацию подключения к 1С и защищённое строковое поля для хранения пути к информационной базе.
Теперь необходимо реализовать «конкретные стратегии» для работы со старой и новой версиями конфигурации.
Класс для работы со старой версией.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class ConfOld : Strategy1C { public ConfOld(string infoBase) : base(infoBase) { } public override void Add(string author) { dynamic conn = Get1CConnection(_infoBase); dynamic addAuthor = conn.Справочники.Авторы.СоздатьЭлемент(); addAuthor.Наименование = author; addAuthor.Записать(); } public override DataTable Load(DataGridView grid) { dynamic conn = Get1CConnection(_infoBase); dynamic dataArray1C = conn.Справочники.Авторы.Выбрать(); DataTable dt = new DataTable(); dt.Columns.Add(); dt.Columns[0].ColumnName = "Наименование"; while (dataArray1C.Следующий == true) { dt.Rows.Add(dataArray1C.Наименование); } return dt; } } |
Класс для работы с новой версией.
1 2 3 |
{ public ConfNew(string infoBase) : base(infoBase) { |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
} public override void Add(string author) { dynamic conn = Get1CConnection(_infoBase); conn.Добавить(author); } public override DataTable Load(DataGridView grid) { dynamic conn = Get1CConnection(_infoBase); dynamic dataArray1C = conn.Загрузить(); DataTable dt = new DataTable(); dt.Columns.Add(); dt.Columns[0].ColumnName = "Наименование"; while (dataArray1C.Следующий == true) { dt.Rows.Add(dataArray1C.Наименование); } return dt; } } |
После этого остаётся только доработать класс формы.
Добавим закрытое поле класса Strategy1C.
1 |
private Strategy1C _strategy1C; |
1 2 3 4 |
private void SetStrategy(Strategy1C strategy) { _strategy1C = strategy; } |
Изменение стратегии выполняется просто.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private void radioButtonOld_CheckedChanged(object sender, EventArgs e) { //Работа со старой версией конфигурации if (((RadioButton)sender).Checked) { SetStrategy(new ConfOld("C:\\1С\\Тестовая\"")); } } private void radioButtonNew_CheckedChanged(object sender, EventArgs e) { //Работа с новой версией конфигурации if (((RadioButton)sender).Checked) { SetStrategy(new ConfNew("C:\\1С\\ТестоваяНовая")); } } |
Например, раньше при добавлении данных требовалась проверка версии конфигурации и соответствующее ветвление алгоритма.
1 2 3 4 5 6 7 8 9 10 |
if (isNewConf) { conn.Добавить(textBox1C.Text); } else { dynamic addAuthor = conn.Справочники.Авторы.СоздатьЭлемент(); addAuthor.Наименование = textBox1C.Text; addAuthor.Записать(); } |
1 2 3 4 5 |
dynamic dataArray1C = isNewConf ? conn.Загрузить() : conn.Справочники.Авторы.Выбрать(); while (dataArray1C.Следующий == true) { grid.Rows.Add(dataArray1C.Наименование); } |
Как при добавлении.
1 |
_strategy1C.Add(textBox1C.Text); |
1 |
grid.DataSource = _strategy1C.Load(); |
- Бизнес-логика, связанная с работой с 1С, теперь полностью отделена от объекта, который её использует. В данном примере от интерфейса пользователя. Алгоритмы, реализованные в классах «конкретных стратегиях» могут со временем изменяться, но изменения тех компонентов, которые их используют, при этом не потребуются;
- Работа со всеми поддерживаемыми конфигурациями стала полностью унифицированной. Это упрощает не только написание кода, но и расширение возможностей программы. При добавлении новой конфигурации потребуется только реализовать «конкретную стратегию» для работы с ней и её инициализацию. Всё остальное изменять не нужно;
- Вследствие предыдущих пунктов также значительно упрощается разработка самих объектов использующих данные из 1С и их модернизация. В частности, в том примере, который был рассмотрен в данной статье, первоначальный интерфейс пользователя на Windows Forms можно без особого труда заменить на более современный с использованием WPF.
К недостаткам использования данного паттерна можно отнести «традиционные» для большинства паттернов увеличение количества классов в приложении и усложнение его архитектуры. Однако, преимущества, которые даёт использование паттерна «Стратегия» применительно к работе с несколькими конфигурациями 1С их покрывают в более чем достаточной степени.
Список источников
- Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приёмы объектно-ориентированного проектирования. Паттерны проектирования.
- Стрелец Coder. Работа с 1С в C#. Способ 1. Работа с объектной моделью 1С
- Стрелец Coder. Работа с 1С в C#. Способ 2. Работа с API конфигурации
Добавить комментарий