Как я должен инкапсулировать доступ к базе данных?

10

Какие примеры хороших структур классов используются для управления доступом к базе данных? Я фанат инкапсуляции классов и предпочел бы, чтобы контейнеры (например, машина) не выполняли задачи базы данных.

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

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

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

Will03uk
источник
Вы рассматривали возможность использования ORM для связи классов с базами данных, такими как Wt :: Dbo ?
user52875

Ответы:

11

Я предпочитаю шаблон репозитория для инкапсуляции доступа к данным. Короче говоря, хранилище отвечает за загрузку всех данных, необходимых для конкретного объекта. Скажем, у вас есть объект Car, как в вашем примере. Но все атрибуты автомобиля, марки, модели, года, владельцев, характеристик (CD-плеер, 4wd и т. Д.) Хранятся в различных таблицах по всей базе данных. Хранилище определяет, как загружать и сохранять данные. Если требуется несколько небольших запросов, хорошо, но об этом должен знать только шаблон репозитория. Сервисному слою, вызывающему хранилище, нужно только знать, какой хранилище вызывать.

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

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

bwalk2895
источник
Все хорошо, за исключением последнего бита об интерфейсах, которых в c ++ не существует (если OP не подразумевает пометку c ++). Мне очень любопытно увидеть реализацию Repository Pattern в C ++, поскольку я хочу использовать ее с QT. Я поражен, что в Интернете нет ничего полезного = [
johnildergleidisson
6

Я использовал шаблон стратегии для инкапсуляции доступа к данным. Этот шаблон позволяет скрыть тип хранилища, которое вы используете за общим интерфейсом. В интерфейсе определите свои методы доступа к данным, не обращая внимания на тип хранилища (файл, база данных, Интернет). Затем для вашего текущего выбора хранилища в классе, реализующем интерфейс стратегии, реализуйте детали доступа к данным. Таким образом, ваше приложение не заботится об источнике данных, который вы используете.

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

Майк Л.
источник
Итак, вы бы добавили класс доступа для каждого типа или один большой класс для всех?
Will03uk
лично я также рассмотрел бы принятие явных приведений для всех данных, которые приходят с дикого на мой сервер / приложение.
user827992
+1 Мне нравится внешний вид этого шаблона, но я чувствую (в масштабе моего проекта), что управление каждым алгоритмом отдельно для базы данных будет сложным; хотя я обязательно буду использовать это в других приложениях. Лямбды должны дополнять это хорошо.
Will03uk
1

Это пример базы данных Factory pattern;

using System.Reflection;
using System.Configuration;

public sealed class DatabaseFactory
{
    public static DatabaseFactorySectionHandler sectionHandler = (DatabaseFactorySectionHandler)ConfigurationManager.GetSection("DatabaseFactoryConfiguration");

    private DatabaseFactory() { }

    public static Database CreateDatabase()
    {
        // Verify a DatabaseFactoryConfiguration line exists in the web.config.
        if (sectionHandler.Name.Length == 0)
        {
            throw new Exception("Database name not defined in DatabaseFactoryConfiguration section of web.config.");
        }

        try
        {
            // Find the class
            Type database = Type.GetType(sectionHandler.Name);

            // Get it's constructor
            ConstructorInfo constructor = database.GetConstructor(new Type[] { });

            // Invoke it's constructor, which returns an instance.
            Database createdObject = (Database)constructor.Invoke(null);

            // Initialize the connection string property for the database.
            createdObject.connectionString = sectionHandler.ConnectionString;

            // Pass back the instance as a Database
            return createdObject;
        }
        catch (Exception excep)
        {
            throw new Exception("Error instantiating database " + sectionHandler.Name + ". " + excep.Message);
        }
    }
}
theclai
источник