¿Cómo debo encapsular el acceso a la base de datos?


10

¿Cuáles son algunos ejemplos de buenas estructuras de clase utilizadas para administrar el acceso a la base de datos? Soy un fanático de la encapsulación de clase y preferiría que los contenedores (por ejemplo, el automóvil) no realicen tareas de base de datos.

También me gustaría la posibilidad de colocar fácilmente cosas como una caché de base de datos en el futuro.

A menudo tomo el patrón de clases de contenedor, completo con getters y setters para validación y acceso a la base de datos realizado por una sola clase singleton. Dicho esto, esto a menudo se mezcla entre los dos y se vuelve bastante confuso.

Lo siento si mi pregunta es difícil de entender; No estoy absolutamente seguro de los términos relacionados con las bases de datos. No dude en solicitar una aclaración si es necesario.


¿Consideró usar un ORM para vincular clases a bases de datos, como Wt :: Dbo ?
user52875

Respuestas:


11

Prefiero el patrón de repositorio para encapsular el acceso a datos. En pocas palabras, el repositorio es responsable de cargar todos los datos necesarios para un objeto específico. Digamos que tiene un objeto Car, como en su ejemplo. Pero todos los atributos para el automóvil, marca, modelo, año, propietarios, características (reproductor de CD, 4wd, etc.) se almacenan en varias tablas a lo largo de la base de datos. El repositorio determina cómo cargar y guardar los datos. Si se necesitan múltiples consultas más pequeñas, está bien, pero solo el patrón del repositorio necesita saberlo. La capa de servicio que invoca el repositorio solo necesita saber qué repositorio invocar.

Eso se puede combinar con el patrón de unidad de trabajo . Entonces, en su ejemplo, la capa de servicio diría que necesita cargar una entidad de automóvil, tiene algún tipo de identificador único y envía ese identificador al repositorio. El repositorio devuelve la entidad del automóvil. Algún otro código manipula la entidad del automóvil y la envía de vuelta al repositorio para que se pueda guardar.

Si realmente quiere ir todo, la capa del repositorio solo expondría interfaces, como ICarRepository. El repositorio contendría una fábrica que la capa de servicio usaría para obtener la interfaz ICarRepository. Todo el acceso a la base de datos estaría oculto detrás de una interfaz, lo que hace que las pruebas unitarias sean mucho más fáciles.


Todo bien, excepto el último bit sobre interfaces que en c ++ no existen (a menos que el OP no signifique etiquetar c ++). Tengo mucha curiosidad por ver una implementación de patrón de repositorio en C ++ ya que quiero usarla con QT. Estoy sorprendido de que no haya nada utilizable en línea = [
johnildergleidisson

6

He usado el Patrón de estrategia para encapsular el acceso a datos. Este patrón le permite ocultar el tipo de almacenamiento que está utilizando detrás de una interfaz común. En la interfaz, defina sus métodos de acceso a datos sin tener en cuenta el tipo de almacenamiento (archivo, base de datos, web). Luego, para su elección de almacenamiento actual, en una clase que comprende la interfaz de estrategia, implemente los detalles de acceso a datos. De esta manera, su aplicación no se preocupa por la fuente de datos que está utilizando.

También puede crear una capa de servicio que use la instancia de estrategia de almacenamiento de datos actual para definir más detalles específicos de la aplicación en lugar de combinar el acceso a los datos y la lógica empresarial.


Entonces, ¿agregaría una clase de acceso para cada tipo o una clase grande para todos?
Will03uk

personalmente, también consideraría la adopción de castings explícitos para cada dato que provenga de la naturaleza a mi servidor / aplicación.
user827992

+1 Me gusta el aspecto de este patrón, pero siento (en la escala de mi proyecto) que manejar cada algoritmo por separado para una base de datos será difícil; aunque ciertamente usaré esto en otras aplicaciones. Las lambdas deben complementar esto bien.
Will03uk

1

Este es un ejemplo del patrón Factory de la base de datos;

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);
        }
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.