Este artigo foi traduzido por máquina.

Padrões em prática

Convenção sobre configuração

Jeremy Miller

Conteúdo

O impacto da inovação de idioma
Diga-lo uma vez e somente uma
Padrões adequados
Convenção sobre configuração
Próximas etapas

Ter você já pensou sobre quanto tempo no seu projeto é gasto em problemas principais versus o tempo que gasto wrestling com questões puramente técnicas? Alguns podem dizer que o objetivo mais importante de todas as novas tecnologias de software e técnicas é reduzir a distância entre a intenção do desenvolvedor do software e a percepção de que intenção no código. E a indústria tem disparados continuamente o nível de abstração para permitir que os desenvolvedores a gastar mais tempo para fornecimento de funcionalidade e de menor nível baixo de escrita do tempo infra estrutura — mas ainda há um longo caminho para ir.

Pense sobre isso por um minuto. Se você tivesse que mostre o código a seus representantes de negócios — supondo que estavam realmente dispostos a ler o código com você — quanto do que o código seria nos importa? Os negócios representantes será provavelmente somente cuidado sobre o código que expressa a funcionalidade de negócios do sistema. Esse código é a "essência" do sistema. Por outro lado, eles provavelmente não têm o interesse slightest no código como declarações de tipo, definições de configuração, blocos try/catch e restrições genéricas. Esse código é a infra-estrutura, ou "cerimônia", que você, desenvolvedor, precisa passar por apenas para a entrega de código.

Em prestações anteriores desta coluna tenha amplamente explorou conceitos de design fundamental e princípios, mais deles foram parte a canon de design por bastante algum tempo. Este tempo aproximadamente, gostaria examinar algumas técnicas mais recentes que você pode adotar para reduzir a quantidade de cerimônia em seu código. Talvez alguns desses conceitos novos para você, mas eu acredito que tudo isso fará parte do .NET importante em alguns anos.

O impacto da inovação de idioma

O primeiro fator que gostaria de considerar é a escolha de idioma e como usar linguagens de programação de programação. Para ilustrar o impacto de uma linguagem de programação na quantidade de cerimônia no código, vamos levar um desvio pequeno nas páginas musty do histórico.

Anteriormente neste década estava criando um sistema grande no Visual Basic 6.0. Cada método poderia se parecer com o que você vê na Figura 1 . Cada único bit de que o código foi cerimônia.

Figura 1 em pé em cerimônia

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

Usei um volume razoável de clichê em cada método para criar um equivalente de um rastreamento de pilha para depuração mais fácil. Também tive de referência a variáveis dentro do método (um conjunto = Nothing) para liberar esses objetos. Eu tinha um padrão estrito para revisões de código apenas verificar que a limpeza de manipulação e o objeto de erro foi codificada corretamente em cada método. O código real, a essência do sistema, flutuou em algum lugar no meio de todos os que cerimônia.

Flash encaminhar para hoje e contemporâneo linguagens de programação, como C# ou Visual Basic. NET. Hoje, coleta de lixo eliminado a maioria da limpeza explícita de memória que você usou para frente na programação do Visual Basic 6.0. Rastreamentos de pilha em exceções são criados no Microsoft .NET Framework, para que você não precise fazer isso para você. Se você pensar em todo o código clichê rote foi eliminado quando você moveu para o .NET Framework, pode diz que as linguagens compatíveis com. NET mais produtivo e legível do que o Visual Basic 6.0 devido a redução no código da cerimônia.

O .NET Framework foi um grande salto, mas a evolução em idiomas não está concluída ainda. Vamos considerar que uma propriedade simples em C# implementado da forma clássica:

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

A essência desse código é simplesmente que a classe ClassWithProperty tem uma propriedade de cadeia de caracteres denominada nome. Vamos rápida frente para o C# 3.0 e fazer a mesma coisa com uma propriedade automaticamente:

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

Esse código tem a intenção de mesma exata como a propriedade de estilo clássico, mas ele necessário notavelmente menos código de ruído de compilador.

Em geral, no entanto, os desenvolvedores de software não tem absoluto controle sobre linguagens de programação. Embora, sem dúvida, acho que nós deve aproveitar inovações de linguagem de programação ou idiomas mesmo alternativos, é hora de falar sobre idéias de design que você pode usar hoje em dia com importante C# e Visual Basic.

Validação centralizada do domínio

