0 4.9K ru

Паттерны архитектуры источников данных (PoEAA)

Row Data Gateway (Шлюз к данным записи)

Паттерн проектирования Row Data Gateway

Объект выступает в роли шлюза к отдельной записи в источнике данных. Один экземпляр на одну запись.

Встраивание кода доступа к БД в объекты, хранящиеся в памяти, может привести в некоторым неудобствам. Во-первых, если ваши объекты содержат бизнес-логику, добавление взаимодействия с БД увеличит сложность. Вскоре тестирование станет неудобным, если объекты, хранящиеся в памяти, завязаны на БД. Тесты станут медленнее из-за доступа к БД. Объект шлюза к записи представляется в точности, как запись в БД, но при этом даёт возможность доступа посредством штатных средств языка программирования. Все подробности доступа к БД скрыты за этим интерфейсом.Пример: объект шлюза PersonGateway, данные о записи из таблицы person и методы insert и update, которые позволяют прозрачно работать с записью.Качественная реализация этого паттерна на PHP существует в частности в Zend Framework в классе Zend_Db_Table_Row.

Active Record (Активная запись) 

Один объект управляет и данными, и поведением. Большинство этих данных постоянны и их надо хранить в БД. Этот паттерн использует подход - хранение логики доступа к данным в объекте сущности.

Объект является "обёрткой" одной строки из БД или представления, включает в себя доступ к БД и логику обращения с данными.

Пример: объект "Person" содержит данные об одной персоне и методы: добавить, обновить или удалить. По сути паттерн продвигает идею когда модель и логика сосредоточены в 1 классе.

рассмотрим диаграмму класса "Person":

