Anti- quoi? Cela semble probablement beaucoup plus compliqué qu'il ne l'est. Au cours des deux dernières décennies, les programmeurs ont pu identifier une sélection utile de modèles de «conception» fréquemment utilisés dans leurs solutions de code. Tout en résolvant des problèmes similaires, ils ont pu «classer» les solutions qui les empêchaient de réinventer la roue tout le temps. Il est important de noter que ces modèles doivent être considérés davantage comme des découvertes que par les inventions d'un groupe de développeurs avancés..
Si ceci est plutôt nouveau pour vous et que vous vous considérez plus comme un débutant en Ruby / Rails, celui-ci est écrit pour vous. Je pense que c'est mieux si vous le considérez comme une rapide plongée dans un sujet beaucoup plus profond dont la maîtrise ne se fera pas du jour au lendemain. Néanmoins, je crois fermement que commencer à se mettre au travail tôt profitera énormément aux débutants et à leurs mentors..
Les anti-modèles, comme leur nom l'indique, représentent à peu près le contraire des modèles. Ce sont des découvertes de solutions à des problèmes que vous devez absolument éviter. Ils représentent souvent le travail de codeurs inexpérimentés qui ne savent pas encore ce qu’ils ne savent pas. Pire encore, ils pourraient être le produit d'une personne paresseuse qui ignore tout simplement les meilleures pratiques et les cadres d'outils sans raison valable - ou pense qu'ils n'en ont pas besoin. Ce qu’ils pourraient espérer gagner au début en gagner du temps en élaborant des solutions rapides, paresseuses ou sales, va les hanter ou leur succéder par la suite, plus tard dans le cycle de vie du projet..
Ne sous-estimez pas les conséquences de ces mauvaises décisions - elles vous tourmenteront, peu importe les circonstances..
Je suis sûr que vous avez entendu les chansons des «modèles Fat, contrôleurs maigres» chanter des tonnes de chansons quand vous avez commencé à jouer avec Rails. OK, maintenant oublie ça! Bien sûr, la logique métier doit être résolue dans la couche modèle, mais vous ne devez pas vous sentir enclin à tout y insérer sans raison, simplement pour éviter de franchir les lignes dans le territoire du contrôleur..
Voici une nouvelle cible que vous devriez viser: «Modèles maigres, contrôleurs maigres». Vous pourriez demander: «Eh bien, comment devrions-nous organiser le code pour y parvenir? Après tout, c'est un jeu à somme nulle?» Bon point! Le nom du jeu est composition, et Ruby est bien équipé pour vous donner beaucoup d'options pour éviter l'obésité modèle.
Dans la plupart des applications Web (Rails) qui s'appuient sur une base de données, la majeure partie de votre attention et de votre travail sera centrée sur la couche modèle, car vous travaillez avec des concepteurs compétents capables d'implémenter leurs propres éléments dans la vue, je veux dire. Vos modèles auront intrinsèquement plus de «gravité» et attireront plus de complexité.
La question est simplement de savoir comment vous comptez gérer cette complexité. Active Record vous donne certainement beaucoup de corde pour vous accrocher tout en vous rendant la vie incroyablement facile. Il est tentant de concevoir votre couche de modèle en suivant simplement le chemin de la plus grande commodité immédiate. Néanmoins, une architecture à l'épreuve du temps prend beaucoup plus en compte que de cultiver d'énormes classes et de tout ranger dans des objets Active Record..
Le vrai problème que vous traitez ici est la complexité - inutilement donc, je dirais. Les classes qui amassent des tonnes de code deviennent complexes du seul fait de leur taille. Ils sont plus difficiles à maintenir, à analyser et à comprendre et de plus en plus difficiles à modifier car leur composition manque probablement de découplage. Ces modèles dépassent souvent la capacité recommandée pour traiter une seule responsabilité et sont plutôt répandus. Dans le pire des cas, ils deviennent comme des camions à ordures, manipulant toutes les ordures qu'on leur jette paresseusement.
On peut faire mieux! Si vous pensez que la complexité n’est pas un problème, après tout, vous êtes spécial, intelligent et réfléchissez à nouveau! La complexité est le tueur de projet en série le plus notoire qui soit, pas votre quartier sympathique «Dark Defender»..
Les «modèles plus minces» réalisent une chose que les spécialistes du codage (probablement beaucoup plus que le code et le design) apprécient, et ce que nous devons tous rechercher - la simplicité! Ou du moins plus, ce qui est un compromis juste si la complexité est difficile à éradiquer.
Quels outils Ruby propose-t-il pour faciliter notre vie à cet égard et nous permettre de réduire le poids de nos modèles? Simple, d'autres classes et modules. Vous identifiez un code cohérent que vous pouvez extraire dans un autre objet et construisez ainsi une couche de modèle composée d'agents de taille raisonnable ayant leurs propres responsabilités distinctives..
Pensez-y en termes d'interprète talentueux. Dans la vraie vie, une telle personne pourrait être capable de rap, casser, écrire des paroles et produire ses propres airs. En programmation, vous préférez la dynamique d’un groupe, ici composé d’au moins quatre membres distincts, où chaque personne est responsable du moins de choses possible. Vous souhaitez créer un orchestre de classes capable de gérer la complexité du compositeur - pas une classe de génie maestro en microgestion de tous les métiers.
Regardons un exemple d'un modèle Fat et jouons avec quelques options pour gérer son obésité. Cet exemple est fictif, bien sûr, et en racontant cette petite histoire loufoque, j'espère qu'il sera plus facile à digérer et à suivre pour les débutants..
Nous avons une classe Spectre qui a trop de responsabilités et qui a donc grandi inutilement. En plus de ces méthodes, je pense qu’il est facile d’imaginer qu’un tel spécimen ait déjà accumulé de nombreuses autres choses aussi bien représentées par les trois petits points. Spectre est en passe de devenir une classe de dieu. (Les chances sont assez faibles pour formuler raisonnablement une telle phrase à nouveau de si tôt!)
"classe de rubis Spectre < ActiveRecord::Base has_many :spectre_members has_many :spectre_agents has_many :enemy_agents has_many :operations
…
def turn_mi6_agent (ennemie_agent) met «l'agent MI6 # ennemi_agent.nom remis à Spectre»
def turn_cia_agent (ennemis_agent) met «l'agent CIA # ennemi_agent.nom renvoyé à Spectre»
def turn_mossad_agent (ennemis_agent) met «l'agent Mossad # ennemi_agent.nom renvoyé à Spectre»
def kill_double_o_seven (spectre_agent) spectre_agent.kill_james_bond end
def dispose_of_cabinet_member (nombre) spectre_member = SpectreMember.find_by_id (nombre)
met "Un certain coupable a manqué à l'intégrité absolue de cette fraternité. L'acte approprié est de fumer le numéro # numéro dans son fauteuil. Ses services ne seront pas trop manqués" spectre_member.die end
def print_assignment (operation) définit l'objectif de «Operation # operation.name»: # operation.objective. »end
privé
def ennemi_agent #clever code end
def spectre_agent #clever code end
opération def #clever code end
…
fin
"
Spectre transforme divers types d’agents ennemis, des délégués tuant 007, grillade les membres du cabinet de Spectre en cas de défaillance et imprime également des missions opérationnelles. Un cas évident de microgestion et une violation du «principe de responsabilité unique». Les méthodes privées s'accumulent aussi rapidement.
Cette classe n'a pas besoin de connaître la plupart des éléments qu'elle contient actuellement. Nous allons scinder cette fonctionnalité en quelques classes et voir si la complexité d'avoir deux classes / objets supplémentaires vaut la liposuccion.
"rubis
classe Spectre < ActiveRecord::Base has_many :spectre_members has_many :spectre_agents has_many :enemy_agents has_many :operations
…
def turn_enemy_agent Interrogator.new (ennemi_agent) .turn end
privé
def ennemis_agents self.enemy_agents.last end end
Interrogateur de classe attr_reader: ennemi_agent
def initialise (ennemi_agent) @enemy_agent = ennemi_agent fin
def turn ennemi_agent.turn end end
classe EnemyAgent < ActiveRecord::Base belongs_to :spectre belongs_to :agency
def turn met "Après un lavage de cerveau intensif, la torture et des réserves d'argent…" end end
classe MI6Agent < EnemyAgent def turn super puts “MI6 agent #name turned over to Spectre” end end
classe CiaAgent < EnemyAgent def turn super puts “CIA agent #name turned over to Spectre” end end
classe MossadAgent < EnemyAgent def turn super puts “Mossad agent #name turned over to Spectre” end end
classe NumberOne < ActiveRecord::Base def dispose_of_cabinet_member(number) spectre_member = SpectreMember.find_by_id(number)
met "Un certain coupable a manqué à l'intégrité absolue de cette fraternité. L'acte approprié est de fumer le numéro # numéro dans son fauteuil. Ses services ne seront pas beaucoup manqués" spectre_member.die end end
classe Operation < ActiveRecord::Base has_many :spectre_agents belongs_to :spectre
def print_assignment définit l'objectif de l'opération # name: # objectif. "end end
classe SpectreAgent < ActiveRecord::Base belongs_to :operation belongs_to :spectre
def kill_james_bond met “M. Bond, j'espère que tu mourras!
classe SpectreMember < ActiveRecord::Base belongs_to :spectre
def die met “Nooo, nooo, ce n'était pas meeeeeeeee! ZCHUNK! ”End end
"
Je pense que la partie la plus importante à laquelle vous devriez prêter attention est la façon dont nous avons utilisé une classe Ruby comme Interrogateur
pour gérer le tournage des agents de différentes agences. Des exemples concrets pourraient représenter un convertisseur qui, par exemple, transforme un document HTML en PDF et inversement. Si vous n'avez pas besoin de toutes les fonctionnalités des classes Active Record, pourquoi les utiliser si une simple classe Ruby peut également faire l'affaire? Un peu moins de corde pour se pendre.
La classe Spectre laisse la mauvaise affaire des agents de tournage à la Interrogateur
classe et juste des délégués à elle. Celui-ci a désormais la seule responsabilité de torturer et de laver le cerveau des agents capturés.
Jusqu'ici tout va bien. Mais pourquoi avons-nous créé des classes séparées pour chaque agent? Simple. Au lieu de simplement extraire directement les différentes méthodes de tournage comme turn_mi6_agent
vers Interrogateur
, nous leur avons donné une meilleure maison dans leur classe respective.
En conséquence, nous pouvons utiliser efficacement le polymorphisme et ne pas nous soucier des cas individuels des agents de retournement. Nous disons simplement à ces différents agents de tourner, et chacun d’entre eux sait quoi faire. le Interrogateur
n'a pas besoin de connaître les détails de la transformation de chaque agent.
Tous ces agents étant des objets Active Record, nous en avons créé un générique., EnemyAgent
, cela donne une idée générale de ce que signifie tourner un agent, et nous encapsulons ce bit pour tous les agents à un endroit en le sous-classant. Nous utilisons ce patrimoine en fournissant le tour
méthodes des différents agents avec super
, et donc nous avons accès au secteur du lavage de cerveau et de la torture, sans double emploi. Les responsabilités uniques et l'absence de duplication sont un bon point de départ pour aller de l'avant..
Les autres classes Active Record assument diverses responsabilités dont Specter n'a pas à se soucier. Le «numéro un» consiste généralement à griller lui-même les membres du cabinet Specter défaillants, alors pourquoi ne pas laisser un objet dédié gérer l'électrocution? En revanche, les membres de Spectre qui échouent savent comment mourir quand ils sont fumés dans leur fauteuil par Numéro un
. Opération
imprime maintenant ses tâches elle-même, pas besoin de perdre le temps de Spectre avec des cacahuètes comme ça.
Enfin et surtout, le meurtre de James Bond est généralement tenté par un agent sur le terrain. kill_james_bond
est maintenant une méthode sur SpectreAgent
. Goldfinger aurait géré cela différemment, bien sûr, je dois jouer avec ce truc laser si vous en avez un..
Comme vous pouvez le voir clairement, nous avons maintenant dix classes pour lesquelles nous n’en avions auparavant qu’une. N'est-ce pas trop? Cela peut être, à coup sûr. C'est un problème que vous devrez résoudre la plupart du temps lorsque vous vous séparez de telles responsabilités. Vous pouvez certainement en faire trop. Mais regarder cela sous un autre angle pourrait aider:
Je n'insinue pas que ces questions doivent être vérifiées à chaque fois sur votre liste, mais vous devriez probablement commencer à vous poser ces questions tout en amincissant vos modèles..
Concevoir des modèles minces peut s'avérer difficile, mais il s'agit d'une mesure essentielle pour que vos applications restent saines et agiles. Ce ne sont pas non plus les seuls moyens constructifs de traiter les gros modèles, mais ce sont un bon début, en particulier pour les débutants..
C'est probablement l'anti-motif le plus évident. Du point de vue des tests, toucher une application mature qui n’a aucune couverture de test peut être l’une des expériences les plus pénibles à vivre. Si vous voulez détester le monde et votre propre profession plus que tout autre chose, consacrez six mois à un tel projet et vous saurez à quel point le misanthrope est potentiellement en vous. Je plaisante, bien sûr, mais je doute que cela vous rende plus heureux et que vous souhaitiez le refaire, jamais. Peut-être qu'une semaine fera aussi bien. Je suis à peu près sûr que le mot torture viendra plus souvent dans votre esprit que vous ne le pensez.
Si les tests ne faisaient pas partie de votre processus jusqu'à présent et que ce type de douleur vous semble normal, vous devriez peut-être considérer que les tests ne sont pas si mauvais, ni votre ennemi. Lorsque vos niveaux de joie liés au code sont plus ou moins constamment au-dessus de zéro et que vous pouvez modifier votre code sans crainte, la qualité globale de votre travail sera bien supérieure à celle d'un résultat teinté d'anxiété et de souffrance..
Est-ce que je surestime? Je ne pense vraiment pas! Vous voulez avoir une couverture de test très étendue, non seulement parce que c'est un excellent outil de conception pour écrire uniquement le code dont vous avez réellement besoin, mais aussi parce que vous devrez changer votre code à un moment donné. Vous serez beaucoup mieux équipé pour utiliser votre base de code - et beaucoup plus confiant - si vous avez un harnais de test qui aide et guide les refactorisations, la maintenance et les extensions. Ils se produiront à coup sûr sur la route, zéro doute à ce sujet.
C’est aussi le point où une suite de tests commence à porter ses fruits, car la vitesse accrue avec laquelle vous pouvez effectuer ces changements de qualité en toute sécurité ne peut pas être obtenue par une longue tentative dans les applications conçues par des personnes qui pensent écrire des tests. est absurde ou prend trop de temps.
Ce sont des modèles très curieux qui veulent collecter trop d'informations sur d'autres objets ou modèles. Cela contraste vivement avec l’une des idées les plus fondamentales de l’encapsulation par programmation orientée objet. Nous voulons plutôt lutter pour des classes et des modèles autonomes qui gèrent autant que possible leurs affaires intérieures. En termes de concepts de programmation, ces modèles voyeuristes violent fondamentalement le «principe de moindre connaissance», autrement dit la «loi de Demeter», peu importe la façon dont vous voulez le prononcer..
Pourquoi c'est un problème? C'est une forme de duplication, subtile, qui conduit également à un code beaucoup plus fragile que prévu..
La loi de Demeter est à peu près l'odeur de code la plus fiable que l'on puisse toujours attaquer sans s'inquiéter des inconvénients possibles.
J'imagine que qualifier celle-ci de «loi» n'était pas aussi prétentieux que cela puisse paraître au premier abord. Creusez dans cette odeur, car vous en aurez beaucoup besoin dans vos projets. En gros, en termes d'objets, vous pouvez appeler des méthodes sur l'ami de votre objet mais pas sur l'ami de votre ami..
C'est une façon courante de l'expliquer, et tout se résume à ne pas utiliser plus d'un point pour vos appels de méthode. Soit dit en passant, il est tout à fait correct d’utiliser plus de points ou d’appels de méthode lorsque vous traitez avec un seul objet qui n’essaie pas d’atteindre autre chose que cela. Quelque chose comme @ arms.find_by_name ('Dart de poison'). formule
est très bien. Les chercheurs peuvent parfois accumuler quelques points. Les encapsuler dans des méthodes dédiées est néanmoins une bonne idée.
Regardons quelques mauvais exemples des classes ci-dessus:
"rubis
@ operation.spectre_agents.first.kill_james_bond
@ spectre.operations.last.spectre_agents.first.name
@ spectre.enemy_agents.last.agency.name
"
Pour bien comprendre, voici quelques exemples plus fictifs:
"rubis
@ quartermaster.gizmos.non_lethal.favorite
@ mi6.operation.agent.favorite_weapon
@ mission.agent.name
"
Les bananes, non? Ça n'a pas l'air bien, n'est-ce pas? Comme vous pouvez le constater, ces appels de méthodes jettent un regard excessif sur l’activité d’autres objets. La conséquence négative la plus importante et la plus évidente est de changer un tas de ces appels de méthodes si la structure de ces objets doit être modifiée - ce qui sera finalement le cas, car la seule constante dans le développement logiciel est le changement. En outre, cela a l'air vraiment méchant, pas facile pour les yeux du tout. Lorsque vous ne savez pas qu'il s'agit d'une approche problématique, Rails vous permet d'aller très loin, de toute façon, sans vous hurler dessus. Beaucoup de corde, rappelez-vous?
Alors, que pouvons-nous faire à ce sujet? Après tout, nous voulons obtenir cette information d’une manière ou d’une autre. D'une part, nous pouvons composer nos objets pour répondre à nos besoins, et nous pouvons utiliser intelligemment la délégation pour garder nos modèles minces en même temps. Ouvrons un peu de code pour vous montrer ce que je veux dire.
"rubis
classe SpectreMember < ActiveRecord::Base has_many :operations has_many :spectre_agents
…
fin
classe Operation < ActiveRecord::Base belongs_to :spectre_member
…
fin
classe SpectreAgent < ActiveRecord::Base belongs_to :spectre_member
…
fin
@ spectre_member.spectre_agents.all @ spectre_member.operations.last.print_assignment @ spectre_member.spectre_agents.find_by_id (1) .name
@ operation.spectre_member.name @ operation.spectre_member.number @ operation.spectre_member.spectre_agents.first.name
@ spectre_agent.spectre_member.number
"
"rubis
classe SpectreMember < ActiveRecord::Base has_many :operations has_many :spectre_agents
…
def list_of_agents spectre_agents.all end
def print_operation_details operation = Operation.last operation.print_operation_details end end
classe Operation < ActiveRecord::Base belongs_to :spectre_member
…
def spectre_member_name spectre_member.name end
def spectre_member_number spectre_member.number end
def print_operation_details met “L'objectif de cette opération est # objectif. La cible est # target ”end end
classe SpectreAgent < ActiveRecord::Base belongs_to :spectre_member
…
def superior_in_charge met “Mon patron est le numéro # spectre_member.number” end end
@ spectre_member.list_of_agents @ spectre_member.print_operation_details
@ operation.spectre_member_name @ operation.spectre_member_number
@ spectre_agent.superior_in_charge
"
C’est définitivement un pas dans la bonne direction. Comme vous pouvez le constater, nous avons regroupé les informations que nous souhaitions acquérir dans un ensemble de méthodes d’emballage. Au lieu de toucher directement de nombreux objets, nous avons résumé ces ponts et laissé le soin aux modèles respectifs de parler à leurs amis des informations dont ils ont besoin..
L'inconvénient de cette approche est d'avoir toutes ces méthodes d'emballage supplémentaires qui traînent. Parfois ça va, mais nous voulons vraiment éviter de maintenir ces méthodes dans un tas d'endroits si un objet change.
Si possible, l'endroit où ils doivent changer est sur leur objet et uniquement sur leur objet. Il faut également se méfier des objets polluants utilisant des méthodes qui ont peu à voir avec leur propre modèle, car cela représente toujours un risque potentiel de dilution des responsabilités individuelles..
Nous pouvons faire mieux que ça. Dans la mesure du possible, déléguons les appels de méthodes directement aux objets en charge et essayons de réduire le plus possible les méthodes d'encapsidation. Rails sait ce dont nous avons besoin et nous fournit le plus pratique déléguer
méthode de classe pour dire aux amis de notre objet quelles méthodes nous avons besoin appelé.
Zoomons sur quelque chose de l'exemple de code précédent et voyons où nous pouvons utiliser correctement la délégation..
"rubis
classe Operation < ActiveRecord::Base belongs_to :spectre_member
délégué: nom,: numéro, à:: spectre_member, préfixe: true
…
# spectre_member.name # end
# spectre_member.number # end
…
fin
@ operation.spectre_member_name @ operation.spectre_member_number
classe SpectreAgent < ActiveRecord::Base belongs_to :spectre_member
delegate: number, to:: spectre_member, préfixe: true
…
def superior_in_charge met “Mon patron est le numéro # spectre_member_number” fin
…
fin
"
Comme vous pouvez le constater, nous pourrions simplifier un peu les choses en utilisant la délégation de méthodes. Nous nous sommes débarrassés de Opération # spectre_member_name
et Opération # spectre_member_number
complètement et SpectreAgent
n'a pas besoin d'appeler nombre
sur spectre_member
plus-nombre
est déléguée directement à sa classe «d'origine» SpectreMember
.
Au cas où cela serait un peu déroutant au début, comment cela fonctionne-t-il exactement? Vous dites au délégué lequel : nom_méthode
il devrait déléguer à:
lequel :nom du cours
(plusieurs noms de méthodes sont bien aussi). le préfixe: true
la partie est optionnelle.
Dans notre cas, il préfixait le nom de la classe destinataire de la classe destinataire avant le nom de la méthode et nous permettait d'appeler operation.spectre_member_name
au lieu de la potentiellement ambiguë operation.name
-si nous n'avions pas utilisé l'option de préfixe. Cela fonctionne vraiment bien avec appartient à
et en a un
les associations.
Sur le a beaucoup
côté de choses, cependant, la musique s'arrête et vous aurez des ennuis. Ces associations vous fournissent un proxy de collection qui vous enverra NameErrors ou NoMethodErrors lorsque vous déléguerez des méthodes à ces "collections"..
Pour terminer ce chapitre sur les modèles AntiPatterns dans Rails, j'aimerais passer un peu de temps sur ce qu'il faut éviter lorsque SQL est impliqué. Les associations Active Record offrent des options qui facilitent considérablement votre vie lorsque vous savez à quoi vous tenir éloigné. Les méthodes de recherche constituent un sujet à part entière - et nous ne les aborderons pas en profondeur - mais je voulais mentionner quelques techniques courantes qui vous aideront même lorsque vous en écrivez de très simples..
Les choses qui devraient nous préoccuper font écho à la plupart de ce que nous avons appris jusqu'à présent. Nous souhaitons disposer de méthodes simples et raisonnablement désignées, révélatrices d'intention, permettant de rechercher des éléments dans nos modèles. Plongeons dans le code.
"rubis
classe Operation < ActiveRecord::Base
has_many: agents
…
fin
agent de classe < ActiveRecord::Base
appartient_à: opération
…
fin
classe OperationsController < ApplicationController
def index @operation = Operation.find (params [: id]) @agents = Agent.where (id_opération: @ operation.id, licence_to_kill: true) end end
"
Semble inoffensif, non? Nous recherchons simplement un groupe d’agents possédant le permis de tuer pour notre page ops. Pensez encore. Pourquoi le OperationsController
creuser dans les entrailles de Agent
? Aussi, est-ce vraiment le mieux que nous puissions faire pour encapsuler un chercheur sur Agent
?
Si vous pensez que vous pourriez ajouter une méthode de classe comme Agent.find_licence_to_kill_agents
qui encapsule la logique de recherche, vous faites certainement un pas dans la bonne direction - pas assez, cependant.
"rubis
agent de classe < ActiveRecord::Base
appartient_à: opération
def self.find_licence_to_kill_agents (operation) où (operation_id: operation.id, licence_to_kill: true) end…
fin
classe OperationsController < ApplicationController
def index @operation = Operation.find (params [: id]] @agents = Agent.find_licence_to_kill_agents (@operation) end end
"
Nous devons être un peu plus engagés que cela. Tout d'abord, cela n'utilise pas les associations à notre avantage et l'encapsulation est également sous-optimale. Des associations comme a beaucoup
venir avec l'avantage que nous pouvons ajouter sur le tableau de proxy que nous sommes retournés. Nous aurions pu le faire à la place:
"rubis
classe Operation < ActiveRecord::Base
has_many: agents
def find_licence_to_kill_agents self.agents.where (licence_to_kill: true) end…
fin
classe OperationsController < ApplicationController
def index @operation = Operation.find (params [: id]) @agents = @ operation.find_licence_to_kill_agents end end
"
Cela fonctionne, bien sûr, mais n’est qu’un pas de plus dans la bonne direction. Oui, le contrôleur est un peu meilleur et nous utilisons bien les associations de modèles, mais vous devriez toujours vous demander pourquoi. Opération
est concerné par la mise en œuvre de la recherche d'un certain type de Agent
. Cette responsabilité revient à la Agent
se modèle.
Les portées nommées sont très utiles avec cela. Les étendues définissent des méthodes chainable-very important-class pour vos modèles et vous permettent ainsi de spécifier des requêtes utiles que vous pouvez utiliser comme appels de méthode supplémentaires par-dessus vos associations de modèles. Les deux approches suivantes pour le cadrage Agent
sont indifférents.
"rubis
agent de classe < ActiveRecord::Base belongs_to :operation
scope: licenced_to_kill, -> where (licence_to_kill: true) end
agent de classe < ActiveRecord::Base belongs_to :operation
def self.licenced_to_kill où (licence_to_kill: true) se termine
classe OperationsController < ApplicationController
def index @operation = Operation.find (params [: id]) @agents = @ operation.agents.licenced_to_kill end end
"
C'est beaucoup mieux. Si la syntaxe des portées est nouvelle pour vous, il ne s'agit que de (stabby) lambdas - il n'est pas extrêmement important de les examiner tout de suite, au fait - et elles constituent le moyen approprié d'appeler des portées, car Rails 4. Agent
est maintenant en charge de la gestion de ses propres paramètres de recherche, et les associations peuvent simplement choisir ce dont elles ont besoin pour trouver.
Cette approche vous permet de réaliser des requêtes en tant qu'appels SQL uniques. Personnellement, j'aime utiliser portée
pour sa clarté. Les scopes sont également très pratiques pour chaîner des méthodes de recherche bien nommées, ce qui augmente les possibilités de réutilisation du code et du code DRY-ing. Disons que nous avons quelque chose d'un peu plus impliqué:
"rubis
agent de classe < ActiveRecord::Base belongs_to :operation
portée: licenced_to_kill, -> où (licence_to_kill: true) portée: womanizer, -> où (womanizer: true) portée: bond, -> où (nom: 'James Bond') portée: joueur, - > where (gambler: true) end
"
Nous pouvons maintenant utiliser toutes ces étendues pour personnaliser des requêtes plus complexes.
"rubis
classe OperationsController < ApplicationController
def index @operation = Operation.find (params [: id]) @double_o_agents = @ operation.agents.licenced_to_kill end
def show @operation = Operation.find (params [: id]) @bond = @ operation.agents.womanizer.gambler.licenced_to_kill end
… fin
"
Bien sûr, cela fonctionne, mais je voudrais vous suggérer d'aller plus loin.
"rubis
agent de classe < ActiveRecord::Base belongs_to :operation
portée: licenced_to_kill, -> où (licence_to_kill: true) portée: womanizer, -> où (womanizer: true) portée: bond, -> où (nom: 'James Bond') portée: joueur, - > où (joueur: vrai)
def self.find_licenced_to_kill licenced_to_kill end
def self.find_licenced_to_kill_womanizer womanizer.licenced_to_kill end
def self.find_gambling_womanizer gambler.womanizer end
…
fin
classe OperationsController < ApplicationController
def index @operation = Operation.find (params [: id]) @double_o_agents = @ operation.agents.find_licenced_to_kill end
def show @operation = Operation.find (params [: id]) @bond = @ operation.agents.find_licenced_to_kill_womanizer #ou @bond = @ operation.agents.bond end
…
fin
"
Comme vous pouvez le constater, cette approche nous permet de tirer parti des avantages d’une encapsulation correcte, des associations de modèles, de la réutilisation de code et de la dénomination expressive de méthodes, le tout tout en effectuant des requêtes SQL uniques. Pas plus de code spaghetti, génial!
Si vous craignez de ne pas enfreindre la loi de Demeter thingie, vous serez ravi d'apprendre que, puisque nous n'ajoutons pas de points en analysant le modèle associé mais en les enchaî