Stellen Sie sich vor, Ihr Kunde möchte die Möglichkeit haben, dem Produkt in seinem E-Shop in seinem CMS eine neue Eigenschaft (z. B. Farbe) hinzuzufügen.
Anstatt Eigenschaften als Felder zu haben:
class Car extends Product {
protected String type;
protected int seats;
}
Am Ende würden Sie wahrscheinlich Folgendes tun:
class Product {
protected String productName;
protected Map<String, Property> properties;
}
class Property {
protected String name;
protected String value;
}
Das heißt, es wird ein eigenes Typensystem über das vorhandene System erstellt. Es fühlt sich für mich so an, als könnte dies eine domänenspezifische Sprache erzeugen, oder nicht?
Ist dieser Ansatz ein bekanntes Entwurfsmuster? Würden Sie das Problem anders lösen? Ich weiß, dass es Sprachen gibt, in denen ich zur Laufzeit ein Feld hinzufügen kann, aber was ist mit der Datenbank? Möchten Sie lieber Spalten hinzufügen / ändern oder etwas wie oben gezeigt verwenden?
Vielen Dank für Ihre Zeit :).
Antworten:
Herzliche Glückwünsche! Sie haben gerade den Globus des Programmiersprachen- / Typensystems umrundet und sind auf der anderen Seite der Welt angekommen, von wo aus Sie abgereist sind. Sie sind gerade an der Grenze von Land mit dynamischer Sprache / Prototypen gelandet!
In vielen dynamischen Sprachen (z. B. JavaScript, PHP, Python) können Objekteigenschaften zur Laufzeit erweitert oder geändert werden.
Die extreme Form davon ist eine prototypbasierte Sprache wie Self oder JavaScript. Sie haben streng genommen keinen Unterricht. Sie können Dinge tun, die wie klassenbasierte, objektorientierte Programmierung mit Vererbung aussehen, aber die Regeln sind im Vergleich zu schärfer definierten, klassenbasierten Sprachen wie Java und C # erheblich gelockert.
Sprachen wie PHP und Python leben im Mittelfeld. Sie haben regelmäßige, idiomatische klassenbasierte Systeme. Objektattribute können jedoch zur Laufzeit hinzugefügt, geändert oder gelöscht werden - allerdings mit einigen Einschränkungen (z. B. "außer für integrierte Typen"), die Sie in JavaScript nicht finden.
Der große Kompromiss für diese Dynamik ist die Leistung. Vergessen Sie, wie stark oder schwach die Sprache getippt ist oder wie gut sie bis zum Maschinencode kompiliert werden kann. Dynamische Objekte müssen als flexible Maps / Wörterbücher und nicht als einfache Strukturen dargestellt werden. Dies erhöht den Aufwand für jeden Objektzugriff. Einige Programme sind sehr bemüht, diesen Overhead zu reduzieren (z. B. mit Phantom-Warg-Zuweisung und slotbasierten Klassen in Python), aber der zusätzliche Overhead ist in der Regel nur eine Frage des Kurses und des Eintrittspreises.
Kehren Sie zu Ihrem Entwurf zurück und übertragen Sie die Fähigkeit, dynamische Eigenschaften auf eine Teilmenge Ihrer Klassen zu übertragen. A
Product
kann variable Attribute haben; vermutlich einInvoice
oder einOrder
würde und konnte nicht. Es ist kein schlechter Weg. Es gibt Ihnen die Flexibilität, Variationen dort zu haben, wo Sie sie benötigen, und bleibt dabei in einem strengen, disziplinierten Sprach- und Typensystem. Auf der anderen Seite sind Sie für die Verwaltung dieser flexiblen Eigenschaften verantwortlich. Möglicherweise müssen Sie dazu Mechanismen verwenden, die sich geringfügig von den nativen Attributen unterscheiden.p.prop('tensile_strength')
eher alsp.tensile_strength
zum Beispiel undp.set_prop('tensile_strength', 104.4)
eher alsp.tensile_strength = 104.4
. Aber ich habe mit vielen Programmen in Pascal, Ada, C, Java und sogar in dynamischen Sprachen gearbeitet und sie erstellt, die genau diesen Get-Set-Zugriff für nicht standardmäßige Attributtypen verwendeten. Der Ansatz ist klar umsetzbar.Im Übrigen ist diese Spannung zwischen statischen Typen und einer sehr unterschiedlichen Welt äußerst verbreitet. Ein analoges Problem tritt häufig beim Entwerfen eines Datenbankschemas auf, insbesondere für relationale und vorrelationale Datenspeicher. Manchmal geht es darum, "Super-Zeilen" zu erstellen, die genügend Flexibilität enthalten, um die Vereinigung aller vorgestellten Variationen zu enthalten oder zu definieren, und dann alle Daten, die in diese Felder gelangen, zu stopfen. Die Wordpress -
wp_posts
Tabelle , zum Beispiel, haben Felder wiecomment_count
,ping_status
,post_parent
undpost_date_gmt
, die nur unter bestimmten Umständen interessant, und dass in der Praxis oft leer gehen. Ein anderer Ansatz ist ein sehr sparsamer, normalisierter Tischwp_options
, ähnlich wie IhrProperty
Klasse. Es ist zwar eine explizitere Verwaltung erforderlich, die darin enthaltenen Elemente sind jedoch selten leer. Objektorientierte Datenbanken und Dokumentendatenbanken (z. B. MongoDB) haben es oft leichter, Optionen zu ändern, da sie Attribute nach Belieben erstellen und festlegen können.quelle
Ich mag die Frage, meine zwei Cent:
Ihre beiden Ansätze unterscheiden sich grundlegend:
In C ++ würden viele eine std :: map von boost :: variant verwenden , um eine Mischung aus beiden zu erzielen.
Disgression: Beachten Sie, dass einige Sprachen, wie z. B. C #, die dynamische Erstellung von Typen ermöglichen. Was eine gute Lösung für das allgemeine Problem sein könnte, Mitglieder dynamisch hinzuzufügen. Das "Ändern / Hinzufügen" von Typen nach dem Kompilieren beschädigt jedoch das Typensystem selbst und macht Ihre "geänderten" Typen fast unbrauchbar (z. B. wie würden Sie auf solche hinzugefügten Eigenschaften zugreifen, da Sie nicht einmal wissen, dass sie existieren? Der einzig sinnvolle Weg wäre dies eine systematische Reflexion über jedes Objekt sein ... mit einer reinen dynamischen Sprache enden _ Sie können auf das Schlüsselwort 'dynamic' verweisen.
quelle
Das Erstellen eines Typs zur Laufzeit klingt viel komplizierter als das Erstellen einer Abstraktionsschicht. Es ist sehr verbreitet, eine Abstraktion zu erstellen, um Systeme zu entkoppeln.
Lassen Sie mich ein Beispiel aus meiner Praxis zeigen. Die Moskauer Börse hat den Handelskern Plaza2 mit der Händler-API. Händler schreiben ihre Programme, um mit Finanzdaten zu arbeiten. Das Problem ist, dass diese Daten sehr umfangreich, komplex und stark Änderungen ausgesetzt sind. Dies kann sich ändern, nachdem ein neues Finanzprodukt eingeführt oder die Clearing-Regeln geändert wurden. Die Art der zukünftigen Änderungen kann nicht vorhergesagt werden. Buchstäblich kann es sich jeden Tag ändern und schlechte Programmierer sollten den Code bearbeiten und eine neue Version veröffentlichen, und verärgerte Händler sollten ihre Systeme überarbeiten.
Offensichtliche Entscheidung ist, alle finanzielle Hölle hinter einer Abstraktion zu verstecken. Sie haben bekannte SQL-Tabellen-Abstraktion verwendet. Der größte Teil ihres Kerns kann mit jedem gültigen Schema arbeiten, genauso wie die Software des Händlers das Schema dynamisch analysieren und herausfinden kann, ob es mit ihrem System kompatibel ist.
Zurück zu Ihrem Beispiel: Es ist normal, eine abstrakte Sprache vor einer Logik zu erstellen. Es kann sich um "Eigenschaft", "Tabelle", "Nachricht" oder sogar um menschliche Sprache handeln. Beachten Sie jedoch die Nachteile dieses Ansatzes:
quelle
In XML und HTML wären dies die Attribute eines Knotens / Elements. Ich habe sie auch als erweiterte Eigenschaften, Name / Wert-Paare und Parameter bezeichnet gehört.
So würde ich das Problem lösen, ja.
Eine Datenbank wäre in gewisser Hinsicht wie Java. In Pesudo-SQL:
Dies würde dem Java entsprechen
Beachten Sie, dass keine Property-Klasse erforderlich ist, da die Map den Namen als Schlüssel speichert.
Ich habe versucht, die Spalte hinzuzufügen / zu ändern, und es war ein Albtraum. Es kann getan werden, aber die Dinge gerieten immer wieder aus dem Takt und ich habe es nie geschafft, gut zu funktionieren. Die oben beschriebene Tabellenstruktur war viel erfolgreicher. Wenn Sie tun müssen , um Durchsuchung und Ordnung durch die, sollte mit einem Attributtabelle für jeden Datentyp (
date_attributes
,currency_attributes
usw.) oder einige der Eigenschaften , wie gute alte Datenbankspalten in der Tabelle Artikel hinzufügen. Berichte lassen sich oft einfacher in Datenbankspalten als in Untertabellen schreiben.quelle