O .NET Framework torna quase trivial adicionar validação de nível de campo declarativa na interface do usuário com ferramentas tais como os controles do validador do ASP.NET. Todos os mesmos, eu acho vantajoso para colocar a lógica de validação nas classes de modelo de domínio real, ou pelo menos fechar, nos serviços de domínio, por esses motivos:

  1. Lógica de validação é uma preocupação de lógica comercial e preferir que toda a lógica comercial estar contidos nas classes de lógica comercial.
  2. Colocar a lógica de validação no modelo de domínio ou os serviços de domínio desconectados da interface do usuário potencialmente reduz a duplicação em telas e permite que a mesma lógica de validação a ser exercidos nos serviços de interface não-usuário (serviços de Web, por exemplo) expostos pelo aplicativo (informando que uma e apenas uma, ainda novamente).
  3. É muito mais fácil compor testes de unidade e aceitação contra lógica de validação no modelo que é para testar essa mesma lógica implementada como parte da interface do usuário.

Diga-lo uma vez e somente uma

O que acontece quando você descobrir situadas através de um projeto que algo sobre a definição de um único campo de dados precisa ser alterado? Em muito muitos casos, essa alteração pequena para o banco de dados será ripple por meio de seu aplicativo ao fazer uma alteração análoga em várias partes da camada intermediária, código de acesso a dados e até mesmo para a camada da interface usuário para acomodar uma única lógica alterar.

Em um empregador passado, chamamos que rippling efeito do campo de dados pequeno muda o Anti-Pattern da fenda espacial. Você deseja obter longe a necessidade de uma pequena alteração em um local que está sendo teleported todo pelas camadas. Você pode tornar lento o efeito da fenda espacial, reduzindo a disposição desnecessária, e que é a primeira etapa. A etapa mais difícil, mas mais interessante é para encontrar uma maneira de dizer que uma vez e apenas uma vez.

Os estados de dizer isso uma e somente uma vez principal que só devem haver uma fonte de autoritativa única para qualquer fato ou a diretiva no sistema como um todo. Vamos pegar o exemplo de criação de um aplicativo da Web com criar, ler, Atualizar e Excluir (CRUD) funcionalidade. Esse tipo de sistema é amplamente preocupado com a edição e armazenar os campos de dados. Esses campos precisam ser editados em telas, validado no servidor, armazenados no banco de dados e esperamos que validado no cliente bem para uma melhor experiência de usuário, mas você gostaria de especificar e / que "Este campo é obrigatório ou este campo deve ser mais de 50 caracteres" no código uma só vez.

Há algumas das abordagens diferentes, que você pode levar. Você pode tornar o esquema do banco de dados a definição do mestre e gerar o código de camada intermediária e apresentação de usuário do esquema. Você também pode definir os campos de dados em algum tipo de armazenamento de metadados externo, como um arquivo XML e usar geração de código para criar o esquema do banco de dados, os objetos de camada intermediária e as telas de interface do usuário. Pessoalmente, não tenho um fã de geração de código em larga escala, portanto, minha equipe escolher outra direção.

Nós geralmente projetar as classes de modelo de domínio primeiro e Consideramos que a lógica de validação para ser de responsabilidade das classes de entidade do modelo de domínio. Para as regras de validação simples, como campos obrigatórios e regras de tamanho máximo da seqüência, nós Decore propriedades com atributos de validação, como a classe de endereço mostrada na Figura 2 .

A Figura 2 usando atributos de validação

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; }
}

Usando atributos é uma técnica simples e bastante comum para especificar regras de validação. Você também é capaz de dizer que, desde que as regras de validação são expressos declarativamente em vez de implementado com código imperativo, você tem passou no teste essência versus cerimônia.

Agora, no entanto, você precisa replicar as regras para campos obrigatórios e comprimentos de seqüência de caracteres máximo para o banco de dados. Em caso de minha equipe, usamos NHibernate para nosso mecanismo de persistência. Um dos recursos poderosos de NHibernate é gerar o código DDL (linguagem de definição de dados dos mapeamentos de NHibernate que você pode usar para criar o esquema do banco de dados e mantê-lo em sincronia com o modelo de domínio (essa estratégia, obviamente, funciona melhor em novos projetos). Em ordem para essa estratégia de gerar o banco de dados do modelo de domínio para ser útil, era necessário para nós adicionar informações para os mapeamentos de NHibernate para marcar campos não-nulos e especifique os comprimentos de seqüência de caracteres.

Nós usamos o novo mecanismo de NHibernate Fluent para definir nossos mapeamentos de objeto. No nosso código de configuração para NHibernate Fluent, é estabelecida convenções automáticas no mapeamento, ensinar NHibernate Fluent como tratar a presença do [necessários] e [MaximumStringLength] atributos nossas classes de modelo com o código mostrado na Figura 3 .

A Figura 3 manipulação de atributos em 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());
    });
  }
}

