web-dev-qa-db-fra.com

Ajout de champ à la classe à l'exécution - modèle de conception

Imaginez que votre client souhaite avoir la possibilité d'ajouter de nouvelles propriétés (par exemple de couleur) au produit dans son ESHOP dans leur CMS.

Au lieu d'avoir des propriétés comme champs:

class Car extends Product {
   protected String type;
   protected int seats;
}

Vous finiriez probablement de faire quelque chose comme:

class Product {
   protected String productName;
   protected Map<String, Property> properties;
}

class Property {
   protected String name;
   protected String value;
}

C'est-à-dire créer un système de type propre sur le dessus de l'existant. Il me semble que tel que cela puisse être considéré comme créant des languages ​​spécifiques à un domaine ou ne pouvait pas?

Est-ce que cette approche est-elle un modèle de conception connu? Souhaitez-vous résoudre le problème différemment? Je sais qu'il y a des langues où je peux ajouter un champ à l'exécution, mais qu'en est-il de la base de données? Souhaitez-vous plutôt ajouter/modifier des colonnes ou utilisées comme indiqué ci-dessus?

Merci pour votre temps :).

16
Filip

Toutes nos félicitations! Vous venez de contourner le programme de programmation de langage/type System Globe, arrivant de l'autre côté du monde d'où vous êtes parti. Vous venez d'atterrir à la frontière de la langue dynamique/des terrains d'objets basés sur des prototypes!

De nombreuses langues dynamiques (par exemple JavaScript, PHP, Python) permettent d'étendre ou de modifier les propriétés d'objet au moment de l'exécution.

La forme extrême de ceci est une langue basée sur des prototypes comme auto ou JavaScript. Ils n'ont pas de cours, à proprement parler. Vous pouvez faire des choses qui ressemblent à une programmation basée sur des objets avec héritage, mais les règles sont grandement détendues par rapport à des langues de classe plus définies, telles que Java et C #.

Langauges comme PHP et Python Live dans le milieu du terrain. Ils ont des systèmes de classe Idiomatic réguliers. Mais les attributs d'objet peuvent être ajoutés, modifiés ou supprimés Au moment de l'exécution - bien que certaines restrictions (comme "sauf pour les types intégrés") que vous ne trouvez pas dans JavaScript.

