Was ist eine “Impedance Mismatch” in der Programmierung?

0
250
Shutterstock/oatawa

Eine Fehlanpassung der Programmierimpedanz tritt auf, wenn Daten in ein anderes Architekturparadigma umgewandelt werden müssen. Das bekannteste Beispiel sind objektorientierte Codebasen und relationale Datenbanken.

Ein Impedanz-Mismatch entsteht, wenn Daten aus einer Datenbank geholt oder in eine Datenbank eingefügt werden. Die Eigenschaften von Objekten oder Klassen innerhalb der Codebasis müssen den entsprechenden Datenbanktabellenfeldern zugeordnet werden.

Zuordnung und Beziehungen

Ihre Klassen werden nicht unbedingt direkt einzelnen Datenbanktabellen zugeordnet. Die Konstruktion eines Objekts erfordert möglicherweise die aggregierte Verwendung von Daten aus mehreren Tabellen.

Sie müssen auch die Beziehungen zwischen Ihren Daten verarbeiten. Relationale Datenbanken machen dies einfach, indem Sie auf andere Datensätze verweisen. Sie können einen JOIN verwenden, um auf alle Daten zuzugreifen, die in der Beziehung eingeschlossen sind.

CREATE TABLE productTypes( ProductTypeUuid VARCHAR(32) PRIMÄRSCHLÜSSEL, ProductTypeName VARCHAR(255) NICHT NULL EINZIGARTIG );   CREATE TABLE Produkte( ProductUuid VARCHAR(32) PRIMÄRSCHLÜSSEL, Produktname VARCHAR(255) NOT NULL UNIQUE, ProductType VARCHAR(32) NICHT NULL, FREMDSCHLÜSSEL (ProductType) REFERENZEN productTypes(ProductTypeUuid) AUF KASKADE LÖSCHEN );

Mit einfacher SQL können Sie die kombinierten Eigenschaften eines Produkts und seines Produkttyps mit dieser einfachen Abfrage abrufen:

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

Dann wird auf die Eigenschaften des Produkts und seines Produkttyps aus derselben flachen Struktur zugegriffen:

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

Dieses flache Array wird bei komplexen Anwendungen schnell zu Einschränkungen. Entwickler modellieren die Entitäten Product und ProductType natürlich als separate Klassen. Die Product-Klasse könnte dann eine Instanz eines ProductType enthalten. So sieht das aus:

letzte Klasse ProductType {   öffentliche Funktion __construct( öffentliche Zeichenfolge $Uuid, öffentliche Zeichenfolge $Name) {}   }   letzte Klasse Produkt {   öffentliche Funktion __construct( öffentliche Zeichenfolge $Uuid, öffentliche Zeichenfolge $Name, öffentlicher Produkttyp $ProductType) {}   }

Der Code weist jetzt eine erhebliche Impedanzfehlanpassung auf. Bevor Datensätze aus der Datenbankabfrage als Produktinstanzen dargestellt werden können, ist eine spezielle Zuordnung erforderlich.

Weitere Komplikationen treten auf, wenn Sie auf alle Produkte eines bestimmten Typs zugreifen möchten. So können Sie dies im Code tun:

letzte Klasse ProductType {   öffentliche Funktion __construct( öffentliche Zeichenfolge $Uuid, öffentliche Zeichenfolge $Name, ProductCollection $Products) {}   }

Der ProductType enthält nun eine ProductCollection, die letztendlich ein Array von Product-Instanzen enthalten würde. Dadurch wird eine bidirektionale relationale Referenz erstellt – ProductType enthält alle seine Produkte und jedes Produkt enthält seinen Produkttyp.

Diese Form der Modellierung existiert innerhalb des relationalen Paradigmas nicht. Jede Verbindungsform wird durch einen einzelnen relationalen Link-Datensatz dargestellt. Die Verwendung bidirektionaler Referenzen vereinfacht den Entwicklerzugriff auf Objekteigenschaften. Es erfordert jedoch ein komplexeres Mapping, wenn es zur und von der Datenbank übertragen wird. Dies liegt daran, dass SQL die Semantik des Modells nicht nativ versteht.

Hierarchie berücksichtigen

Werbung

Das oben erläuterte Modell erstellt eine Hierarchie in der Codebasis: Product sitzt unterhalb ProductType. Das klingt logisch und entspricht unseren Erwartungen an die reale Welt.

Relationale Datenbanken respektieren keine Hierarchien. Da alle Beziehungen gleichwertig sind, haben relationale Datenbanken von Natur aus eine “flache” Struktur. Wir haben dies früher beim Abrufen von Daten mit einem JOIN gesehen.

Die fehlende Hierarchie in SQL bedeutet, dass alle Tabellen die gleiche Priorität haben. Dies hat zur Folge, dass Sie problemlos auf die Eigenschaften von Datensätzen zugreifen können, die tief in Ihrer logischen Objekthierarchie verschachtelt sind. Außerdem besteht ein geringeres Risiko zyklischer Abhängigkeiten.

Das obige Beispiel zeigt, dass Product und ProductType im Code aufeinander verweisen können; die flache Natur relationaler Datenbanken würde dieses spezielle Beispiel verhindern. Zyklen können in einfacher SQL immer noch auftauchen, aber Sie werden seltener darauf stoßen als bei der Modellierung mit objektorientiertem Code.

Die objektorientierte Programmierung beruht auf der Zusammensetzung einfacher Objekte in komplexere. Relationale Modelle haben keine solche Vorstellung von Komposition oder der “einfachen” und “komplex” – jeder Datensatz kann auf jeden anderen verweisen.

Vererbung