Active record (активная запись

 

Пример реализации Active record на C#

using System.Data;
using System.Data.SqlClient;

namespace ActiveRecord
{
    public class Customer
    {
        private const string CONNECTION_STRING =
            "Data Source=(local);Initial Catalog=DesignPatterns;Integrated Security=True";

        public int ID { get; set; }
        public string Name { get; set; }
        public bool IsPremiumMember { get; set; }

        public Customer(int id, string name, bool isPremiumMember)
        {
            ID = id;
            Name = name;
            IsPremiumMember = isPremiumMember;
        }

        // Этот статический метод действует как фабрика объектов для объектов Customer,
        // чтение значений из базы данных и создание объекта.
        //  Итак, код для получения клиента из базы данных может быть:
        //
        //    Customer.GetByID(123);
        //
        public static Customer GetByID(int id)
        {
            using(SqlConnection connection = new SqlConnection(CONNECTION_STRING))
            {
                connection.Open();

                using(SqlCommand command = connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;

                    command.CommandText = "SELECT TOP 1 * FROM [Customer] WHERE [ID] = @ID";
                    command.Parameters.AddWithValue("@ID", id);

                    SqlDataReader reader = command.ExecuteReader();
                    if(reader.HasRows)
                    {
                        reader.Read();

                        string name = (string) reader["Name"];
                        bool isPremiumMember = (bool) reader["IsPremiumMember"];

                        return new Customer(id, name, isPremiumMember);
                    }
                }
            }

            return null;
        }

        public void Save()
        {
             // Этот метод должен обрабатывать INSERT и UPDATE.
             // Или вам нужно создать две отдельные функции и вызывать их при необходимости.

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

        public void Delete()
        {
            using(SqlConnection connection = new SqlConnection(CONNECTION_STRING))
            {
                connection.Open();

                using(SqlCommand command = connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = "DELETE FROM [Customer] WHERE [ID] = @ID";
                    command.Parameters.AddWithValue("@ID", ID);
                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

Table Data Gateway (Шлюз к данным таблицы)

Описание:

Объект выступает в качестве шлюза между данными в приложении и в БД. Один объект работает сразу со всеми записями в таблице.

Шлюз к данным таблицы содержит весь SQL для доступа к таблице или представлению (view): selects, inserts, updates, deletes. Идея состоит в том что Table Data Gateway предоставляет "прокси-класс" (шлюз) для доступа к каждой таблицы БД.

Как это работает:

Шлюз табличных данных имеет простой интерфейс, обычно состоящий из нескольких методов поиска для получения данных из базы данных и методов обновления, вставки и удаления. Каждый метод отображает входные параметры в вызов SQL и выполняет SQL для соединения с базой данных. Шлюз табличных данных, как правило, не имеет состояния, поскольку его роль заключается в передаче данных с базы в бизнес логику и обратно.

По сути Table Data Gateway паттерн это прослойка которая отделяет SQL от бизнесс логики.

Когда нужно использовать Table data gateway?

  • Шлюз таблиц данных, вероятно, является наиболее простым в использовании интерфейс БД паттерном , поскольку он так хорошо отображается на таблицу базы данных или тип записи. Это также создает естественную точку для инкапсуляции точной логики доступа источника данных.
  • Используя Table Data Gateway, у вас будет по одному шлюзудля каждой таблицы в базе данных. Однако для очень простых случаев у вас может быть один шлюз табличных данных, который обрабатывает все методы для всех таблиц.
  • Одним из преимуществ использования шлюза табличных данных для инкапсуляции доступа к базе данных является то, что один и тот же интерфейс может работать как для использования SQL запросов для работы с БД, так и для использования хранимых процедур.

Пример:

UML диаграмма будет выглядеть так: 

table data gataway паттерн диаграмма

Создаем POCO объект

 public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Gender { get; set; }
        public int Age { get; set; }
    }

Создаем класс PersonGateway, который содержит весь SQL для доступа к одной таблице или представлению: выбирает, вставляет, обновляет и удаляет. Другой код вызывает свои методы для всего взаимодействия с базой данных.

public class PersonGateWay
    {
        public void Update(string firstName, string lastName, string age)
        {
            // Обновляем person энтити.
        }
        public void Insert(string firstName, string lastName, string gender, string age)
        {
            // Вставляем person энтити.
        }
        public void Delete(int id)
        {
            // удаляем запись person 
        }

        /* .... */
    }

Data Mapper

data mapper

Объектные и реляционные БД используют разные способы структурирования данных. Множество составляющих объектов, например коллекции и наследование, не представлены в реляционных БД. Когда проектируется объектная модель с большим количеством бизнес-логики, полезно применять такие механизмы для улучшения организации хранения данных и логики, которая работает c ними. Это приводит к различиям в организации. Так что объектная и реляционная схемы не идентичны.

Тем не менее, необходимость в обмене данными между двумя схемами не отпадает, и этот обмен становится, в свою очередь, сложным. Если же объект знает о реляционной структуре — изменения в одной из структур приведёт к проблемам в другой.

Data Mapper — это программная прослойка, разделяющая объект и БД. Его обязанность — пересылать данные между ними и изолировать их друг от друга. При использовании Data Mapper'а объекты не нуждаются в знании о существовании БД. Они не нуждаются в SQL-коде, и (естественно) в информации о структуре БД. Так как Data Mapper - это разновидность паттерна Mapper, сам объект-Mapper неизвестен объекту.

 

пример реализации data mapper на C#

using System.Data;
using System.Data.SqlClient;

namespace DesignPatternDemos.DataMapper
{
    public class CustomerDataMapper
    {
        private const string CONNECTION_STRING =
            "Data Source=(local);Initial Catalog=DesignPatterns;Integrated Security=True";

        public static Customer GetByID(int id)
        {
            using(SqlConnection connection = new SqlConnection(CONNECTION_STRING))
            {
                connection.Open();

                using(SqlCommand command = connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;

                    command.CommandText = "SELECT TOP 1 * FROM [Customer] WHERE [ID] = @ID";
                    command.Parameters.AddWithValue("@ID", id);

                    SqlDataReader reader = command.ExecuteReader();

                    // If the query returned a row, create the Customer object and return it.
                    if(reader.HasRows)
                    {
                        reader.Read();

                        string name = (string)reader["Name"];
                        bool isPremiumMember = (bool)reader["IsPremiumMember"];

                        return new Customer(id, name, isPremiumMember);
                    }
                }
            }

            return null;
        }

        // Notice that we need to pass in the Customer object (or some information from it)
        // to use some of the methods in the DataMapper class.

        public void Save(Customer customer)
        {
            // This method needs to handle INSERT (new Customer) and UPDATE (existing Customer).
            // Or, you would need to create two separate functions, and call them when appropriate.

            // Pretend there is code here to do the insert and/or update to the database.
        }

        // We also could have only passed in the ID for this method, 
        // because that is the only value from the Customer object that this method needs.
        public void Delete(Customer customer)
        {
            using(SqlConnection connection = new SqlConnection(CONNECTION_STRING))
            {
                connection.Open();

                using(SqlCommand command = connection.CreateCommand())
                {
                    command.CommandType = CommandType.Text;

                    command.CommandText = "DELETE FROM [Customer] WHERE [ID] = @ID";
                    command.Parameters.AddWithValue("@ID", customer.ID);

                    command.ExecuteNonQuery();
                }
            }
        }
    }
}

 

Comments:

Please log in to be able add comments.