Question

Au fur et à mesure que j'en apprends de plus en plus sur la POO et que je commence à mettre en œuvre divers modèles de conception, je reviens sans cesse à des cas où les gens détestent Enregistrement actif.

Souvent, les gens disent que cela ne s'adapte pas bien (citant Twitter comme exemple principal) - mais personne ne l'explique réellement. pourquoi ça ne s'adapte pas bien ;et/ou comment obtenir les avantages de la RA sans les inconvénients (via un modèle similaire mais différent ?)

J'espère que cela ne se transformera pas en une guerre sainte à propos des modèles de conception - tout ce que je veux savoir, c'est ****plus précisément**** ce qui ne va pas avec Active Record.

Si cela ne s’adapte pas bien, pourquoi pas ?

Quels autres problèmes a-t-il ?

Était-ce utile?

La solution

Il y a ActiveRecord le modèle de conception et ActiveRecord la bibliothèque ORM Rails, et il existe également une tonne de contrefaçons pour .NET et d'autres langages.

Ce sont toutes des choses différentes.Ils suivent principalement ce modèle de conception, mais l'étendent et le modifient de différentes manières. Ainsi, avant que quiconque dise "ActiveRecord Sucks", il doit être qualifié en disant "quel ActiveRecord, il y a des tas?"

Je ne connais qu'ActiveRecord de Rails, je vais essayer de répondre à toutes les plaintes qui ont été soulevées dans le cadre de son utilisation.

@BlaM

Le problème que je vois avec Active Records est qu'il ne s'agit toujours que d'une seule table.

Code:

class Person
    belongs_to :company
end
people = Person.find(:all, :include => :company )

Cela génère du SQL avec LEFT JOIN companies on companies.id = person.company_id, et génère automatiquement les objets Société associés afin que vous puissiez faire people.first.company et il n'est pas nécessaire d'accéder à la base de données car les données sont déjà présentes.

@pix0r

Le problème inhérent à Active Record est que les requêtes de base de données sont automatiquement générées et exécutées pour remplir les objets et modifier les enregistrements de la base de données.

Code:

person = Person.find_by_sql("giant complicated sql query")

Ceci est déconseillé car c'est moche, mais pour les cas où vous avez simplement besoin d'écrire du SQL brut, c'est facile à faire.

@Tim Sullivan

... et vous sélectionnez plusieurs instances du modèle, vous effectuez essentiellement un "select * from ..."

Code:

people = Person.find(:all, :select=>'name, id')

Cela sélectionnera uniquement les colonnes de nom et d'ID de la base de données, tous les autres « attributs » dans les objets mappés seront simplement nuls, sauf si vous rechargez manuellement cet objet, et ainsi de suite.

Autres conseils

J'ai toujours trouvé qu'ActiveRecord convient aux applications rapides basées sur CRUD où le modèle est relativement plat (comme dans peu de hiérarchies de classes).Cependant, pour les applications avec des hiérarchies OO complexes, un DataMapper est probablement une meilleure solution.Bien qu'ActiveRecord suppose un rapport de 1:1 entre vos tables et vos objets de données, ce type de relation devient compliqué avec des domaines plus complexes.Dans son livre sur les patrons, Martin Fowler souligne qu'ActiveRecord a tendance à s'effondrer dans des conditions où votre modèle est assez complexe, et suggère un DataMapper comme alternative.

J'ai constaté que cela était vrai dans la pratique.Dans les cas où vous avez beaucoup d'héritage dans votre domaine, il est plus difficile de mapper l'héritage à votre SGBDR que de mapper les associations ou la composition.

La façon dont je le fais est d'avoir des objets "domaine" auxquels vos contrôleurs accèdent via ces classes DataMapper (ou "couche de service").Ceux-ci ne reflètent pas directement la base de données, mais agissent comme votre représentation OO pour un objet du monde réel.Supposons que vous ayez une classe User dans votre domaine et que vous ayez besoin de références ou de collections d'autres objets déjà chargées lorsque vous récupérez cet objet User.Les données peuvent provenir de nombreuses tables différentes, et un modèle ActiveRecord peut rendre la tâche très difficile.