Le grand compromis pour ce dynamisme est la performance. Oubliez à quel point la langue est fortement ou faiblement dactylographiée ou à quel point il peut être compilé au code de la machine. Les objets dynamiques doivent être représentés sous forme de cartes/dictionnaires flexibles, plutôt que de structures simples. Cela ajoute des frais généraux à chaque accès objet. Certains programmes vont à de grandes longueurs pour réduire ce surcharge (par exemple avec des classes Phantom Kwarg et des classes à base d'emplacements en Python), mais les frais généraux supplémentaires sont généralement égaux pour le cours et le prix de l'admission.

Revenez à votre conception, vous gérez la possibilité d'avoir des propriétés dynamiques sur un sous-ensemble de vos classes. A Product peut avoir des attributs variables; Vraisemblablement un Invoice ou un Order aurait pu. Ce n'est pas un mauvais moyen d'y aller. Cela vous donne la possibilité d'avoir une variation si vous en avez besoin, tout en restant dans un système de langue et de type strict, discipliné. En bas, vous êtes responsable de la gestion de ces propriétés flexibles et vous devrez probablement le faire à travers des mécanismes légèrement différents des attributs plus indigènes. p.prop('tensile_strength') plutôt que p.tensile_strength, par exemple, et p.set_prop('tensile_strength', 104.4) plutôt que p.tensile_strength = 104.4. Mais j'ai travaillé avec et construit de nombreux programmes à Pascal, ADA, C, Java et même des langages dynamiques qui utilisaient exactement cet accès à un getter-Setter pour des types d'attributs non standard; l'approche est clairement réalisable.

Par la suite, cette tension entre types statiques et un monde très varié est extrêmement courante. Un problème analogue est souvent vu lors de la conception de schéma de base de données, en particulier pour les magasins de données relationnels et pré-relationnels. Parfois, il est traité en créant des "super-rangs" qui contiennent suffisamment de flexibilité pour contenir ou définir l'union de toutes les variations imaginées, puis remplissez toutes les données qui vont dans ces champs. Tableau WordPress wp_posts , par exemple, a des champs tels que comment_count, ping_status, post_parent Et post_date_gmt Ce n'est intéressant que dans certaines circonstances et que, dans la pratique, allez souvent en blanc. Une autre approche est une table très épargnée, normalisée comme wp_options, Beaucoup comme votre Property classe. Bien que cela nécessite plus de gestion explicite, les articles dedans sont rarement vides. Les bases de données d'objet et de documents (par exemple MongoDB) ont souvent un temps plus facile avec des options changeantes, car elles peuvent créer et définir des attributs à peu près à volonté.

4
Jonathan Eunice

La création d'un type à l'exécution semble beaucoup plus compliquée, alors créez une couche d'abstraction. Il est très courant de créer une abstraction pour découpler des systèmes.

Laissez-moi montrer un exemple de ma pratique. Exchange de Mosco a le noyau de trading appelé Plaza2 avec API du Trader. Les commerçants écrivent leurs programmes pour travailler avec des données financières. Le problème est que ces données sont très énormes, complexes et fortement exposées à des modifications. Cela peut changer après un nouveau produit financier introduit ou des rools de compensation modifiés. La nature des modifications futures ne peut être prédite. Littéralement, il peut changer chaque jour et les programmeurs pauvres doivent éditer le code et publier une nouvelle version, et les traders en colère devraient réviser leurs systèmes.

La décision évidente est de cacher tout l'enfer financier derrière une abstraction. Ils ont utilisé une abstraction bien connue de tables SQL. La plus grande partie de leur noyau peut fonctionner avec tout schéma valide, la même chose que le logiciel du commerçant peut analyser dynamiquement le schéma et déterminer s'il est compatible avec leur système.

Retour à votre exemple, il est normal de créer une langue abstraite devant une logique. Cela pourrait être "propriété", "table", "message", voire de langue humaine, mais faites attention aux inconvénients de cette approche:

  • Plus de code pour analyse et validation (et plus de temps hors du temps). Tout ce que le compilateur a fait pour vous avec la dactylographie statique que vous devez faire dans le temps d'exécution.
  • Plus de documentation de messages, tables ou autres primitives. Toute la complexité passe du code à une sorte de schéma ou de standard. Voici un exemple de schéma d'enfer financier mentionné ci-dessus: http://ftp.moex.com/pub/forts/plaza2/p2gate_en.pdf (dizaines de pages de tables)
0
astef

Est-ce que cette approche est-elle un modèle de conception connu?

Dans XML et HTML, ce serait les attributs à un nœud/élément. Je les ai également entendus appelé des propriétés étendues, des paires de noms/de valeur et des paramètres.

Souhaitez-vous résoudre le problème différemment?

C'est ainsi que je résoudrais le problème, oui.

Je sais qu'il y a des langues où je peux ajouter un champ à l'exécution, mais qu'en est-il de la base de données?

Une base de données serait comme Java, dans certains sens. Dans Pesudo-SQL:

TABLE products
(
    product_name VARCHAR(50),
    product_id INTEGER AUTOINCREMENT
)

TABLE attributes
(
    product_id INTEGER,
    name VARCHAR(50),
    value VARCHAR(2000)
)

Cela correspondrait à la Java

class Product {
   protected String productName;
   protected Map<String, String> properties;
}

Notez qu'il n'y a pas besoin de classe de propriété, car la carte stocke le nom comme clé.

Souhaitez-vous plutôt ajouter/modifier des colonnes ou utilisées comme indiqué ci-dessus?

J'ai essayé la chose Ajout/Alter Colonne, et c'était un cauchemar. Cela peut être fait, mais les choses continuaient de sortir de la synchronisation et je ne l'ai jamais eu pour bien fonctionner. La structure de la table que j'ai décrite ci-dessus a été beaucoup plus réussie. Si vous devez effectuer des recherches et commandez par's, envisagez d'utiliser une table d'attributs pour chaque type de données (date_attributes, currency_attributes, etc.) ou ajouter certaines des propriétés en tant que bonnes anciennes colonnes de base de données dans la table des produits. Les rapports sont souvent beaucoup plus faciles à écrire sur les colonnes de base de données, puis sous-tableaux.

0
Guy Schalnat

J'aime la question, mes deux cents:

Vos deux approches sont radicalement différentes:

  • Le premier est OO AS Fortement dactylographié - mais pas extensible
  • Le second est faiblement dactylographié (String encapsule n'importe quoi)

En C++, beaucoup utiliseraient une STD :: Plan de Boost :: Variant pour obtenir un mélange des deux.

Discression: Notez que certaines langues, telles que c #, permettent la création dynamique de types. Ce qui pourrait être une belle solution pour la question générale de l'ajout de membres de manière dynamique. Cependant, les types "Modification/Ajout" après la compilation corrompre le système de type lui-même et rendent vos types "modifiés" presque inutilement (par exemple, comment accéderiez-vous de telles propriétés ajoutées, car vous ne savez même pas qu'ils existent? La seule façon raisonnable serait Soyez une réflexion systématique sur chaque objet ... En finissant par une langue dynamique pure _ Vous pouvez vous reporter au mot-clé "dynamique" .NET).

0
quantdev