Partager via


Cet article a fait l'objet d'une traduction automatique.

Modèles dans les exercices pratiques

Convention sur la configuration

Jeremy Miller

Contenu

L'impact d'innovation de langue
Supposons que son une fois et uniquement une fois
Par défaut sensé
Convention de configuration
Suivant les étapes

Vous jamais pensé sur combien heure de votre projet est passé sur les problèmes de base et le temps passé wrestling avec problèmes purement techniques ? Certains peuvent indiquer que l'objectif plus importante de toutes les nouvelles technologies de logiciels et techniques est de réduire la distance entre intention le développeur du logiciel et la réalisation de cet objectif dans le code. Et l'industrie a générée en permanence le niveau d'abstraction pour permettre aux développeurs de passer plus de temps fonctionnalité et moins infrastructure temps écriture plus bas niveau, mais il existe toujours une solution longue pour accéder.

Pensez à ce pendant une minute. Si vous deviez afficher votre code pour vos représentants métier, en supposant qu'il s'agissait en fait prêt à lire le code avec vous, la quantité de ce code s'ils intéresse ? Ces Entreprise représentants sera probablement uniquement attention sur le code qui exprime les fonctionnalités métier du système. Ce code est « essence » du système. D'autre part, ils probablement n'ont le slightest intérêt dans code tel que déclarations de type, paramètres de configuration, blocs try/catch et contraintes génériques. Ce code est l'infrastructure, ou « cérémonie », que vous, le développeur, devez atteindre via simplement pour livrer code.

En versements précédentes de cet article j'ai principalement exploré conception fondamental concepts et principes, plus qui ont été partie canon la conception pendant assez longtemps. Ce temps autour, j'aimerais examiner quelques techniques plus récentes que vous pouvez adopter pour réduire la quantité de cérémonie de votre code. Certaines des ces concepts peut-être pour vous, mais je pense que tout cela feront partie de du .NET standard dans quelques années.

L'impact d'innovation de langue

Le premier facteur que je souhaite prendre en compte est le choix de programmation de langue et l'utilisation langages de programmation. Pour illustrer l'impact d'un langage de programmation de la quantité de cérémonie dans le code, prenons un peu detour dans les pages musty d'historique.

Plus haut dans ce cours des dix j'a été création d'un grand système dans Visual Basic 6.0. Chaque méthode serait ressembler à ce que vous voyez à la figure 1 . Chaque seul bit de que code était cérémonie.

Figure 1 ministres de cérémonie

Sub FileOperations()
    On Error Goto ErrorHandler

    Dim A as AType
    Dim B as AnotherType

    ' Put some code here

    Set A = Nothing
    Set B = Nothing
    ErrorHandler:
        Err.Raise Err.Number, _
            "FileOperations" & vbNewLine & Err.Source, _
            Err.Description, _
            Err.HelpFile, _
            Err.HelpContext
Exit Sub

J'AI utilisé une partie réutilisable juste sur chaque méthode pour créer un équivalent d'une trace de pile pour le débogage plus facile. J'AI dû également déréférencer les variables dans la méthode (jeu A = Nothing) pour lancer ces objets. J'AI eu une norme stricte de vérifications du code simplement vérifier que le nettoyage de gestion et l'objet erreur a été codé correctement sur chaque méthode. Le code réel, l'essence du système, flottante n'importe où au milieu de tout ce cérémonie.

Flash avant aujourd'hui et contemporain langues de programmation telles que C# ou Visual Basic .NET. Aujourd'hui, garbage collection éliminés la plupart du nettoyage de mémoire explicite que vous avez utilisé pour image dans la programmation Visual Basic 6.0. Traces de pile dans les exceptions sont intégrées dans Microsoft .NET Framework proprement dit, afin de ne plus avoir à faire pour vous-même. Si vous pensez sur tout le code réutilisable rote qui a été annulé lorsque vous déplacée vers le .NET Framework, vous pouvez dire que les langues compatible .NET sont plus productive et lisible que Visual Basic 6.0 en raison de la réduction en code cérémonie.

Le .NET Framework a une déviation de grosse, mais l'évolution dans les langues n'est pas encore terminée. Prenons qu'une propriété simple en LANGAGE c# implémentée la manière classique :