Au lieu de charger directement l'objet User et d'accéder aux données à l'aide d'une API de style ActiveRecord, votre code contrôleur récupère un objet User en appelant par exemple l'API de la méthode UserMapper.getUser().C'est ce mappeur qui est responsable du chargement de tous les objets associés à partir de leurs tables respectives et du renvoi de l'objet "domaine" utilisateur complété à l'appelant.

Essentiellement, vous ajoutez simplement une autre couche d'abstraction pour rendre le code plus gérable.Que vos classes DataMapper contiennent du SQL brut personnalisé, ou des appels à une API de couche d'abstraction de données, ou même accèdent elles-mêmes à un modèle ActiveRecord, n'a pas vraiment d'importance pour le code du contrôleur qui reçoit un objet User agréable et rempli.

Quoi qu'il en soit, c'est comme ça que je fais.

Je pense qu'il existe probablement un ensemble de raisons très différentes entre les raisons pour lesquelles les gens « détestent » ActiveRecord et ce qui ne va pas.

Sur la question de la haine, il y a beaucoup de venin envers tout ce qui concerne Rails.En ce qui concerne ce qui ne va pas, il est probable que c'est comme toute technologie et qu'il y a des situations où c'est un bon choix et des situations où il existe de meilleurs choix.La situation dans laquelle vous ne profitez pas de la plupart des fonctionnalités de Rails ActiveRecord, d'après mon expérience, est celle où la base de données est mal structurée.Si vous accédez à des données sans clés primaires, avec des éléments qui violent la première forme normale, où de nombreuses procédures stockées sont requises pour accéder aux données, il est préférable d'utiliser quelque chose qui est plutôt un simple wrapper SQL.Si votre base de données est relativement bien structurée, ActiveRecord vous permet d'en profiter.

Pour ajouter au thème de la réponse aux commentateurs qui disent que les choses sont difficiles dans ActiveRecord avec une réplique d'extrait de code

@Sam McAfee Supposons que vous ayez une classe User dans votre domaine et que vous ayez besoin d'avoir des références ou des collections d'autres objets déjà chargées lorsque vous récupérez cet objet User.Les données peuvent provenir de nombreuses tables différentes, et un modèle ActiveRecord peut rendre la tâche très difficile.

user = User.find(id, :include => ["posts", "comments"])
first_post = user.posts.first
first_comment = user.comments.first

En utilisant l'option include, ActiveRecord vous permet de remplacer le comportement de chargement différé par défaut.

Ma réponse longue et tardive, même pas complète, mais une bonne explication POURQUOI je déteste ce schéma, des opinions et même quelques émotions :

1) version courte :Active Record crée un "fine couche" de "liaison forte" entre la base de données et le code de l'application.Ce qui ne résout aucun problème logique, aucun problème, aucun problème du tout.À mon humble avis, il ne fournit AUCUNE VALEUR, à l'exception de certains sucre syntaxique pour le programmeur (qui peut alors utiliser une "syntaxe objet" pour accéder à certaines données, qui existent dans une base de données relationnelle).L'effort visant à créer un certain confort pour les programmeurs devrait (à mon humble avis...) mieux être investi dans des outils d'accès aux bases de données de bas niveau, par ex.quelques variantes de simple, facile, clair hash_map get_record( string id_value, string table_name, string id_column_name="id" ) et des méthodes similaires (bien sûr, les concepts et l'élégance varient considérablement selon le langage utilisé).

2) version longue :Dans tous les projets basés sur des bases de données où j'avais le « contrôle conceptuel » des choses, j'évitais la RA, et c'était bien.Je construis habituellement un architecture en couches (vous divisez tôt ou tard votre logiciel en couches, au moins dans les projets de moyenne à grande taille) :

A1) la base de données elle-même, les tables, les relations, même un peu de logique si le SGBD le permet (MySQL a également grandi maintenant)

A2) très souvent, il y a plus qu’un magasin de données :système de fichiers (les blobs dans la base de données ne sont pas toujours une bonne décision...), systèmes existants (imaginez-vous "comment" ils seront accessibles, de nombreuses variétés possibles.mais ce n'est pas le sujet...)

B) couche d'accès à la base de données (à ce niveau, les méthodes outils, les aides pour accéder facilement aux données de la base de données sont les bienvenues, mais la RA n'apporte ici aucune valeur, à l'exception d'un peu de sucre syntaxique)