Essas convenções agora serão aplicadas a todos os mapeamentos no projeto. Para a classe de endereços, basta dizer NHibernate Fluent quais propriedades são mantidas:

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);
  }
}

Agora que eu tenha ensinado NHibernate Fluent sobre os atributos de validação, pode gerar o DDL para a tabela de endereços (consulte a Figura 4 ). Observe de SQL na Figura 4 que os comprimentos de seqüência de caracteres coincidir com a definição dos atributos de [MaximumStringLength] na classe de endereço. Da mesma forma, o NULL/valores NULL não siga dos atributos [necessários] na classe de endereço.

A Figura 4 Gerando código 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]

Nós você investiu em algumas infra-estrutura que deriva a estrutura do banco de dados os atributos de validação em objetos de modelo de domínio, mas ainda temos a questão de validação do lado do cliente e a aparência do lado do cliente. Gostaríamos executar as validações de entrada simples dentro do navegador e marcar os elementos de campo obrigatório de alguma forma para uma melhor experiência do usuário.

A solução que minha equipe chegou ao era fazer o processamento de HTML de elementos input atento os atributos de validação. (Para contexto, estamos usando o ASP.NET Model View Controller ou MVC, Beta1 Framework com o mecanismo de formulários da Web como nosso mecanismo de modo de exibição.) Figura 5 mostra o que a marcação pode parecer para uma exibição de endereços em nosso sistema.

A Figura 5 endereços Exibir marcação

<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 e DropDownListFor são pouco auxiliares HTML no modo de exibição base comuns que são usados para todos os modos de nossa arquitetura MVC. A assinatura de TextBoxFor é mostrada aqui:

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);
  }

O importante observar nesse código é de que o argumento de entrada é uma expressão (expressão < Função < TModel, objeto >> para ser preciso). Ao construir o HTML real para a caixa de texto, a classe TextBoxExpression irá:

  1. Analisar a expressão e localizar o objeto de PropertyInfo exato para a propriedade ligada.
  2. Interrogar que PropertyInfo a existência dos atributos de validação.
  3. Processa o HTML para a caixa de texto de acordo.

Simplesmente adicionamos uma classe chamada necessário para todos os elementos HTML que são vinculados a propriedades marcadas com o atributo [Required]. Da mesma forma, se nós encontrado [MaximumStringAttribute] em uma propriedade ligada, defina o atributo maxlength da caixa de texto HTML para corresponde ao atributo e limitar os comprimentos de entrada para permitido do usuário. O HTML resultante é semelhante esta:

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

A aparência dos campos obrigatórios é fácil controle simplesmente editando a aparência da classe CSS necessária (é definir a cor de campos obrigatórios na tela para um azul claro). Fizemos a validação do cliente real com jQuery validação plug-in, que, convenientemente suficiente, simplesmente parece a existência da classe necessária em elementos de entrada. O comprimento máximo de um elemento de texto é imposto simplesmente definindo o atributo de maxlength sobre os elementos de entrada.

Por nenhum meio era isso uma implementação completa. A implementação real com o tempo de criação não era tão difícil. A parte difícil estava pensando por meio de maneiras de eliminar os repetitivos metadados e codificando em várias camadas. Tenho certeza de que muitas equipes não será bacana na maneira como que minha equipe gerado o banco de dados do modelo de objeto, mas que é OK porque meu objetivo real apenas é oferecer algumas idéias sobre como você pode usar a dizê-lo uma vez e somente uma vez principal para simplificar seus próprios esforços de desenvolvimento.

Padrões adequados

Na seção anterior, mostrei um exemplo muito pequeno (por meio de uma classe AddressMap) de expressar a relação de objeto (ORM) de mapeamento com NHibernate Fluent. Aqui está um exemplo um pouco mais complicado que expresse uma referência de uma classe de site para objetos de endereço:

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();
}

Ao configurar as ferramentas ORM, você geralmente precisa:

  1. Especifique o nome de tabela para que uma classe de entidade mapas.
  2. Especifique o campo de chave primário de uma entidade e geralmente também especificar algum tipo de estratégia para atribuir os valores de chaves primária. (É uma seqüência de número/banco de dados automática? Ou o sistema de atribuir os valores de chaves primária? Ou faça usar GUIDs para a chave primária?)
  3. Mapear campos ou propriedades do objeto para colunas da tabela.
  4. Ao estabelecer uma relação "para um" de um objeto para outro (como o mapeamento do site para endereço), você precisará especificar a coluna de chave externa que a ferramenta ORM pode usar para associar registros pai e filho.