public class ClassWithProperty {
  // Higher Ceremony
  private string _name;
  public string Name {
    get { return _name; }
    set { _name = value; }
  }
}

L'essence de ce code est simplement que la classe ClassWithProperty a une propriété de chaîne nommée nom. Nous allons rapide vers l'avant à C# 3.0 et effectuez la même chose avec une propriété automatique :

public class ClassWithProperty {
  // Lower Ceremony
  public string Name { get; set; }
}

Ce code a l'intention de même exacte de la propriété style classique, mais il requis considérablement moins de code « compilateur parasites ».

En règle générale, cependant, les développeurs de logiciels ne pas pouvez absolue contrôler langages de programmation. Alors que je pense sans aucun doute que nous doit tirer parti des innovations de langage de programmation nouveau ou même d'autres langues, il est temps de parler des idées de conception que vous pouvez utiliser aujourd'hui avec standard en c# et Visual Basic.

Validation de domaine centrée sur

Le .NET Framework rend presque trivial pour ajouter déclarative validation au niveau des champs dans l'interface utilisateur avec des outils tels que les contrôles de validateur ASP.NET. Les mêmes, je pense qu'il est intéressant de placer logique de validation sur les classes de modèle de domaine réel ou au moins fermer en dans les services de domaine, pour ces raisons :

  1. Logique de validation est un problème de logique métier et je souhaitez préfère que toute la logique métier être contenus dans les classes de logique métier.
  2. Placer la logique de validation dans le modèle de domaine ou les services de domaine déconnectés de l'interface utilisateur potentiellement réduit la duplication sur les écrans et autorise la même logique de validation pour être utilisée dans les services interface non utilisateur (Web services, par exemple) exposées par l'application (indiquant unique et une seule fois, encore à nouveau).
  3. Il est beaucoup plus facile d'écrire des tests d'unité et acceptation contre la logique de validation dans le modèle que pour tester cette même logique implémentée en tant que partie de l'interface utilisateur.

Supposons que son une fois et uniquement une fois

Que se passe-t-il lorsque vous constatez cours via un projet quelque chose sur la définition d'un champ de données unique doit être modifié ? Bien trop souvent, cette petite modification à la base de données est ondulations à travers votre application que vous apportez une modification analogue à différentes parties de niveau intermédiaire, code d'accès aux données et même dans la couche d'interface utilisateur afin de prendre en charge une seule logique modifiez.

À un employeur passé, nous avons appelé que rippling effet de champ de données petite change le Anti-Pattern Wormhole. Vous souhaitez obtenir dehors de la nécessité d'une petite modification à un emplacement en cours teleported tout par les couches. Vous pouvez ralentir l'effet wormhole en réduisant les couches inutiles, et c'est la première étape. L'étape plus rewarding mais plus difficile consiste à trouver un moyen à dire qu'il une fois et une seule fois.

Les états dites son unique et uniquement une fois principal qui il doivent uniquement être une source faisant autoritée unique pour tout fait ou une stratégie dans le système dans son ensemble. Supposons prenez l'exemple de création d'une application Web à créer, lire, mettre à jour et supprimer de fonctionnalités (CRUD). Ce type de système est en grande partie préoccuper Modification et stockage des champs de données. Ces champs doivent être modifiés dans les écrans, validé sur le serveur, stocké dans la base de données et j'espère que validé sur le client et pour une meilleure expérience utilisateur, mais vous souhaitez spécifier que « ce champ est obligatoire et/ou ce champ doit contenir plus de 50 caractères » dans le code qu'une seule fois.

Il y a quelques de différentes approches que vous pouvez prendre. Vous pourriez rendre le schéma de base de données de la définition du masque et générer des code niveau intermédiaire et présentation de l'utilisateur du schéma. Vous pouvez également définissez les champs de données dans une sorte de stockage de métadonnées externe tel qu'un fichier XML, puis à utiliser génération de code pour créer le schéma de base de données, les objets de niveau intermédiaire et les écrans d'interface utilisateur. Personnellement, je ne suis pas un ventilateur de génération de code à grande échelle, pour mon équipe choisi une autre direction.

