Страницы

Поиск по вопросам

четверг, 21 февраля 2019 г.

Подскажите, как лучше оформить класс

В общем, хочу сделать класс, который будет импортировать DataTable в Access или MS SQL.
Класс будет иметь 1 метод: Append, которому на вход будет подаваться DataTable, а метод в свою очередь будет загружать DataTable в нужный пункт назначения.
Разумеется, внутренняя логика Append у Access может сильно отличаться, например:
В Access нужно следить за тем, что бы файл не был выше 2гб, иначе бд поломается В MS SQL можно воспользоваться BulkCopy, а в Access потребуется какой-то самопал.
Соответственно и конструкторы могут сильно отличаться по параметрам, например:
Строки подключения между двумя типами будут отличаться В MS SQL можно использовать или не использовать транзакцию В Access можно восстанавливать или не восстанавливать БД после каждого Append.
Как лучше всего все это оформить?
UPD:
Есть у меня некоторые предположения, как это можно сделать, но не знаю на сколько это верно.
Сделать интерфейс:
public interface IApend { void Append(DataTable dt); }
Реализовать его в 2 ух классах, каждый из которых будет содержать в себе собственную логику импорта:
public class ImporterToSQL: IApend { //Какие-то поля public void Append(DataTable dt) { //Какая-то логика } } public class ImporterAccess : IApend { //Какие-то поля public void Append(DataTable dt) { //Какая-то логика } }
И создать такой класс:
public class Importer { IApend Concrete;
public Importer(IApend concrete) { Concrete = concrete; } static Importer ImporterToSQL(object v,object v2,object v3) { return new Importer(new ImporterToSQL(object v, object v2, object v3)); } static Importer ImporterToSQL(object v, object v2) { return new Importer(new ImporterToSQL(object v, object v2)); } static Importer ImporterToAccess(object v, object v2, object v3) { return new Importer(new ImporterAccess(object v, object v2, object v3))); } static Importer ImporterToAccess(object v, object v2) { return new Importer(new ImporterAccess(object v, object v2)); } public void Append(DataTable dt) { Concrete.Append(dt); } }
На сколько это верно и на сколько хорошо этот подход будет работать в плане добавления нового функционала(Например, мне приспичило добавить импорт из SQL в Access)? Это , вроде, зовется фабрикой классов?


Ответ

Вы на правильном пути. Общий интерфейс для конвертации -- это правильно. Только я бы назвал его чуть поконкретнее, хотя б IImporter какой-нибудь.
Что касается создания конкретных экземпляров и вашего класса Importer. Для создания действительно нужна фабрика. Однако она у вас получилась не в чистом виде. Смысла оборачивать экземпляр в ваш Importer нет -- у вас уже есть интерфейс. Просто создавайте нужные экземпляры и возвращайте их. А клиент уже будет вызывать напрямую методы:
public static class ImporterFactory { static IImporter CreateSQLImporter(object v, object v2, object v3) { return new ImporterToSQL(v, v2, v3); }
static IImporter CreateAccessImporter(object v, object v2, object v3) { return new ImporterAccess(v, v2, v3); }
// и так далее }
В качестве параметров для конструкторов у вас наверняка выступают специфичные для каждой реализации данные: например, строка подключения для SQL и путь к mdb файлу для Access. В идеале клиент вообще не должен знать что-то о деталях реализации, а фабрика должна содержать один метод на тип. Все детали о реализации прячутся в фабрике, которая подсовывает нужные параметры напрямую из конфигурации (например, из app.config). При этом клиент вообще ничего не будет знать о реализации:
public static class ImporterFactory { static IImporter CreateImporter(object v) { var generalConfig = GetGeneralConfig(); if (generalConfig.Type == "sql") { var config = GetSqlImporterConfig(); return new ImporterToSQL(config.ConnectionString, v); } else if (generalConfig.Type == "access") { var config = GetAccessImporterConfig(); return new ImporterToAccess(config.ConnectionString, v); } } }
Такой подход будет очень хорошо работать, если вам потребуется импорт в новое место. Вы просто напишете еще одну реализацию IImporter, внести изменения в ImporterFactory и все. Все клиентs, которые используют IImporter, менять не придется. В случае же отдельных методов вам придется менять вызывающий код, который управляет тем, какой метод вызвать.

Что касается импорта из SQL в Access, то такую задачу описанным выше способом не решить. Выше мы рассмотрели только задачу "как данные, представленные в универсальном виде (DataTable), записать в конкретное место". Задачу импорта из SQL в Access можно переформулировать как "как извлечь данные из одного места и записать их в другое место". Такую задачу полезно разбить на два шага и ввести промежуточный этап. Какой? Правильно, получение данных в универсальном виде. Таким образом, шага будет два:
Импорт из источника с преобразованием в универсальный вид Экспорт в другое место
Соответственно, два основных контракта будут выглядеть как-то так:
interface IImporter { DataTable Import(); }
interface IExporter { void Export(DataTable data); }
Реализация второго шага у вас уже есть, правда сейчас она называется импортом. При наличии двух шагов будет логичнее назвать их с т.з. того, как данные движутся относительно вашего приложения. Вы импортируете (загружаете) данные из одного источника и экспортируете (выгружаете) их в другое место.
Реализация первого шага будет аналогична -- различные реализации интерфейсов, фабрика.
Для осуществления процесса конвертации вам понадобится некоторая общая сущность, которая будет соединять шаги. Она может принимать в качестве зависимостей как фабрики, так и экземпляры импортера/экспортера:
public class DataConverter // придумайте имя поудачнее, пожалуйста :) { private readonly IImporter _importer; private readonly IExporter _exporter;
public DataConverter(IImporter importer, IExporter exporter) { _importer = importer; _exporter = exporter; }
public void Convert() { var data = _importer.Import(); _exporter.Export(data); } }

Комментариев нет:

Отправить комментарий