Au fur et à mesure que j'en apprends de plus en plus sur la POO et que je commence à implémenter divers modèles de conception, je reviens sans cesse à des cas où les gens détestent Active Record .
Souvent, les gens disent qu'il ne s'adapte pas bien (citant Twitter comme leur premier exemple) - mais personne n'explique en fait pourquoi il ne s'adapte pas bien; et / ou comment atteindre 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 sur les modèles de conception - tout ce que je veux savoir, c'est **** spécifiquement **** ce qui ne va pas avec Active Record.
S'il ne s'adapte pas bien, pourquoi pas?
Quels autres problèmes a-t-il?
ruby-on-rails
design-patterns
oop
activerecord
Adam Tuttle
la source
la source
Réponses:
Il y a ActiveRecord the Design Pattern et ActiveRecord la bibliothèque Rails ORM , et il y a aussi 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 nombreuses manières différentes, donc avant que quelqu'un ne dise "ActiveRecord Sucks", il doit être qualifié en disant "quel ActiveRecord, il y a des tas?"
Je ne connais que ActiveRecord de Rails, je vais essayer de répondre à toutes les plaintes qui ont été soulevées dans le cadre de son utilisation.
Code:
Cela génère du SQL avec
LEFT JOIN companies on companies.id = person.company_id
et génère automatiquement des objets Société associés afin que vous puissiez le fairepeople.first.company
et qu'il n'ait pas besoin d'accéder à la base de données car les données sont déjà présentes.Code:
C'est déconseillé car c'est moche, mais pour les cas où vous avez simplement et simplement besoin d'écrire du SQL brut, c'est facile à faire.
Code:
Cela ne sélectionnera que 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, à moins que vous ne rechargiez manuellement cet objet, et ainsi de suite.
la source
J'ai toujours trouvé qu'ActiveRecord est bon pour les applications rapides basées sur CRUD où le modèle est relativement plat (comme dans, pas beaucoup de hiérarchies de classes). Cependant, pour les applications avec des hiérarchies OO complexes, un DataMapper est probablement une meilleure solution. Alors qu'ActiveRecord suppose un rapport 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 modèles , Martin Fowler souligne qu'ActiveRecord a tendance à se décomposer dans des conditions où votre modèle est assez complexe, et suggère un DataMapper comme alternative.
J'ai trouvé que c'é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 deviez 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.
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 de contrôleur récupère un objet User en appelant l'API de la méthode UserMapper.getUser (), par exemple. C'est ce mappeur qui est chargé de charger tous les objets associés à partir de leurs tables respectives et de renvoyer l'objet "domaine" utilisateur terminé à 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 personnalisé brut, ou des appels à une API de couche d'abstraction de données, ou même qu'ils accèdent eux-mêmes à un modèle ActiveRecord, n'a pas vraiment d'importance pour le code du contrôleur qui reçoit un bel objet User rempli.
Bref, c'est comme ça que je fais.
la source
Je pense qu'il y a probablement un ensemble très différent de raisons entre pourquoi les gens «détestent» ActiveRecord et ce qui ne va pas avec lui.
En ce qui concerne la haine, il y a beaucoup de venin envers tout ce qui est lié à Rails. En ce qui concerne ce qui ne va pas, il est probable que ce soit comme toute technologie et il y a des situations où c'est un bon choix et des situations où il y a de meilleurs choix. La situation dans laquelle vous ne pouvez pas profiter 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 nécessaires pour accéder aux données, il vaut mieux utiliser quelque chose qui n'est plus qu'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
En utilisant l'option include, ActiveRecord vous permet de remplacer le comportement de chargement différé par défaut.
la source
Ma réponse longue et tardive, même pas complète, mais une bonne explication POURQUOI je déteste ce schéma, ces opinions et même certaines émotions:
1) version courte: Active Record crée une " 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 quelconque, aucun problème du tout. À mon humble avis, il ne fournit AUCUNE VALEUR, à l'exception du sucre syntaxique pour le programmeur (qui peut alors utiliser une «syntaxe d'objet» pour accéder à certaines données, qui existe dans une base de données relationnelle). L'effort pour créer un certain confort pour les programmeurs devrait (à mon humble avis) être mieux investi dans des outils d'accès aux bases de données de bas niveau, par exemple certaines variantes de simples, faciles, simples
hash_map get_record( string id_value, string table_name, string id_column_name="id" )
méthodes et similaires (bien sûr, les concepts et l'élégance varient considérablement avec le langue utilisée).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 généralement une architecture en couches (vous divisez tôt ou tard votre logiciel en couches, au moins dans les projets de taille moyenne à grande):
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 est également développé 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 hérités (imaginez "comment" ils seront accédés, de nombreuses variétés possibles .. mais c'est pas le point ...)
B) couche d'accès à la base de données (à ce niveau, les méthodes d'outils, les aides pour accéder facilement aux données de la base de données sont les bienvenues, mais AR ne fournit aucune valeur ici, sauf quelques sucres syntaxiques)
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és objets toute façon, et ont une logique plus élevée attachée, donc investir du temps dans des 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 cela devrait-il permettre de les mettre à jour ou de les supprimer? semble idiot, et certaines magnitudes sont plus faciles à utiliser . Et par exemple: l'utilisation d'un "objet Entrée de journal" dans la vue du journal de votre application fonctionnera pour 100, 1000 ou même 10000 lignes de journal, mais tôt ou tard, vous devrez optimiser - et je parie que dans la plupart des cas, vous n'aurez qu'à utilisez cette belle petite instruction SQL SELECT dans la logique de votre application (ce qui rompt totalement l'idée AR ...), au lieu d'encapsuler cette petite instruction dans des cadres d'idées AR fixes rigides avec beaucoup de code enveloppant et masquant. Le temps que vous avez perdu avec l'écriture et / ou la construction de code AR aurait pu être investi dans une interface beaucoup plus intelligente pour lire des listes d'entrées de journal (de nombreuses façons, le ciel est la limite).
App::Log("I am a log message")
le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();
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 de l'application - implémente la logique des objets en interaction et de la création, de la suppression et de la liste (!) Des objets de la logique de l'application (NON, ces tâches doivent rarement être ancrées dans les objets de la logique de l'application elle-même: la feuille de papier sur votre bureau le dit-elle vous 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 humaine de penser s'intègre dans [certains-not-all-AR-framework-like -] Pensée 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 qui reposaient 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 se sentent comme des applications de codeurs pour codeurs, orientés vers la technologie à l'intérieur et à l'extérieur. Les codeurs se sentent bien (travail acharné enfin fait, tout est terminé et correct, selon le concept sur papier ...), et les clients "doivent juste apprendre que ça doit être comme ça", parce que c'est "professionnel" .. ok, désolé, je m'égare ;-)
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 «enregistrement actif» comme élément constitutif de la logique d'application de niveau supérieur. Dans mon expérience, ce bien en évidence souventé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 le produit devrait finalement être. Ces clients pensent dans des cadres rigides ("dans le projet il y a dix ans, ça a bien fonctionné .."), ils peuvent étoffer des entités, ils peuvent définir des relations d'entités, ils peuvent décomposer les relations de données et définir la logique de base de l'application, mais ensuite ils s'arrêtent et vous le remettez, et pensez que c'est tout ce dont vous avez besoin ... ils manquent souvent d'un concept complet de logique d'application, d'interface utilisateur, de convivialité, etc., ils n'ont pas la vue d'ensemble et ils manquent d'amour pour le détails, et ils veulent que vous suiviez cette façon de faire AR, 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éparer les hommes des garçons, ou… comment était le slogan original de la publicité? ;-)
Après de nombreuses années (dix ans d'expérience dans le développement actif), chaque fois qu'un client mentionne un «modèle d'enregistrement actif», ma sonnette d'alarme sonne. J'ai appris à essayer de les ramener à cette phase de conception essentielle , de les laisser réfléchir à deux fois, de les essayer pour montrer leurs faiblesses conceptuelles ou simplement de les éviter du tout s'ils ne sont pas conscients (au final, vous savez, un client qui ne le fait pas encore) savoir ce qu'il veut, peut-être même pense qu'il sait mais ne le fait pas, ou essaie d'externaliser gratuitement le travail de concept vers MOI, me coûte beaucoup d'heures, de jours, de semaines et de mois précieux de mon temps, vivre est trop court ...).
Donc, enfin: CECI TOUT est la raison pour laquelle je déteste ce "modèle d'enregistrement actif" idiot, et je le fais et je l'éviterai autant que possible.
EDIT : J'appellerais même cela un No-Pattern. Cela ne résout aucun problème (les motifs 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 qu'il cache juste le bon vieux SQL bien développé et puissant derrière une interface qui est par la définition des modèles extrêmement limitée.
Ce modèle remplace la flexibilité par du sucre syntaxique!
Pensez-y, quel problème la RA résout-elle pour vous?
la source
before_save
rappels sensibles afin de maintenir la cohérence dans l'enregistrement 4)after_commit
hooks pour les déclencheurs de service externes. 5) Un bon DSL pour organiser les changements DDL en changesets (migrations). (Il y a encore de la douleur là-bas, mais ne pas avoir de modèle est pire lorsque> 1 développeur.)Certains messages me confondent. 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 où vous tirez parti 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 certains comportements (méthodes qui fonctionnent généralement sur ces propriétés).
L'AR dit simplement "ajouter des 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 le modèle.
À première vue, cela peut sembler plutôt bon. Certains outils Java modernes comme Spring Roo utilisent ce modèle.
Pour moi, le vrai problème est simplement la préoccupation de la POO. Le motif AR vous oblige d'une manière ou d'une autre à ajouter une dépendance de votre objet aux objets d'infra-structure. Ces objets d'infra-structure 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 couches sont la clé du succès d'un projet. La couche de service (où réside la logique commerciale ou peut être exportée via une sorte de technologie de communication à 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 couche de domaine pour résoudre le modèle AR, nos objets de domaine seront plus difficiles à partager avec d'autres couches ou des applications externes (rares).
L'implémentation Spring Roo de AR est intéressante, car elle ne repose pas sur l'objet lui-même, mais sur certains fichiers AspectJ. Mais si par la suite 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. Allions-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 au XML à nos objets de domaine ... Vraiment triste je pense). Pourquoi alors devons-nous implémenter les méthodes DB relationnelles dans les objets du domaine, comme le dit le modèle Ar?
Pour résumer, le modèle AR peut sembler plus simple et bon pour les 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 source
La question d'origine est étiquetée avec des rails et fait référence à Twitter qui est intégré à Ruby on Rails. Le framework ActiveRecord dans Rails est une implémentation du modèle de conception Active Record de Fowler.
la source
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 faites essentiellement un "select * from ...". C'est très bien pour modifier un enregistrement ou afficher un enregistrement, mais si vous voulez, 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 . Pour ce faire avec Active Record, vous devez sélectionner toutes les colonnes, mais uniquement en utilisant City.
Bien sûr, 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 diront que c'est plus un effort que l'avantage.
Moi, je creuse Active Record. :-)
HTH
la source
J'adore la façon dont SubSonic fait une seule colonne.
Soit
, ou:
Mais Linq est toujours roi en matière de chargement paresseux.
la source
@BlaM: Parfois, je viens de mettre en œuvre un enregistrement actif pour le résultat d'une jointure. Ne doit pas toujours être la relation Table <--> Active Record. Pourquoi pas "Résultat d'une instruction Join" <--> Enregistrement actif?
la source
Je vais parler d'Active Record comme modèle de conception, je n'ai pas vu 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 indiquent que l'enregistrement actif viole le principe de resposobility 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-to-1 avec la base de données, ce qui peut être considéré comme une limitation dans certains types de systèmes (n-tiers principalement).
C'est juste des choses abstraites, je n'ai pas vu ruby on rails implémentation réelle de ce modèle.
la source
Le problème que je vois avec Active Records est qu'il ne s'agit toujours que d' une table. Ce n'est pas grave, tant que vous ne travaillez vraiment qu'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, la jointure est généralement pire que l' absence de jointure du tout en termes de performances, mais la jointure est généralement meilleure que la "fausse" jointure en lisant d'abord toute la table A, puis en utilisant les informations obtenues pour lire et filtrer la table B.
la source
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 faire des astuces peu intuitives pour optimiser les requêtes qui vous laissent vous demander s'il aurait été plus efficace d'écrire la requête à la main en premier lieu.
la source
Bien que tous les autres commentaires concernant l'optimisation SQL soient certainement valides, ma principale plainte avec le modèle d'enregistrement actif est qu'il conduit généralement à une discordance 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 faire.
la source
Essayez de faire une relation polymorphe plusieurs à plusieurs. Pas si facile. Surtout lorsque vous n'utilisez pas d'ITS.
la source