C) couche d'objets d'application :Les "objets d'application" sont parfois de simples lignes d'une table dans la base de données, mais la plupart du temps, ils sont composé de toute façon, et ont une logique supérieure attachée, donc investir du temps dans les objets AR à ce niveau est tout simplement inutile, une perte de temps précieux pour les codeurs, car la "valeur réelle", la "logique supérieure" de ces objets doit être implémentée. au-dessus des objets AR, de toute façon - avec et sans AR !Et, par exemple, pourquoi voudriez-vous avoir une abstraction des « Objets d’entrée de journal » ?Le code logique de l'application les écrit, mais devrait-il avoir la possibilité de les mettre à jour ou de les supprimer ?ça a l'air idiot, et App::Log("I am a log message") certaines grandeurs sont-elles plus faciles à utiliser que le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();.Et par exemple :l'utilisation d'un "Objet d'entrée de journal" dans la vue du journal de votre application fonctionnera pour 100, 1 000 ou même 10 000 lignes de journal, mais tôt ou tard, vous devrez optimiser - et je parie que dans la plupart des cas, vous utiliserez simplement ce petit beau Instruction SQL SELECT dans la logique de votre application (ce qui brise totalement l'idée AR ..), au lieu d'envelopper cette petite instruction dans des cadres d'idée AR fixes et rigides avec beaucoup de code encapsulé et masqué.Le temps que vous avez perdu à écrire et/ou à créer du code AR aurait pu être investi dans une interface beaucoup plus intelligente pour lire des listes d'entrées de journaux (de nombreuses façons, le ciel est la limite).Les codeurs devraient osez inventer de nouvelles abstractions pour réaliser leur logique d'application qui correspond à l'application prévue, et ne pas réimplémenter bêtement des schémas idiots, ça sonne bien à première vue !

D) la logique d'application - implémente la logique des objets en interaction et la création, la suppression et la liste (!) des objets de logique d'application (NON, ces tâches doivent rarement être ancrées dans les objets de logique d'application eux-mêmes :la feuille de papier sur votre bureau vous indique-t-elle les noms et les emplacements de toutes les autres feuilles de votre bureau ?oubliez les méthodes "statiques" pour lister les objets, c'est idiot, un mauvais compromis créé pour que la façon de penser humaine s'intègre dans la pensée AR [certains-pas-tous-AR-frame-like-]AR)

E) l'interface utilisateur - eh bien, ce que j'écrirai dans les lignes suivantes est très, très, très subjectif, mais d'après mon expérience, les projets basés sur la RA négligeaient souvent la partie UI d'une application - du temps était perdu à créer des abstractions obscures .En fin de compte, de telles applications ont fait perdre beaucoup de temps aux codeurs et ressemblent à des applications de codeurs pour codeurs, enclins à la technologie à l'intérieur comme à l'extérieur.Les codeurs se sentent bien (le travail est enfin fait, tout est terminé et correct, selon le concept sur papier...), et les clients "n'ont qu'à apprendre qu'il faut que ça soit comme ça", parce que c'est "professionnel".ok, désolé, je m'éloigne du sujet ;-)

Eh bien, certes, tout cela est subjectif, mais c'est mon expérience (Ruby on Rails exclu, cela peut être différent, et je n'ai aucune expérience pratique avec cette approche).

Dans les projets payants, j'ai souvent entendu la demande de commencer par créer des objets « d'enregistrement actif » comme élément de base pour la logique d'application de niveau supérieur.D'après mon expérience, ceci visiblement souvent C'était une sorte d'excuse pour que le client (une société de développement de logiciels dans la plupart des cas) n'ait pas un bon concept, une vue d'ensemble, un aperçu de ce que devrait finalement être le produit.Ces clients pensent dans des cadres rigides ("dans le projet d'il y a dix ans, cela fonctionnait bien..."), ils peuvent étoffer les entités, ils peuvent définir les relations entre les entités, ils peuvent décomposer les relations entre les données et définir la logique d'application de base, mais ensuite ils s'arrêtent. et je vous le remets, et je pense que c'est tout ce dont vous avez besoin...il leur manque souvent un concept complet de logique d'application, d'interface utilisateur, de convivialité, etc.ils n'ont pas une vue d'ensemble et ils manquent d'amour pour les détails, et ils veulent que vous suiviez cette façon de faire de la RA, parce que...eh bien, pourquoi, cela a fonctionné dans ce projet il y a des années, cela garde les gens occupés et silencieux ?Je ne sais pas.Mais les "détails" séparent les hommes des garçons, ou ..comment était le slogan publicitaire original ?;-)