Tudo isso é geralmente tedioso trabalho. É cerimônia que você deve seguir para que o ORM para manter os objetos. Felizmente, você poderá eliminar alguns do tédio incorporando alguns padrões adequado em nosso código (Observe a presença de letras maiúsculas).

Você pode observar os exemplos de mapeamento que eu não especificou um nome de tabela, uma estratégia de chave primária ou qualquer nome de campo de chave externa. Em NHibernate Fluent mapeamento superclasse minha equipe, definimos alguns padrões para o nosso mapeamento:

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);
  }
}

Software opinionated

Você pode observar que descrevi a adoção de convenções como "restritivas." Parte a filosofia de convenção sobre configuração é fazer "software opinionated" que cria artificiais restrições sobre o design.
Uma estrutura opinionated espera que os desenvolvedores a fazer coisas de uma determinada maneira quase ao ponto de eliminação de flexibilidade. Proponentes de software opinionated sentem-se que essas restrições tornar desenvolvimento mais eficiente, removendo decisões dos desenvolvedores e promover a consistência.

Uma opinião usado por minha equipe é que todas as classes de modelo de domínio completamente são identificadas por uma única propriedade longa chamada ID:

public virtual long Id { get; set; }

É uma regra simples, mas que tenha tido um número de impactos profundos no design. Porque todas as classes de entidade são identificadas da mesma forma, você for capaz de usar uma classe de único repositório em vez de escrever especializados classes de repositório para cada entidade de nível superior. Na mesma direção, URL tratamento em um aplicativo da Web é consistente em classes de entidade sem a registrar regras de roteamento especiais para cada entidade.

Após esse opinião reduz o custo da infra-estrutura para adicionar um novo tipo de entidade. A desvantagem dessa abordagem é que seria muito difícil para acomodar uma chave natural ou mesmo uma chave composta ou usar um GUID para um identificador de objeto. Isso não é um problema para minha equipe, mas ele pode bloquear facilmente outra equipe da adoção de nossa opinião.

Agora, como você aplicar essas opiniões? A primeira etapa simplesmente está criando um comuns Noções básicas sobre e contrato dentro de uma equipe sobre essas opiniões. Desenvolvedores informados espera a melhor possibilidade de com eficiência usando essas opções de design opinionated a seu favor. Da mesma forma, os convenção sobre configuração pode ser a um desastre quase se os desenvolvedores não estiver familiarizados com as convenções existentes ou as convenções são confusas.

Você também pode considerar usando uma ferramenta de análise estática de código como parte de sua compilação de integração contínua para aplicar automaticamente as convenções do seu projeto.

Nesse código que está configurando uma diretiva que todas as classes que subclasse Domain­Entity será identificada pela propriedade ID que é atribuída com a estratégia de identidade. Assume-se a tabela que o nome é o mesmo que o nome da classe. Agora, nós sempre pode substituir essas opções em uma base por classe, mas que raramente já tínhamos de fazer isso (uma classe chamada usuário tinha que ser mapeado para uma tabela denominada Users apenas para evitar um conflito com uma palavra reservada no SQL Server). Da mesma forma, NHibernate Fluent assume um nome de chave externo com base no nome da propriedade que faz referência a outra classe.

Certo, isso não salvar várias linhas de código por classe de mapeamento, mas faz as partes do mapeamento que variar mais fácil a lido por reduzir o código de ruído geral no mapeamento.

Convenção sobre configuração

Os desenvolvedores de software tem buscou obter mais produtividade e tornar os sistemas mais dinâmico, movendo comportamento fora do código imperativo e em Configuração do XML declarativa. Muitos desenvolvedores achamos que a proliferação de configuração XML deu muito e foi se tornando uma prática prejudicial. A estratégia de padrões sobre configuração explícita é também conhecido como convenção sobre configuração.

Convenção sobre configuração é uma filosofia de design e a técnica que procura para aplicar padrões que podem ser implícitas da estrutura do código em vez de solicitar código explícito. A idéia é simplificar o desenvolvimento, permitindo que o desenvolvedor se preocupar somente as partes unconventional do aplicativo e a arquitetura.

À direita agora, muitas pessoas são cuidadosamente brincar com o Framework de MVC ASP.NET e Testar maneiras diferentes de usá-lo. No modelo de MVC do desenvolvimento da Web, há algumas de fontes de código repetitivo, que pode ser uma ótima oportunidade para aplicar as convenções sobre configuração.