Eine weitere OOP-exklusive Funktion ist die Vererbung. Es ist üblich, dass eine Klasse eine andere erweitert und zusätzliche Verhaltensweisen hinzufügt. Relationale Datenbanken können dies nicht replizieren. Es ist unmöglich, dass eine Tabelle “erweitert” ein anderer Tisch.

Werbung

Eine Codebasis, die Vererbung verwendet, wird auf Schwierigkeiten stoßen, wenn untergeordnete Objekte über eine relationale Datenbank beibehalten oder hydratisiert werden. Innerhalb der Datenbank benötigen Sie normalerweise zwei Tabellen. Einer speichert die Eigenschaften des Basisobjekts (das Sie erweitern), während ein anderer die Eigenschaften des untergeordneten Objekts verarbeitet.

Der Zuordnungscode iteriert dann alle Eigenschaften des Objekts. Von der erweiterten Klasse abgeleitete Eigenschaften werden in den ersten Code eingefügt. Diejenigen, die direkt für das Kind definiert sind, landen in der zweiten Tabelle.

Ein ähnliches System ist erforderlich, wenn aus der Datenbank zurück auf die Codebasis gemappt wird. Ein SQL JOIN könnte verwendet werden, um alle Datensätze zu erhalten, die der (erweiterten) Basisklasse entsprechen, einschließlich der untergeordneten Eigenschaften. Diese Datensätze, die die untergeordneten Eigenschaften enthielten, würden dann den untergeordneten Klasseninstanzen zugeordnet.

CREATE TABLE Parent(Id INTEGER PRIMARY KEY, A INTEGER); CREATE TABLE child(Id INTEGER PRIMARY KEY, B INTEGER, ParentId INTEGER); Klasse Übergeordnet { öffentliche Funktion __construct(int $A) {} }   final class Child erweitert Parent { öffentliche Funktion __construct(int $A, int $B) {} }   //Datensätze abrufen //SELECT * FROM parent INNER JOIN child ON child.ParentId = parent.Id; $objs = []; foreach ($records as $record) { wenn (isset($record["B"])) { $objs[] = neues Kind($record["A"], $record["B"]); } sonst $objs[] = neue übergeordnete ($record["A"]); }

Die Einführung der Vererbung erfordert die Verwendung einer komplexeren Zuordnungslogik. Die Unfähigkeit relationaler Datenbanken, die Vererbungsfähigkeiten objektorientierter Sprachen zu modellieren, führt zu dieser Impedanzfehlanpassung.

Sichtbarkeit und Kapselung

Ein grundlegender Grundsatz der objektorientierten Programmierung ist Sichtbarkeit und Kapselung. Eigenschaften werden als öffentlich oder privat deklariert. Die interne Darstellung eines Objekts kann hinter einer Schnittstelle verborgen werden, die Daten für den Verbraucher vorformatiert.

Relationalen Datenbanken fehlen diese Steuerelemente. Es gibt keine Möglichkeit, ein Feld als privat zu deklarieren, und es besteht auch keine offensichtliche Notwendigkeit. Jedes in einer Datenbank gespeicherte Datenelement hat seinen eigenen Zweck; es sollte keine Redundanz geben.

Werbung

Dies gilt nicht unbedingt, wenn es auf ein objektorientiertes Paradigma übertragen wird. Ein Objekt kann zwei Datenbankfelder zusammen verwenden, um eine neue berechnete Information bereitzustellen. Die Werte der beiden einzelnen Felder können für die Anwendung irrelevant und daher nicht sichtbar sein.

CREATE TABLE Produkte(Preis INTEGER, Steuersatz INTEGER); letzte Klasse Produkt {   öffentliche Funktion __construct( geschützt int $Price, geschützt int $TaxRate) {}   öffentliche Funktion getTotalPrice() : int { ($this -> Preis * ($this -> Steuersatz/100)); } }

Die Anwendung berücksichtigt nur den Gesamtproduktpreis, einschließlich Steuern. Der Einheitspreis und der Steuersatz werden durch die Produktklasse gekapselt. Sichtbarkeitskontrollen (geschützt) verbergen die Werte. Die öffentliche Schnittstelle besteht aus einer einzigen Methode, die die beiden Felder verwendet, um einen neuen Wert zu berechnen.

Im Allgemeinen befürwortet die objektorientierte Programmierung die Programmierung von Schnittstellen. Vom direkten Zugriff auf Immobilien wird abgeraten. Durch die Verwendung von Klassenmethoden, die ein Interface implementieren, können in Zukunft alternative Implementierungen konstruiert werden.

Es gibt kein direktes Gegenstück dazu in der relationalen Welt. Datenbankansichten bieten eine Möglichkeit, Tabellen und abstrakte Felder zu neuen Formularen zu kombinieren. Sie arbeiten jedoch immer noch direkt mit Feldwerten.

Zusammenfassung

Objektrelationale Impedanzfehlanpassungen treten auf, wenn eine objektorientierte Codebasis Daten mit einer relationalen . austauscht Datenbank. Es gibt grundlegende Unterschiede in der Art und Weise, wie Daten modelliert werden. Dies erfordert die Einführung einer Mapping-Schicht, die Daten zwischen den Formaten transponiert.

Das Impedanz-Mismatch-Problem ist einer der wichtigsten Motivationsfaktoren bei der Einführung von ORMs in objektorientierten Sprachen. Diese ermöglichen die automatische Hydratation komplexer Codebasisobjekte aus relationalen Datenquellen. Ohne eine dedizierte Datenzuordnungsschicht haben nur die einfachsten Anwendungen einen direkten Weg zu und von einer relationalen Datenbank.