Après de nombreuses années (dix ans d'expérience active en développement), chaque fois qu'un client mentionne un "modèle d'enregistrement actif", ma sonnette d'alarme retentit.J'ai appris à essayer de les obtenir retour à cette phase conceptuelle essentielle, laissez-les réfléchir à deux fois, essayez-les de montrer leurs faiblesses conceptuelles ou tout simplement évitez-les du tout s'ils manquent de discernement (en fin de compte, vous savez, un client qui ne sait pas encore ce qu'il veut, peut-être même pense qu'il le sait mais ne le sait pas). t, ou essaie de M'externaliser gratuitement le travail de conception, me coûte de nombreuses heures, jours, semaines et mois précieux, le live est trop court...).

Alors finalement :TOUT CELA est la raison pour laquelle je déteste ce stupide "modèle d'enregistrement actif", et je le fais et je l'éviterai autant que possible.

MODIFIER:J'appellerais même cela un No-Pattern.Cela ne résout aucun problème (les modèles ne sont pas destinés à créer du sucre syntaxique).Cela crée de nombreux problèmes :la racine de tous ses problèmes (mentionnés dans de nombreuses réponses ici ..) est que ça se cache juste le bon vieux SQL bien développé et puissant derrière une interface extrêmement limitée par la définition des modèles.

Ce modèle remplace la flexibilité par du sucre syntaxique !

Pensez-y, quel problème la RA résout-elle pour vous ?

Certains messages me laissent perplexe.Certaines réponses vont à "ORM" vs "SQL" ou quelque chose comme ça.

Le fait est que la RA n'est qu'un modèle de programmation de simplification dans lequel vous profitez des objets de votre domaine pour y écrire le code d'accès à la base de données.

Ces objets ont généralement des attributs métier (propriétés du bean) et un certain comportement (méthodes qui fonctionnent généralement sur ces propriétés).

L'AR dit simplement "ajouter quelques méthodes à ces objets de domaine" aux tâches liées à la base de données.

Et je dois dire, d’après mon opinion et mon expérience, que je n’aime pas ce modèle.

À première vue, cela peut paraître plutôt bien.Certains outils Java modernes comme Spring Roo utilisent ce modèle.

Pour moi, le vrai problème réside uniquement dans le souci de la POO.Le modèle AR vous oblige d'une manière ou d'une autre à ajouter une dépendance de votre objet aux objets d'infrastructure.Ces objets d'infrastructure permettent à l'objet de domaine d'interroger la base de données via les méthodes suggérées par AR.

J'ai toujours dit que deux niveaux sont la clé du succès d'un projet.La couche de service (où réside la logique métier ou peut être exportée via une sorte de technologie à distance, comme les services Web, par exemple) et la couche de domaine.À mon avis, si nous ajoutons des dépendances (pas vraiment nécessaires) aux objets de la couche de domaine pour résoudre le modèle AR, nos objets de domaine seront plus difficiles à partager avec d'autres couches ou (rares) applications externes.

L'implémentation Spring Roo de AR est intéressante, car elle ne s'appuie pas sur l'objet lui-même, mais sur certains fichiers AspectJ.Mais si plus tard vous ne souhaitez pas travailler avec Roo et devez refactoriser le projet, les méthodes AR seront implémentées directement dans vos objets de domaine.

Un autre point de vue.Imaginez que nous n'utilisons pas de base de données relationnelle pour stocker nos objets.Imaginez que l'application stocke nos objets de domaine dans une base de données NoSQL ou simplement dans des fichiers XML, par exemple.Allons-nous implémenter les méthodes qui effectuent ces tâches dans nos objets de domaine ?Je ne pense pas (par exemple, dans le cas de XM, nous ajouterions des dépendances liées à XML à nos objets de domaine... Vraiment triste je pense).Pourquoi alors devons-nous implémenter les méthodes relationnelles de base de données dans les objets de domaine, comme le dit le modèle Ar ?

