Поиск по сайту:

Что такое «несоответствие импеданса» в программировании?


Несоответствие импеданса программирования возникает, когда данные необходимо преобразовать в другую архитектурную парадигму. Наиболее ярким примером являются объектно-ориентированные кодовые базы и реляционные базы данных.

Несоответствие импеданса возникает, когда данные извлекаются из базы данных или вставляются в нее. Свойства объектов или классов в кодовой базе должны быть сопоставлены с соответствующими полями таблицы базы данных.

Отображение и отношения

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

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

CREATE TABLE productTypes(
    ProductTypeUuid VARCHAR(32) PRIMARY KEY,
    ProductTypeName VARCHAR(255) NOT NULL UNIQUE
);
 
CREATE TABLE products(
    ProductUuid VARCHAR(32) PRIMARY KEY,
    ProductName VARCHAR(255) NOT NULL UNIQUE,
    ProductType VARCHAR(32) NOT NULL,
    FOREIGN KEY (ProductType) REFERENCES productTypes(ProductTypeUuid) ON DELETE CASCADE
);

Используя простой SQL, вы можете получить объединенные свойства продукта и его типа продукта с помощью этого простого запроса:

SELECT * FROM products
INNER JOIN productTypes ON ProductTypeUuid = ProductType;

Затем доступ к свойствам продукта и его ProductType осуществляется из той же плоской структуры:

echo $record["ProductName"];
echo $record["ProductTypeName"];

Этот плоский массив быстро становится ограничивающим в сложных приложениях. Разработчики естественным образом моделируют сущности Product и ProductType как отдельные классы. Затем класс Product может содержать экземпляр ProductType. Вот как это выглядит:

final class ProductType {
 
    public function __construct(
        public string $Uuid,
        public string $Name) {}
 
}
 
final class Product {
 
    public function __construct(
        public string $Uuid,
        public string $Name,
        public ProductType $ProductType) {}
 
}

Теперь в коде имеется значительное несоответствие импеданса. Прежде чем записи из запроса к базе данных могут быть представлены в виде экземпляров Product, требуется некоторая форма специализированного сопоставления.

Дальнейшие сложности возникают, когда вы хотите получить доступ ко всем продуктам определенного типа. Вот как это можно сделать в коде:

final class ProductType {
 
    public function __construct(
        public string $Uuid,
        public string $Name,
        ProductCollection $Products) {}
 
}

Теперь ProductType содержит ProductCollection, который в конечном итоге будет содержать массив экземпляров Product. Это создает двунаправленную реляционную ссылку — ProductType содержит все его продукты, а каждый Product содержит свой тип продукта.

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

Учитывая иерархию

Описанная выше модель создает иерархию в кодовой базе: Product находится ниже ProductType. Это звучит логично и соответствует нашим ожиданиям от реального мира.

Реляционные базы данных не признают иерархии. Поскольку все отношения эквивалентны, реляционные базы данных по своей сути имеют «плоскую» структуру. Мы видели это ранее при получении данных с помощью JOIN.

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

В приведенном выше примере показано, что Product и ProductType могут ссылаться друг на друга в коде; плоская природа реляционных баз данных предотвратит появление этого конкретного примера. Циклы все еще могут возникать в простом SQL, но вы столкнетесь с ними реже, чем при моделировании с помощью объектно-ориентированного кода.

Объектно-ориентированное программирование основано на объединении простых объектов в более сложные. В реляционных моделях нет понятия композиции или «простого» и «сложного» — любая запись может ссылаться на любую другую.

Наследование

Еще одна исключительная функция ООП — наследование. Обычно класс расширяет другой, добавляя дополнительные поведения. Реляционные базы данных не могут воспроизвести это. Таблица не может «расширять» другую таблицу.

Кодовая база, использующая наследование, столкнется с трудностями, когда дочерние объекты сохраняются или гидратируются через реляционную базу данных. В базе данных вам обычно понадобятся две таблицы. Один хранит свойства базового объекта (который вы расширяете), а другой обрабатывает свойства дочернего объекта.

Затем код сопоставления перебирает все свойства объекта. Свойства, производные от расширенного класса, будут вставлены в первый код. Те, которые непосредственно определены для дочернего элемента, окажутся во второй таблице.

Аналогичная система требуется при отображении обратно в кодовую базу из базы данных. SQL JOIN можно использовать для получения всех записей, соответствующих базовому (расширенному) классу, включая дочерние свойства. Те записи, которые содержат дочерние свойства, затем будут сопоставлены с экземплярами дочернего класса.

CREATE TABLE parent(Id INTEGER PRIMARY KEY, A INTEGER);
CREATE TABLE child(Id INTEGER PRIMARY KEY, B INTEGER, ParentId INTEGER);
class Parent {
    public function __construct(int $A) {}
}
 
final class Child extends Parent {
    public function __construct(int $A, int $B) {}
}
 
// Get records
// SELECT * FROM parent INNER JOIN child ON child.ParentId = parent.Id;
$objs = [];
foreach ($records as $record) {
    if (isset($record["B"])) {
        $objs[] = new Child($record["A"], $record["B"]);
    }
    else $objs[] = new Parent($record["A"]);
}

Введение наследования требует использования более сложной логики отображения. Неспособность реляционных баз данных моделировать возможности наследования объектно-ориентированных языков приводит к этому несоответствию импеданса.

Видимость и инкапсуляция

Фундаментальным принципом объектно-ориентированного программирования являются видимость и инкапсуляция. Свойства объявляются как public или private. Внутреннее представление объекта может быть скрыто за интерфейсом, который предварительно форматирует данные для потребителя.

В реляционных базах данных отсутствуют эти элементы управления. Невозможно объявить поле как private, и в этом нет очевидной необходимости. Каждая часть данных, хранящихся в базе данных, имеет свое назначение; не должно быть избыточности.

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

CREATE TABLE products(Price INTEGER, TaxRate INTEGER);
final class Product {
 
    public function __construct(
        protected int $Price,
        protected int $TaxRate) {}
 
    public function getTotalPrice() : int {
        return ($this -> Price * ($this -> TaxRate / 100));
    }
}

Приложение заботится только об общей стоимости продукта, включая налоги. Цена за единицу и налоговая ставка инкапсулированы классом Product. Элементы управления видимостью (protected) скрывают значения. Открытый интерфейс состоит из одного метода, который использует два поля для вычисления нового значения.

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

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

Краткое содержание

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

Проблема несоответствия импеданса является одним из ключевых факторов, побуждающих к принятию ORM в объектно-ориентированных языках. Они обеспечивают автоматическую гидратацию сложных объектов кодовой базы из реляционных источников данных. Без выделенного уровня отображения данных только самые простые приложения будут иметь прямой путь к реляционной базе данных и обратно.




Все права защищены. © Linux-Console.net • 2019-2024