Aqui estão as etapas cinco no fluxo básico de uma única solicitação no modelo MVC:

  1. Recebe uma URL de cliente. O subsistema de roteamento analisará a URL e determinar o nome do controlador de que lida com essa URL.
  2. O nome do controlador de determinado pelo subsistema de roteamento, criar ou localizar o objeto controlador adequado.
  3. Chamar o método correto do controlador.
  4. Selecione o modo de exibição adequado e empacotar os dados de modelo gerados do método controlador para esse modo de exibição.
  5. Processa o modo de exibição.

Fora da caixa de, há alguns cerimônia repetitiva na criação de páginas da Web com o Framework MVC ASP.NET que você pode mitigar adotando algumas convenções restritivas.

A primeira tarefa é conectar uma URL de entrada ao site da Web a classe controller adequado. A biblioteca de roteamento no Framework MVC pode interrogar uma URL e determinar o nome do controlador de. O Framework MVC pedirá o objeto IControllerFactory registrado para o objeto controlador que coincide com o controlador que corresponde ao nome controlador determinado a partir o URL de entrada.

Muitas equipes simplesmente delegam construção controlador a uma inversão da ferramenta de controle (IOC). No caso de minha equipe, é usar a ferramenta de StructureMap do código-fonte aberto para resolução de instâncias de controlador por nome:

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());
  }
}

Solicitando que o controlador é bastante simples, mas primeiro você precise registrá todas as classes de controlador pelo nome com o recipiente IOC. Espere! Que não É adicionar alguns cerimônia da a arquitetura? Um ano ou dois há que eu poderia ter plunged em frente com configuração de IOC explícita das classes de controlador como essa:

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
      });
    });
  }
}

Este código representa tédio puro e cerimônia existe para nenhum outro motivo que para feed a ferramenta IOC. Se você examinar mais de perto o código de registro, você observará que ela é seguindo um padrão consistente. AddressController é registrado como endereço e Contact­Controller está registrado como contato. Em vez de configurar explicitamente cada controlador, você pode simplesmente criar uma convenção para determinar automaticamente o nome de roteamento de cada classe de controlador.

Felizmente, há suporte direto em StructureMap para registro com base em convenção, para que você possa criar um novo ControllerConvention que registra qualquer tipo concreto de IController automaticamente:

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);
    }
  }
}

Em seguida, você precisa de um código que inicializa o contêiner StructureMap com a nova convenção, conforme mostrado na Figura 6 . Depois que o novo ControllerConvention estiver no lugar e parte do recipiente IOC carregando, quaisquer novas classes de controlador que você adiciona ao aplicativo serão automaticamente adicionadas ao Registro IOC sem qualquer configuração explícita por parte do desenvolvedor. Portanto, há não mais erros e erros porque um desenvolvedor esqueceu de adicionar novos elementos de configuração para novas telas.

Figura 6 convenções de novas para 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();
      });
    });
  }
}

Como uma observação de lado, quero explicar que essa estratégia de registro automático é possível em todos os recipientes de IOC que estou ciente do .NET Framework, desde que o recipiente IOC expõe um API de registro através de programação.

Próximas etapas

No final, é tudo sobre a redução a distância e atrito entre sua intenção e o código que torna essa intenção acontecer. Muita as técnicas que mostrei nesta coluna são realmente sobre permitindo que o código "apenas descobrir" de usando as convenções de nome em vez de código explícito ou localizar formas de evitar duplicar informações no sistema. Eu também usado algumas técnicas de reflexão para reutilizar informações enterradas nos atributos para reduzir o esforço mecânico.

Todas essas idéias de design podem reduzir a cerimônia repetitiva do esforço de desenvolvimento, mas ele tem um preço. Redutores de convenção sobre configuração reclamam sobre a "mágica" inerente da abordagem. Incorporar opiniões seu código ou a estrutura será reduz possível reutilização em novos cenários onde essas opiniões não como favorável.

Houve muita de outros tópicos que eu estava não é possível cobrir dessa vez que eu pode cobrir em uma próxima coluna. Definitivamente, pretendo explorar a programação orientada a como idioma; idiomas alternativos como F #, o IronRuby e o IronPython; e o uso de linguagem de específica de domínio interno afeta o processo de design de software.

Envie suas dúvidas e comentários parammpatt@microsoft.com.

Jeremy Miller, um MVP da Microsoft para o C#, também é autor do código-fonte abertoStructureMapferramenta de injeção de dependência com .NET e o forthcomingFerramenta storyTellerpara supercharged FIT testes em .NET. Visite seu blog,O desenvolvedor de árvore de sombra, parte do site CodeBetter.