Pour résumer, le modèle AR peut sembler plus simple et adapté aux applications petites et simples.Mais lorsque nous avons des applications complexes et volumineuses, je pense que l’architecture classique en couches est une meilleure approche.

La question concerne le modèle de conception d'enregistrements actif.Pas un outil ORM.

La question d'origine est étiquetée avec des rails et fait référence à Twitter qui est construit dans Ruby on Rails.Le framework ActiveRecord dans Rails est une implémentation du modèle de conception Active Record de Fowler.

La principale chose que j'ai vue en ce qui concerne les plaintes concernant Active Record est que lorsque vous créez un modèle autour d'une table et que vous sélectionnez plusieurs instances du modèle, vous effectuez essentiellement une "sélection * à partir de ...".C'est très bien pour modifier un enregistrement ou afficher un enregistrement, mais si vous souhaitez, par exemple, afficher une liste des villes pour tous les contacts de votre base de données, vous pouvez faire "sélectionner la ville de..." et obtenir uniquement les villes. .Faire cela avec Active Record nécessiterait que vous sélectionniez toutes les colonnes, mais uniquement en utilisant City.

Bien entendu, différentes implémentations géreront cela différemment.Néanmoins, c'est un problème.

Maintenant, vous pouvez contourner ce problème en créant un nouveau modèle pour la chose spécifique que vous essayez de faire, mais certaines personnes diraient que cela demande plus d'efforts que d'avantages.

Moi, j'adore Active Record.:-)

HTH

J'aime la façon dont SubSonic fait la seule chose avec une seule colonne.
Soit

DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)

, ou:

Query q = DataBaseTable.CreateQuery()
               .WHERE(DataBaseTable.Columns.ColumnToFilterOn,value);
q.SelectList = DataBaseTable.Columns.ColumnYouWant;
q.Load();

Mais Linq est toujours le roi en matière de chargement paresseux.

@BlaM :Parfois, je viens d'implémenter un enregistrement actif à la suite d'une jointure.Il ne doit pas toujours s'agir de la relation Table <--> Active Record.Pourquoi pas "Résultat d'une instruction Join" <--> Active Record ?

Je vais parler d'Active Record comme design pattern, je n'ai pas vu de ROR.

Certains développeurs détestent Active Record, car ils lisent des livres intelligents sur l'écriture de code propre et soigné, et ces livres déclarent que l'enregistrement actif viole le principe de responsabilité unique, viole la règle DDD selon laquelle l'objet de domaine doit être ignorant persistant, et de nombreuses autres règles de ce type de livres. .

La deuxième chose que les objets de domaine dans Active Record ont tendance à être 1 pour 1 avec la base de données, ce qui peut être considéré comme une limitation dans certains types de systèmes (principalement à n niveaux).

Ce ne sont que des choses abstraites, je n'ai pas vu Ruby on Rails implémenter réellement ce modèle.

Le problème que je vois avec Active Records est qu'il s'agit toujours de un tableau.Ce n'est pas grave, tant que vous travaillez réellement avec cette seule table, mais lorsque vous travaillez avec des données, dans la plupart des cas, vous aurez une sorte de jointure quelque part.

Oui, rejoindre est généralement pire que pas de jointure du tout quand il s'agit de performances, mais rejoindre généralement est mieux que "fausse" adhésion en lisant d'abord l'intégralité du tableau A, puis en utilisant les informations obtenues pour lire et filtrer le tableau B.

Le problème avec ActiveRecord est que les requêtes qu'il génère automatiquement pour vous peuvent entraîner des problèmes de performances.

Vous finissez par utiliser des astuces peu intuitives pour optimiser les requêtes, ce qui vous laisse vous demander s'il aurait été plus efficace d'écrire la requête à la main en premier lieu.

Bien que tous les autres commentaires concernant l'optimisation SQL soient certainement valables, mon principal reproche concernant le modèle d'enregistrement actif est qu'il conduit généralement à inadéquation d'impédance.J'aime garder mon domaine propre et correctement encapsulé, ce que le modèle d'enregistrement actif détruit généralement tout espoir de le faire.

Essayez de créer une relation polymorphe plusieurs à plusieurs.Pas si facile.Surtout lorsque vous n'utilisez pas STI.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top