Nous généralement concevoir les classes de modèle de domaine tout d'abord et nous considérons la logique de validation pour être chargé des classes domaine modèle entité. Règles de longueur maximale de la chaîne et des règles de validation simple tels que les champs requis, nous décorer propriétés avec des attributs de contrôle tels que la classe adresse illustrée à la figure 2 .

Figure 2 Utilisation attributs de validation

public class Address : DomainEntity {
  [Required, MaximumStringLength(250)]
  public string Address1 { get; set; }

  [Required, MaximumStringLength(250)]
  public string City { get; set; }

  [Required]
  public string StateOrProvince { get; set; }

  [Required, MaximumStringLength(100)]
  public string Country { get; set; }

  [Required, MaximumStringLength(50)]
  public string PostalCode { get; set; }

  public string TimeZone { get; set; }
}

Utilisant des attributs d'est une technique simple et relativement courant pour spécifier les règles de validation. Vous pouvez également que depuis les règles de validation sont exprimées déclarative au lieu d'implémenté avec code impératif, vous avez transmis le test essence et cérémonie.

Cependant, vous devez maintenant répliquer les règles de champs obligatoires et longueur maximale de la chaîne dans la base de données. Dans le cas de mon équipe, nous utilisons NHibernate pour notre mécanisme de persistance. Une des fonctionnalités puissantes de NHibernate consiste à générer de code de langue (DDL) de définition de données à partir des mappages NHibernate que vous pouvez ensuite utiliser pour créer le schéma de base de données et maintenir synchronisées avec le modèle de domaine (cette stratégie à l'évidence fonctionne mieux dans les nouveaux projets). Pour cette stratégie de génération de la base de données à partir du modèle de domaine puisse être utile, il était nécessaire pour nous permet d'ajouter des informations pour les mappages NHibernate pour marquer les champs non null et spécifier longueur de chaîne.

Le nouveau mécanisme NHibernate Fluent nous permet de définir nos mappages d'objet. Dans notre code de programme d'installation pour NHibernate Fluent, nous établies conventions automatiques dans le mappage par enseigner NHibernate Fluent comment gérer la présence de le [Obligatoire] et les attributs [MaximumStringLength] dans notre classes du modèle avec le code illustré figure 3 .

La figure 3 traitement des attributs de NHibernate

public class MyPersistenceModel : PersistenceModel {
  public MyPersistenceModel() {
    // If a property is marked with the [Required]
    // attribute, make the corresponding column in
    // the database "NOT NULL"
    Conventions.ForAttribute<RequiredAttribute>((att, prop) => {
      if (prop.ParentIsRequired) {
        prop.SetAttribute("not-null", "true");
      }
    });

    // Uses the value from the [MaximumStringLength]
    // attribute on a property to set the length of 
    // a string column in the database
    Conventions.ForAttribute<MaximumStringLengthAttribute>((att, prop) => {
      prop.SetAttribute("length", att.Length.ToString());
    });
  }
}

Ces conventions sont maintenant appliquées à tous les mappages du projet. Pour la classe adresse, simplement savoir Fluent NHibernate les propriétés sont conservées :

public class AddressMap : DomainMap<Address> {
  public AddressMap() {
    Map(a => a.Address1);
    Map(a => a.City);
    Map(a => a.TimeZone);
    Map(a => a.StateOrProvince);
    Map(a => a.Country);
    Map(a => a.PostalCode);
  }
}

Maintenant que j'ai expliquées NHibernate Fluent concernant les attributs de validation, je peux générer le script DDL pour la table d'adresses (voir figure 4 ). Notez dans le code SQL dans la figure 4 que la longueur de chaîne correspond à la définition des attributs [MaximumStringLength] de la classe d'adresse. De même, la valeur Null/pas les valeurs NULL suivez dans les attributs [Obligatoire] à la classe d'adresse.

La figure 4 génération de code DDL

CREATE TABLE [dbo].[Address](
  [id] [bigint] IDENTITY(1,1) NOT NULL,
  [StateOrProvince] [nvarchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
  [Country] [nvarchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
  [PostalCode] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
  [TimeZone] [nvarchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
  [Address1] [nvarchar](250) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
  [Address2] [nvarchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
  [City] [nvarchar](250) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
PRIMARY KEY CLUSTERED 
(
  [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Nous avez investi dans une infrastructure dérivée de la structure de base de données les attributs de validation sur les objets du modèle de domaine, mais nous ont toujours le problème de validation côté client et côté client apparence. Nous aimerions effectuer des validations d'entrée simples dans le navigateur et marquer les éléments de champ obligatoire d'un moyen pour une meilleure expérience utilisateur.

La solution qui mon équipe sont arrivé à était pour rendre le rendu HTML du éléments entrées connaître les attributs de validation. (Pour contexte, nous utilisons le contrôleur de vue modèle ASP.NET, ou MVC, Framework bêta 1 avec le moteur de formulaires Web comme notre moteur d'affichage.) la figure 5 illustre ce que le balisage peut ressembler pour une vue d'adresses dans notre système.

La figure 5 adresse afficher marques

<div class="formlayout_body">
  <p><%= this.TextBoxFor(m => m.Address1).Width(300)%></p>
  <p><%= this.TextBoxFor(m => m.Address2).Width(300) %></p>
  <p>
    <%= this.TextBoxFor(m => m.City).Width(140) %>
    <%= this.DropDownListFor(m => 
        m.StateOrProvince).FillWith(m => m.StateOrProvinceList) %>
  </p>
  <p><%= this.TextBoxFor(m => m.PostalCode).Width(80) %></p>        
  <p><%= this.DropDownListFor(m => 
         m.Country).FillWith(m => m.CountryList) %></p>
  <p><%= this.DropDownListFor(m => 
         m.TimeZone).FillWith(m => m.DovetailTimeZoneList) %></p>
</div>

TextBoxFor et DropDownListFor sont des applications de peu HTML assistance dans la vue base courants qui sont utilisées pour tous les affichages de notre architecture MVC. La signature de TextBoxFor est illustrée ci-dessous :

public static TextBoxExpression<TModel> TextBoxFor< TModel >(
  this IViewWithModel< TModel > viewPage, 
  Expression<Func< TModel, object>> expression)

  where TModel : class {
    return new TextBoxExpression< TModel >(
    viewPage.Model, expression);
  }

L'important à noter dans ce code est que l'argument entrée est une expression (expression < Func < TModel, objet >> pour être précis). Lors de la construction le code HTML réel pour la zone de texte, la classe TextBoxExpression est :

  1. Analyser l'expression et recherchez l'objet PropertyInfo exacte de la propriété liée.
  2. Interroger ce PropertyInfo de l'existence des attributs de validation.
  3. En conséquence de rendre le code HTML pour la zone de texte.

Nous avons simplement ajouté une classe appelée requise pour tous les éléments HTML qui sont liés à propriétés marquées avec l'attribut [Obligatoire]. De même, si nous avons trouvé [MaximumStringAttribute] sur une propriété liée, nous définir l'attribut maxlength de la zone de texte HTML pour correspond à l'attribut tout en limitant la longueur d'entrée à autorisée utilisateur. Le code HTML obtenu ressemble à ceci :

<p><label>Address:</label>
<input type="text" name="Address1" value="" 
       maxlength="250" style="width: 300px;" 
       class="required textinput" /></p>

L'apparence des champs obligatoires est facile à contrôle simplement en modifiant l'apparence de la classe CSS requise (nous définir automatiquement la couleur des champs requis à l'écran pour un bleu clair). Nous l'avons fait la validation côté client réelle avec jQuery validation plug-in, qui, facilement suffisamment, simplement ressemble de l'existence de la classe requise sur les éléments entrées. La longueur maximale d'un élément de texte est appliquée tout simplement en définissant l'attribut maxlength sur les éléments entrées.

Par aucun moyen était cela une implémentation complète. Création de l'implémentation réelle dans le temps n'était pas difficile que. Le plus difficile a été penser à façons d'éliminer les métadonnées répétitives et de codage dans les couches plusieurs. Je suis certain que de nombreuses équipes ne sera pas keen sur la manière dont que mon équipe générées la base de données à partir du modèle d'objet, mais c'est OK car mon objectif réel est simplement pour vous donner quelques idées comment vous pouvez utiliser le dire une fois et uniquement une fois principal pour optimiser vos propres efforts de développement.

Par défaut sensé

Dans la section précédente, J'AI montré un exemple très petit (via une classe AddressMap) d'exprimer la relation d'objet (ORM) de mappage avec NHibernate Fluent. Voici un exemple légèrement plus complexe qui exprime une référence d'une classe de site à l'adresse objets :

public SiteMap() {
  // Map the simple properties
  // The Site object has an Address property called PrimaryAddress
  // The code below sets up the mapping for the reference between
  // Site and the Address class
  References(s => s.PrimaryAddress).Cascade.All();
  References(s => s.BillToAddress).Cascade.All();
  References(s => s.ShipToAddress).Cascade.All();
}

Lorsque vous configurez Outils ORM, vous avez généralement à :

  1. Spécifiez le nom table auquel se mappe une classe d'entité.
  2. Spécifier le champ de clé primaire d'une entité et généralement également spécifier un type de stratégie pour affecter les valeurs de clé primaire. (Est-il une séquence de numéro de base de données automatique ? Ou le système affecte les valeurs de clé primaire ? Ou vous utilisez les identificateurs globaux uniques (GUID) pour la clé primaire?)
  3. Mapper des propriétés de l'objet ou des champs sur colonnes du tableau.
  4. Lorsque vous effectuez une relation « à un » d'un seul objet à un autre (tels que le mappage du site vers l'adresse), vous devez spécifier la colonne de clé étrangère que l'outil ORM pouvez utiliser pour joindre d'enregistrements parent et enfant.

Tout cela est généralement fastidieux. Il est cérémonie de que vous devrez suivre pour obtenir le ORM pour conserver les objets. Heureusement, vous pouvez éliminer certaines le tedium en incorporant certaines valeurs par défaut sensé dans notre code (notez la présence de lettres majuscules).

Vous pouvez remarquer dans les exemples de mise en correspondance que je n'a pas spécifier un nom de table, une stratégie de clé primaire ou les noms de champ de clé étrangère. Dans NHibernate Fluent mappage superclasse mon équipe, nous avons défini des valeurs par défaut pour notre mappage :

public abstract class DomainMap<T> : ClassMap<T>, 
  IDomainMap where T : DomainEntity {

  protected DomainMap() {
    // For every DomainEntity class, use the Id property
    // as the Primary Key / Object Identifier
    // and use an Identity column in SQL Server,
    // or an Oracle Sequence
    UseIdentityForKey(x => x.Id, "id");
    WithTable(typeof(T).Name);
  }
}

Logiciel opinionated

Vous remarquerez peut-être décrite l'adoption de conventions que « restrictif ». Partie de la philosophie de convention sur configuration consiste à rendre « opinionated logiciel » qui crée des contraintes artificiels sur la conception.

Une infrastructure opinionated attend aux développeurs d'effectuer les opérations d'une manière certaine presque pour le point d'éliminer la flexibilité. Partisans du logiciel opinionated pensez que ces contraintes rendre développement plus efficace en supprimant des décisions des développeurs et promouvoir la cohérence.

Un avis utilisée par mon équipe est que toutes les classes de modèle de domaine sont complètement identifiés par une propriété longue unique appelée ID :

public virtual long Id { get; set; }

C'est une règle simple, mais il a eu un certain nombre d'impact important sur la conception. Étant donné que toutes les classes d'entité sont identifiés de la même manière, vous avez réussi à utiliser une classe référentiel unique au lieu d'écrire spécialisé classes du référentiel pour chaque entité de niveau supérieur. Dans le même vein, URL de gestion dans une application Web est cohérent entre classes entité sans devoir enregistrer certaines règles de routage pour chaque entité.

Suivant cette opinion réduit le coût de l'infrastructure pour ajouter un nouveau type d'entité. L'inconvénient de cette approche est qu'il serait très difficile pour prendre en charge une clé naturelle ou même une clé composite ou pour utiliser un GUID pour un identificateur d'objet. Qui n'est pas un problème de mon équipe, mais il peut facilement bloquer une autre équipe d'adopter notre avis.

Maintenant, la vous appliquer ces avis ? La première étape est simplement créer un courant comprendre et accord au sein d'une équipe sur ces avis. Les développeurs informés veille le risque préférable d'utiliser efficacement ces choix de conception opinionated à leur avantage. De même, convention de configuration peut être un incident proche si les développeurs ne sont pas familiarisés avec les conventions existantes ou les conventions sont déroutantes.

Vous pouvez également envisager d'utiliser un outil d'analyse de code statique dans le cadre de votre version d'intégration continue pour appliquer automatiquement les conventions de votre projet.

Dans ce code que nous définissez une stratégie que toutes les classes qui sous-classe Domain­Entity est identifiée par la propriété ID attribué par la stratégie d'identité. Le nom de la table est supposé pour être le même que le nom de la classe. À présent, nous pouvez toujours remplacer ces choix en fonction de la classe, mais nous avons dû rarement cela (une classe nommée utilisateur devait être mappé à une table intitulée utilisateurs uniquement pour éviter tout un conflit avec un mot réservé dans SQL Server). De la même manière, NHibernate Fluent suppose un nom de clé étrangère basé sur le nom de propriété qui fait référence à une autre classe.

D'accord, ce n'est pas l'enregistrement de nombreuses lignes de code par la classe de mappage, mais rend les parties du mappage qui varier plus facile à lire en réduisant le code de bruit global dans le mappage.

Convention de configuration

Les développeurs de logiciels ont recherchée obtenir plus de productivité et rendre les systèmes plus dynamique en déplaçant le comportement de code impératif et dans configuration XML déclarative. De nombreux développeurs pensé que la prolifération de configuration XML s'est passé trop loin et a été devenir une pratique dangereux. La stratégie par défaut sur la configuration explicite d'est également appelé convention sur configuration.

Convention de configuration est une philosophie de conception et la technique qui cherche à appliquer par défaut qui peut être déterminée de la structure du code au lieu de nécessitant code explicite. L'idée est de simplifier le développement en permettant au développeur de soucier uniquement les parties unconventional de l'application et l'architecture.

Droite maintenant, plusieurs personnes sont supprimez jouer avec l'infrastructure MVC ASP.NET et tester les différentes façons d'utiliser il. Dans le modèle MVC de développement Web Il existe plusieurs de sources de code répétitif peut être une opportunité idéale pour appliquer des conventions sur configuration.

Voici les cinq étapes dans le flux d'une demande unique dans le modèle MVC simple :

  1. Recevoir une URL du client. Le sous-système de routage est analyser le URL et déterminer le nom du contrôleur qui gère cette URL de.
  2. Du nom du contrôleur déterminé par le sous-système de routage, créer ou recherchez l'objet de contrôleur correcte.
  3. Appeler la méthode de contrôleur correcte.
  4. Sélectionnez la vue appropriée et regrouper les données de modèle générées à partir de la méthode contrôleur à cet affichage.
  5. Rendre l'affichage.

De la zone est une cérémonie répétitive dans Création de pages Web avec l'infrastructure ASP.NET MVC que vous pouvez atténuer en adoptant des conventions restrictives.

La première tâche consiste à se connecter une URL entrante au site Web avec la classe contrôleur correcte. La bibliothèque de routage dans le MVC Framework pouvez interroger une URL et déterminer le nom du contrôleur de. Le Framework MVC demandera ensuite l'objet IControllerFactory enregistré pour l'objet contrôleur qui correspond le contrôleur qui correspond au nom de contrôleur déterminé à partir de l'URL entrante.

De nombreuses équipes délèguent simplement construction contrôleur à une inversion de contrôle (IOC) outil. Dans les cas mon équipe, nous utiliser l'outil de StructureMap open source pour résoudre les instances du contrôleur par un nom :

public class StructureMapControllerFactory 
  : IControllerFactory {

  public IController CreateController(
    RequestContext requestContext, string controllerName) {

    // Requests the named Controller from the 
    // StructureMap container
    return ObjectFactory.GetNamedInstance<IController>(
      controllerName.ToLowerInvariant());
  }
}

Demander que le contrôleur est relativement simple, mais la première vous devez enregistrer toutes les classes du contrôleur par un nom avec le conteneur IOC. Attendez ! Qui n'est pas ajouter certains cérémonie à l'architecture ? Une année ou deux d'il y a que je serait ont plunged à l'avance avec configuration IOC explicite des classes contrôleur comme suit :

public static class ExplicitRegistration {
  public static void BootstrapContainer() {
    ObjectFactory.Initialize(x => {
      x.ForRequestedType<IController>().AddInstances(y => {
        y.OfConcreteType<AddressController>().WithName("address");
        y.OfConcreteType<ContactController>().WithName("contact");

        // and so on for every possible type of Controller
      });
    });
  }
}

Ce code représente tedium pur et cérémonie qui existe pour aucune autre raison qu'au flux l'outil IOC. Si vous regardez plus près le code d'enregistrement, vous remarquerez qu'il est suit un modèle cohérent. AddressController est enregistré en tant qu'adresse et Contact­Controller est enregistré comme contact. Au lieu de configuration explicitement chaque contrôleur, vous pouvez créer simplement une convention pour déterminer automatiquement le nom de gamme de chaque classe controller.

Heureusement, il est directement prise en charge dans StructureMap pour l'enregistrement en fonction de convention, afin que vous pouvez créer une nouveau ControllerConvention qui enregistre automatiquement un type concret de IController :

public class ControllerConvention : TypeRules, ITypeScanner {
  public void Process(Type type, PluginGraph graph) {
    if (CanBeCast(typeof (IController), type)) {
      string name = type.Name.Replace("Controller", "").ToLower();
      graph.AddType(typeof(IController), type, name);
    }
  }
}

Ensuite, vous devez du code qui amorce le conteneur StructureMap avec la nouvelle convention, comme illustré figure 6 . Une fois la nouveau ControllerConvention en place et partie du conteneur IOC démarrage, les nouvelles classes contrôleur que vous ajoutez à l'application sont automatiquement ajoutés à l'enregistrement IOC sans aucune configuration explicite de la part du développeur. Ainsi, il existe plus erreurs et bogues car un développeur oublié ajouter de nouveaux éléments de configuration pour les nouveaux écrans.

Figure 6 conventions de nouveau pour StructureMap

/// <summary>
/// This code would be in the same assembly as 
/// the controller classes and would be executed
/// in the Application_Start() method of your
/// Web application
/// </summary>
public static class SampleBootstrapper {
  public static void BootstrapContainer() {
    ObjectFactory.Initialize(x => {
      // Directs StructureMap to perform auto registration
      // on all the Types in this assembly
      // with the ControllerConvention
      x.Scan(scanner => {
        scanner.TheCallingAssembly();
        scanner.With<ControllerConvention>();
        scanner.WithDefaultConventions();
      });
    });
  }
}

Qu'un côté une note, je souhaite Expliquez que cette stratégie de l'enregistrement automatique est possible dans tous les conteneurs IOC que je suis conscient de pour .NET Framework tant que le conteneur IOC expose un enregistrement par programmation API.

Suivant les étapes

Dans la fin, il est à tous les propos de la réduction la distance et friction entre votre intention et le code qui effectue cette intention de se produire.De nombreuses les techniques que J'AI présenté dans cette colonne sont véritablement sur laisser le code « simplement figure il hors » d'utiliser des conventions d'appellation au lieu de code explicite ou recherche de méthodes pour éviter les doublons dans le système.J'AI également utilisé certaines techniques réfléchissant pour réutiliser les informations cachées dans attributs pour réduire l'effort mécanique.

Toutes ces idées de conception peuvent réduire la cérémonie répétitive les efforts de développement, mais il s'agit à un prix.Detractors de convention sur configuration plaindre les « magiques » inhérente de l'approche.Incorporation avis dans votre code ou la structure perturbe de réutilisation potentielle dans nouveaux scénarios où les opinions ne sont pas comme favorables.

Il y avait beaucoup d'autres rubriques qui j'a pas pu couvrir cette fois que J'AI peuvent concerner dans une colonne d'une version ultérieure.J'AI sans aucun doute découvrir la programmation orientée la langue ; autres langues comme F #, IronRuby et IronPython ; et le Utilisation interne langue propres au domaine affecte le processus de création de logiciels.

Veuillez envoyer vos questions et commentaires àmmpatt@microsoft.com.

Jeremy Leroy, MVP Microsoft pour Visual c# est également l'auteur de la source ouverteStructureMapoutil de l'injection de dépendance avec .NET et la venirOutil storyTellerPour supercharged FIT test dans .NET.Consulter son blogLe développeur d'organigramme Affichage, du site CodeBetter.