#c_sharp #net #проектирование
В общем, хочу сделать класс, который будет импортировать 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)? Это , вроде, зовется фабрикой классов?
Ответы
Ответ 1
Вы на правильном пути. Общий интерфейс для конвертации -- это правильно. Только я бы назвал его чуть поконкретнее, хотя б 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); } }
Комментариев нет:
Отправить комментарий