Como devo encapsular o acesso ao banco de dados?

10

Quais são alguns exemplos de boas estruturas de classe usadas para gerenciar o acesso ao banco de dados? Sou fã do encapsulamento de classe e preferiria que os contêineres (por exemplo, carro) não executassem tarefas do banco de dados.

Eu também gostaria da capacidade de inserir facilmente coisas como um cache de banco de dados no futuro.

Costumo usar o padrão de classes de contêineres, completo com getters e setters para validação e acesso ao banco de dados executados por uma única classe singleton. Dito isto, isso geralmente se mistura entre os dois e se torna bastante confuso.

Desculpe se minha pergunta é difícil de entender; Não tenho certeza absoluta dos termos de bancos de dados. Por favor, não hesite em pedir esclarecimentos, se necessário.

Will03uk
fonte
Você considerou usar um ORM para vincular classes a bancos de dados, como Wt :: Dbo ?
user52875

Respostas:

11

Prefiro o Padrão de Repositório para encapsular o acesso a dados. Em poucas palavras, o repositório é responsável por carregar todos os dados necessários para um objeto específico. Digamos que você tenha um objeto Car, como no seu exemplo. Mas todos os atributos do carro, marca, modelo, ano, proprietários, recursos (CD player, 4x4, etc.) são armazenados em várias tabelas em todo o banco de dados. O repositório determina como carregar e salvar os dados. Se várias consultas menores forem necessárias, tudo bem, mas apenas o padrão do repositório precisa saber disso. A camada de serviço que chama o repositório precisa apenas saber qual repositório chamar.

Isso pode ser combinado com o padrão de unidade de trabalho . Portanto, no seu exemplo, a camada de serviço diria que precisa carregar uma entidade de carro, possui algum tipo de identificador exclusivo e envia esse identificador para o repositório. O repositório retorna a entidade do carro. Algum outro código manipula a entidade do carro e envia essa entidade de volta ao repositório para que possa ser salva.

Se você realmente deseja dar tudo de si, a camada do repositório apenas expõe interfaces, como ICarRepository. O repositório conteria uma fábrica que a camada de serviço usaria para obter a interface ICarRepository. Todo acesso ao banco de dados ficaria oculto por trás de uma interface, o que facilita muito o teste de unidade.

bwalk2895
fonte
Tudo bem, exceto o último bit sobre interfaces que em c ++ não existem (a menos que o OP não tenha a intenção de marcar c ++). Estou muito curioso para ver uma implementação de Padrão de Repositório em C ++, pois quero usá-la com o QT. Estou surpreso que não haja nada utilizável on-line = [
johnildergleidisson
6

Eu usei o padrão de estratégia para encapsular o acesso a dados. Esse padrão permite ocultar o tipo de armazenamento que você está usando atrás de uma interface comum. Na interface, defina seus métodos de acesso a dados sem levar em consideração o tipo de armazenamento (arquivo, banco de dados, web). Em seguida, para sua escolha de armazenamento atual, em uma classe que realiza a interface da estratégia, implemente os detalhes de acesso a dados. Dessa forma, seu aplicativo não se importa com a fonte de dados que você está usando.

Você também pode criar uma camada de serviço que usa a instância atual da estratégia de armazenamento de dados para definir mais detalhes específicos do aplicativo em vez de misturar o acesso a dados e a lógica de negócios.

Mike L.
fonte
Então, você adicionaria uma classe de acesso para cada tipo ou uma classe grande para todos?
21412 Will03uk
pessoalmente, eu também consideraria a adoção de castings explícitos para todos os dados que chegam do ambiente selvagem ao meu servidor / aplicativo.
user827992
+1 Gosto da aparência desse padrão, mas sinto (na escala do meu projeto) que gerenciar cada algoritmo separadamente para um banco de dados será difícil; embora eu certamente o use em outros aplicativos. Lambdas deve complementar isso bem.
21412 Will03uk
1

Este é um exemplo do padrão de fábrica do banco de dados;

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
fonte