Страницы

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

пятница, 10 января 2020 г.

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

#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); } }

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

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