Posts com o tag ‘.NET’

17out2011

Vem aí a RubyConf Brasil 2011 by Locaweb!

(0) comentários

A Locaweb, com a colaboração de Fabio Akita, realiza nos dias 03 e 04 de novembro a RubyConf Brasil by Locaweb 2011, evento que promoverá discussões técnicas sobre as linguagens Ruby, Java e .NET e plataformas de código aberto em geral.

Com público estimado de 750 programadores e desenvolvedores, a missão da RubyConf 2011 é difundir melhores práticas e proporcionar um conhecimento que possa ser utilizado no ambiente de trabalho rapidamente. Trata-se de uma grande oportunidade para ampliar todo o conhecimento técnico e desenvolver o relacionamento com outros profissionais da área.

Patrocinada pela GoNow, Globo.COM, PayPal e Dextra, além do apoio VMware, Garage, Groupon, ToughtWorks, Safew Labs, Pusher, Engine Yard e Cubox, esta 4ª edição da Conferência conta com palestrantes de renome internacional falando sobre a linguagem Ruby, integração de tecnologias Ruby e Java, utilização de cloud computing para criar produtos baseados em Ruby, grandes cases como Groupon, melhores práticas de desenvolvimento de software atuais, empreendedorismo e agilidade.

Clique na imagem abaixo e conheça os palestrantes:


As inscrições continuam abertas pelo site do evento ou via Facebook, na Fan Page de Eventos Locaweb. Pra quem quiser acompanhar pelo Twitter, a hashtag é #rubyconfbr.

No dia 27 de setembro aconteceu o 3º BarCamp realizado pela Locaweb para comemorar o lançamento do website da RubyConf Brasil 2011. É o terceiro encontro anual que fazemos como um pré-evento para que desenvolvedores e rubistas se encontrem para discutir código, fazer networking e confraternizar. Confira como foi o 3º BarCamp Locaweb:

 

Rubistas, a RubyConf Brasil 2011 by Locaweb vem aí, participem!

4nov2010

RUBYCONF BY LOCAWEB É SUCESSO DE PÚBLICO

(0) comentários

Superando o ano de 2009, evento reuniu 700 pessoas e reuniu os maiores entusiastas da linguagem de programação Ruby!

A RubyConf Brasil by Locaweb, evento que reúne os maiores desenvolvedores da linguagem de programação Ruby, cumpriu seu objetivo: na edição de 2010, realizada nos dias 26 e 27 de outubro, no Centro de Convenções Shopping Frei Caneca, em São Paulo, cerca de 700 pessoas compareceram e assistiram a palestras de Charles Nutter, David Chelimksy, Ola Bini, Jim Weirich, além de brasileiros como Fábio Akita, José Valim e Pedro Franceschi, que tem 14 anos de idade e já trabalha em diversos projetos em Ruby, entre outros.

No Twitter, a RubyConf Brasil também mostrou seu potencial: foram 1.899 menções nos dois dias do evento, com um percentual favorável de 75% dos visitantes, que elogiaram o conteúdo das palestras e dos estandes, sendo que as menções continuaram, somando quase 4.000 até o dia 3/11/2010. Destaque para René de Paula, gerente de relacionamento com desenvolvedores da Locaweb, que utilizou diversas fotografias dos visitantes para montar um mosaico que, visto de longe, monta o rosto de Fábio Akita, um dos palestrantes. Quando a imagem é aproximada, aparecem as fotografias dos visitantes.

A RubyConf Brasil by Locaweb contou com patrocínios de GoNow, ThoughtWorks, PayPal, Caelum e Engine Yard, além de ser apoiada pelo GitHub, InfoQ e a própria ONG Ruby Central, que emprestou seu nome para o evento nesta edição de 2010.

Confira o vídeo do evento, produzido por Fábio Akita:

O download desta notícia você encontra no nosso Press.

25out2010

Começa amanhã o RubyConf Brasil by Locaweb. Concorra a VIPs!

(1) comentário

Amanhã começa o RubyConf Brasil, evento organizado pela Locaweb que reúne programadores e desenvolvedores Ruby on Rails, Java, .NET e plataformas de código aberto em geral.

Com duas salas simultâneas, queremos apresentar conteúdo altamente relevante oferecendo palestras com os principais “Rubistas” do mundo, aprendizado técnico, relacionamento entre os profissionais da área e a melhor expeciência. Estarão disponíveis tradutores de português-inglês e inglês-português em ambas as salas e sessões do evento.

Desconferência
Ao final do primeiro dia do evento, acontecerá uma sessão de desconferência. Para participar basta sugerir assuntos relacionados diretamente a Ruby, Rails, Empreendedorismo, Metodologias Ágeis. Você terá de 10 a 15 min para sua apresentação, leve seus slides preparados no seu notebook e não se esqueça do adaptador para VGA!

As inscições estão encerradas pelo site oficial. Mas quem for ao evento, também conseguirá realizar a inscrição no local, a R$250, somente em dinheiro.

PROMOÇÃO NO TWITTER ENCERRADA!

Mas para quem não conseguiu garantir a participação no RubyConf Brasil, uma promoção relâmpago valendo SOMENTE HOJE para nossos seguidores no Twitter!

Para concorrer é simples: basta twittar (somente uma vez) a frase abaixo e já estará concorrendo a 1 entrada no evento:

Quero um VIP para o evento de #rails mais esperado do ano!  http://kingo.to/jPk #rubyconfbr

O sorteio foi realizado às 18h de hoje. Tivemos 5 ganhadores, que foram anunciados  no nosso Twitter! Os dados para cadastro no evento serão enviados via DM (direct message) no Twitter.

Bom evento a todos! Nos vemos amanhã!

29set2010

RubyConf Brasil 2010 by Locaweb. Já garantiu sua inscrição?

(0) comentários

A Locaweb, em toda sua história, tem buscado investir em eventos que levam conhecimento e evolução ao mercado de internet. Foi assim, por exemplo, que idealizamos a criação do Rails Summit Latin America, dois anos atrás. Com esta visão, realizamos também anualmente o Encontro Locaweb de Profissionais de Internet, que em 2010 reuniu mais de 3.000 pessoas em sua 12ª edição.

Com o sucesso do Rails Summit nos últimos anos, já podemos dizer que é um evento que veio para ficar. Ele nasceu em 2008 de uma iniciativa da Locaweb com Fábio Akita, referência em Rails no Brasil, e agora, em sua 3º edição neste ano (26 e 27 de outubro), recebe um “upgrade” e passa a se chamar RUBY CONF BRASIL.

Essa mudança só ocorreu por termos conseguido a permissão da RubyCentral para utilização do nome, além de apoio ao conteúdo, devido não só à credibilidade conquistada com as edições anteriores, mas também pela grande audiência que o público trouxe a elas.

Formato: com duas salas simultâneas, o evento reúne programadores e desenvolvedores Ruby on Rails, Java, .NET e plataformas de código aberto em geral. Posicionado para atender profissionais com alto nível técnico, o evento está programado para reunir aproximadamente 750 programadores e desenvolvedores nesta edição.

As inscrições com valor promocional (R$ 200) vão até o dia 30/09. Após essa data, o valor é de R$ 250.

Em parceria com a Caelum, reconhecido centro de treinamentos e uma das empresas que apóia a comunidade Ruby on Rails, estamos oferecendo uma aula gratuita no centro de treinamentos da Caelum, para quem ainda está começando com Ruby e Rails, um dia antes do evento!

Confira a grade de programação repleta de nomes de peso da comunidade Ruby e inscreva-se!

17ago2010

Hospedagem Locaweb com Windows 2008 R2 64 bits e .NET 4.0

(16) comentários

Estamos prontos para oferecer a nova plataforma Windows 2008 R2 64 bits com .NET 4.0 para nossos clientes de Hospedagem de Sites.

Lançada pela Microsoft no final de 2009, a versão R2 64 bits é a evolução do Windows 2008 e se tornará a plataforma padrão para nossa Hospedagem de Sites em Windows. Este lançamento traz diversas melhorias, que o tornam o melhor ambiente para aplicações do Windows Server até o momento:

  • - Funcionalidades que permitem que as aplicações sejam executadas de forma mais rápida e usando menos recursos de hardware
  • - Mais disponibilidade, confiabilidade e escalabilidade para qualquer aplicação hospedada nesta plataforma, inclusive comparada a ofertas Windows 2008 convencionais, em 32 bits.
  • - Isolamento de aplicações em um pool próprio, independente de outros sites do servidor. Além disso, você poderá reiniciá-lo pelo Painel de Controle.
  • - IIS Remote Administration, que permite configurar suas aplicações .NET através da interface de gerenciamento do IIS
  • - A versão mais poderosa do .NET Framework 4.0 (executando em medium trust)
  • - PHP 5.3.3 – A versão mais nova do PHP e com ganhos de performance significativos sobre plataformas Windows. Para aqueles que ainda não estiverem prontos para usar a versão 5.3.3, será possível utilizar a versão 5.2.14, alterando essa configuração de forma fácil e rápida pelo IIS Remote Administration.

A Locaweb, um dos maiores clientes de soluções de hosting da Microsoft no Brasil, sempre esteve comprometida com a estabilidade e a segurança de seu ambiente. Para isso, fizemos um extenso trabalho de pesquisa, testes e otimização. Criamos o ambiente ideal, que combina a melhor relação entre recursos, segurança e desempenho em Windows.

Por isso, se você é nosso cliente de Hospedagem Profissional I, II e III e possui sites em plataforma Windows 2003-32 bits e 2008-32 bits, está convidado a testar a nova plataforma! Ao participar do programa de testes, você contribuirá com o desenvolvimento dos recursos (componentes e configurações específicas) e nos ajudará a fazer os últimos ajustes para a plataforma entrar definitivamente no ar.

Clientes do plano Profissional Premium poderão participar do teste, porém alguns recursos (registro de .dll, por exemplo) não estão disponíveis neste momento. Clientes da Hospedagem Expressa, descontinuada pela Locaweb, devem solicitar pelo Painel de Controle o upgrade para qualquer plano Profissional.

Interessado? Então não perca tempo e seja um dos primeiros a experimentar esta novidade. Basta preencher o formulário abaixo e aguardar nosso contato.

Importante:
O programa de testes é exclusivo para clientes de Hospedagem Windows da Locaweb. O número de participantes é restrito e o preenchimento do formulário não garante sua participação no teste. Sua inscrição será avaliada por nossa equipe, que entrará em contato (via HelpDesk) para orientá-lo sobre o procedimento e migrar sua hospedagem para o novo ambiente. Neste contato, você conhecerá os canais de comunicação para trocar informações e dar seu feedback.

1jul2010

Locaweb patrocina o QCon, que chega ao Brasil

(0) comentários

A Locaweb é patrocinadora diamond do QCon, principal evento que reúne arquitetos e desenvolvedores e que agora chega a América Latina.

Serão 2 dias de palestras, que acontecerão na cidade de São Paulo, nos dias 11 e 12 de setembro. O QCon SP 2010 traz ícones nacionais e internacionais de diversas áreas, com apresentações de alto nível técnico.

Os participantes assistirão a mais de 40 palestras nos dois dias do evento, divididos em 6 tracks em diversos assuntos como Java, .Net, Agile, Ruby on Rails e Arquitetura da Informação. Nomes de destaque das comunidades nacional e internacional estarão presentes: Douglas Crockford, criador do JSON; Charles Nutter, criador o JRuby; Guilherme Silveira, criador do Restfulie; Fabio Kung, gerente de Cloud Computing da Locaweb, Nick Kallen, criador do banco de dados do Twitter; Randy Shoup, responsável pela infraestrutura do eBay; Fabio Akita, pioneiro de Rails no Brasil, entre muitos outros.

As inscrições são feitas pelo site oficial do evento, e os valores podem ser parcelados em até 10x. Alunos da Caelum, organizadora do QCon, têm 15% de desconto.

Para quem quer acompanhar as novidades pelo Twitter, a tag do evento é #qconsp.

qcon

28out2009

Serialização de objetos em JSON com .NET

(1) comentário

Serializar e deserializar instâncias de objetos no formato JSON com .NET acaba sendo muito simples.

Vamos utilizar como exemplo a classe abaixo:

public class SomeFakeClass
{
    public int ID { get; set; }
    public string Text { get; set; }
    public decimal Value { get; set; }
}

E criamos uma instância dela:

SomeFakeClass fake = new SomeFakeClass
{
    ID = 123,
    Text = "I am a sample text.",
    Value = 150.85M
};

Então queremos serializar a variável fake em JSON para obter o seguinte resultado:

{"ID":123,"Text":"I am a sample text.","Value":150.85}

A classe DataContractJsonSerializer torna essa tarefa muito fácil. Basta escrever os dados serializados para um Stream usando o método WriteObject e depois recuperar uma string a partir desse Stream:

MemoryStream stream = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(SomeFakeClass));
serializer.WriteObject(stream, obj);
string json = return Encoding.Default.GetString(stream.ToArray());

Para fazer o inverso, transformar dados em JSON para um objeto, usamos o método ReadObject passando o Stream que possui os dados serializados:

string json = "{"ID":123,"Text":"I am a sample text.","Value":150.85}";
MemoryStream stream = new MemoryStream(json);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(SomeFakeClass));
SomeFakeClass fake = (SomeFakeClass)serializer.ReadObject(stream);

Um detalhe importante é que a partir do .NET Framework 3.5 Service Pack 1 não há mais a necessidade de decorar a classe que queremos serializar com os atributos DataContract e DataMember, como o exemplo abaixo:

[DataContract]
public class SomeFakeClass
{
    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public string Text { get; set; }

    [DataMember]
    public decimal Value { get; set; }
}

Isso somente se faz necessário se quisermos ter uma controle maior do que será serializado, como por exemplo, serializar uma propriedade com um nome diferente. Na classe abaixo, a propriedade Text é configurada para ser serializada com o nome Message:

[DataContract]
public class SomeFakeClass
{
    [DataMember]
    public int ID { get; set; }

    [DataMember(Name = "Message")]
    public string Text { get; set; }

    [DataMember]
    public decimal Value { get; set; }
}

Os dados serializados em JSON podem ser algo como:

{"ID":123,"Message":"I am a sample text.","Value":150.85}

Para usar a classe DataContractJsonSerializer, que está no namespace System.Runtime.Serialization.Json, é necessário adicionar referências para dois assemblies no seu projeto:

  • System.Runtime.Serialization
  • System.ServiceModel.Web

Agora que já sabemos como serializar e deserializar em JSON, podemos criar um utilitário que faz esse trabalho. O exemplo abaixo utiliza Extensions Methods combinados com Generics:

public static class JsonSerializerExtensions
{
    public static string ToJson<T>(this T obj)
    {
        MemoryStream stream;

        using (stream = new MemoryStream())
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            serializer.WriteObject(stream, obj);
        }

        return Encoding.Default.GetString(stream.ToArray());
    }

    public static T FromJson<T>(this string json)
    {
        T obj;

        using (MemoryStream stream = new MemoryStream(Encoding.Default.GetBytes(json)))
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            obj = (T)serializer.ReadObject(stream);
        }

        return obj;
    }
}

E um exemplo de sua utilização:

SomeFakeClass fake = new SomeFakeClass
{
    ID = 123,
    Text = "I am a sample text.",
    Value = 150.85M
};

string fakeJson = fake.ToJson();
SomeFakeClass otherFake = fakeJson.FromJson<SomeFakeClass>();

.
O código-fonte desse utilitário você pode baixar aqui.


Post original:
http://prodis.pro.br/2009/10/23/serializacao-de-objetos-em-json-com-net

.

16out2009

Automatizando seus builds com NAnt

(0) comentários

NAnt (http://nant.sourceforge.net/) é uma ferramenta open source de criação de builds automáticos para .NET, modelado a partir do Ant do Java.

Com o NAnt você consegue compilar o seu projeto .NET, rodar testes unitários, configurar a aplicação de acordo com o ambiente (local, integração contínua, produção, etc), realizar o deploy, entre muitas outras coisas. Porém, para utilizar algumas dessas funcionalidades, é necessário um add-on, chamado NAntContrib (http://nantcontrib.sourceforge.net/), que adiciona várias outras funcionalidades ao NAnt, como por exemplo controle de serviços do Windows, de diretórios virtuais no IIS, utilização do MSBuild, entre outras coisas.

O NAnt funciona através de linha de comando, baseado num XML que você cria para seu build.

Como o foco deste post não é explicar como o NAnt funciona e sim mostrar alguns exemplos de como automatizar seus builds, vamos partir para a codificação.

Criando o primeiro build com o NAnt

Vamos começar com um exemplo simples. Iremos criar um build que apenas compila uma solution, em modo release, que contenha um projeto do tipo Class Library:

  1. Criar uma nova solution no Visual Studio com um projeto do tipo Class Library
  2. Criar um arquivo com o nome de “default.build”, no diretório raiz da solution criada (Ex.: C:\NomeDoProjeto\default.build), com o seguinte conteúdo:
    <?xml version="1.0"?>
    <project name="NAnt Example" default="build">
      <property name="nant.contrib.path" value="C:\Program Files (x86)\nant-contrib\bin" />
      <target name="build">
        <loadtasks assembly="${nant.contrib.path}\NAnt.Contrib.Tasks.dll" />
        <msbuild project="NAnt.sln" target="Rebuild">
          <arg line="/p:Configuration=Release" />
        </msbuild>
      </target>
    </project>
  3. Executar, através de linha de comando, dentro do diretório do projeto, o comando “nant” para realizar o build
    image01-nant-first-execution
  4. Caso tudo dê certo, no final da execução, aparecerá a mensagem “BUILD SUCCEEDED” para confirmar que tudo deu certo
    image02-nant-succeeded

O que fizemos?

  • Linha 2 – Criamos uma tag para o projeto (project), onde definimos o nome do projeto (atributo name) e o target padrão para execução (atributo default), para o NAnt. No nosso caso, definimos nosso target padrão como build, assim não há necessidade de executarmos esse projeto utilizando o comando “nant build“.
  • Linha 3 – Criamos a tag property para definir um valor para uma propriedade, para utilizarmos em outros pontos do nosso arquivo de build. Neste caso criamos uma propriedade com o nome de “nant.contrib.path” com o valor “C:\Program Files (x86)\nant-contrib\bin” para guardarmos o diretório onde foi instalado o NAnt-contrib (É aconselhável que todos os ambientes que você trabalhe esteja instalado no mesmo diretório).
  • Linha 4 – Criamos uma tag para realizar uma tarefa específica, no nosso caso, uma chamada de build (atributo name). Dentro desta tag iremos codificar o que queremos que esta tarefa faça.
  • Linha 5 – Dentro da tarefa build, criamos uma tag para carregar as funcionalidades do NAnt-contrib, lembrando que utilizamos a propriedade “nant.config.path“, que definimos na linha 3. Sempre que quisermos utilizar uma propriedade, devemos utilizar a sintaxe ${nomedapropriedade}.
  • Linha 6 – Agora, vamos utilizar uma task do NAnt-contrib (msbuild), que utilizará o MSBuild para compilar o projeto. Definimos o nome da solução/projeto a ser compilada (atributo project) e o tipo de compilação (atributo target).
  • Linha 7 – Definimos um parâmetro para ser executado no MSBuild (tag arg), no nosso caso, estamos dizendo ao MSBuild que utilize a configuração de Release do projeto (atributo line).

Acabamos de criar nosso primeiro build no NAnt que compila uma solution.

Adicionando execução de testes ao build

Agora, vamos criar um projeto de testes em nossa solution e vamos adicionar uma tarefa ao NAnt para rodar os testes:

  1. Adicionar um projeto de teste a solution
  2. Criar apenas um teste unitário em branco
  3. Modificar o arquivo default.build (Como o arquivo cresceu um pouco, coloquei umas quebras de linhas extras para melhor visualização do código)
    <?xml version="1.0"?>
    <project name="NAnt Example" default="build">
    
      <property name="program.files.path" value="${environment::get-variable('PROGRAMFILES')}" />
      <property name="nant.contrib.path" value="${program.files.path}\nant-contrib\bin" />
      <property name="mstest.path" value="${program.files.path}\Microsoft Visual Studio 9.0\Common7\IDE\MSTest.exe" />
      <property name="test.container.path" value="Tests\bin\Release\NAnt.Tests.dll" />
      <property name="test.results.path" value="TestResults" />
    
      <target name="clean">
        <delete dir="${test.results.path}" />
      </target>
    
      <target name="build" depends="clean">
        <loadtasks assembly="${nant.contrib.path}\NAnt.Contrib.Tasks.dll" />
        <msbuild project="NAnt.sln" target="Rebuild">
          <arg line="/p:Configuration=Release" />
        </msbuild>
    	<call target="test" />
      </target>
    
      <target name="test">
        <mkdir dir="${test.results.path}" failonerror="true" />
        <exec
          program="${mstest.path}"
          commandline=" /testcontainer:${test.container.path} /resultsfile:${test.results.path}\TestResults.trx  /detail:errormessage" />
      </target>
    
    </project>
  4. Executar novamente o comando “nant” para realizar o build
    image03-nant-with-tests

O que fizemos?

  • Linha 4 – Propriedade para pegar o valor da variável de ambiente PROGRAMFILES do Windows
  • Linha 5 – Utilizando a propriedade “program.files.path” para o diretório do NAntContrib
  • Linha 6 – Propriedade com o caminho físico do MSTest (caso use o NUnit, o NAntContrib tem uma tag específica para ele)
  • Linha 7 – Propriedade do caminho físico do arquivo de testes que será executado
  • Linha 8 – Propriedade do diretório onde serão salvos os resultados dos testes
  • Linha 10 – Tarefa para limpar o ambiente antes de começar o build
  • Linha 11 – Apagando o diretório dos resultados dos testes
  • Linha 14 – Adicionado a dependência à tarefa “clean” (atributo depends)
  • Linha 19 – Tag para chamar a tarefa de testes, após compilar o projeto
  • Linha 22 – Tarefa da execução dos testes
  • Linha 23 – Criando o diretório para o resultado dos testes. Caso não seja possível criar o diretório, o build para por aqui
  • Linha 24 a 26 – Chamando o MSTest para executar os testes e salvar os resultados

Agora nosso build só será compilado quando todos os testes da solution estiverem passando.

Limpando a sujeira

Com o crescimento e refatoração de nossos projetos, normalmente criamos arquivos novos e apagamos arquivos desnecessários, com isso, os diretórios onde são gerados os assemblies dos projetos, vão armazenando arquivos antigos, que provavelmente não serão mais necessários. Então vamos criar um build para apagarmos todos os arquivos das pastas obj e bin que o MSBuild cria ao compilar os projetos:

  1. Alterar o arquivo default.build
    <?xml version="1.0"?>
    <project name="NAnt Example" default="build">
    
      <property name="program.files.path" value="${environment::get-variable('PROGRAMFILES')}" />
      <property name="nant.contrib.path" value="${program.files.path}\nant-contrib\bin" />
      <property name="mstest.path" value="${program.files.path}\Microsoft Visual Studio 9.0\Common7\IDE\MSTest.exe" />
      <property name="test.container.path" value="Tests\bin\Release\NAnt.Tests.dll" />
      <property name="test.results.path" value="TestResults" />
    
      <target name="clean">
        <delete>
          <fileset>
            <include name="**\bin\**" />
    		<include name="**\obj\**" />
          </fileset>
        </delete>
        <delete dir="${test.results.path}" />
      </target>
    
      <target name="test">
        <mkdir dir="${test.results.path}" failonerror="true" />
        <exec
          program="${mstest.path}"
          commandline=" /testcontainer:${test.container.path} /resultsfile:${test.results.path}\TestResults.trx /detail:errormessage" />
      </target>
    
      <target name="build" depends="clean">
        <loadtasks assembly="${nant.contrib.path}\NAnt.Contrib.Tasks.dll" />
        <msbuild project="NAnt.sln" target="Rebuild">
          <arg line="/p:Configuration=Release" />
        </msbuild>
        <call target="test" />
      </target>
    
    </project>
  2. Executar o nant
    image04-nant-clean

O que fizemos?

  • Linha 11 a 16 – Adicionando a tag delete para apagar todos os arquivos, de todas as pastas bin e obj, existentes na solution

Agora não teremos mais problemas com arquivos antigos do projeto.

Diferentes arquivos de configuração para cada ambiente

Recapitulando, nós já conseguimos compilar um projeto, rodar os testes e limpar os diretórios das compilações, porém, se precisarmos utilizar arquivos de configuração para nossos projetos, com certeza teremos problemas com as diferentes configurações para cada ambiente (desenvolvimento, integração contínua, homologação, produção). Então, vamos ver um exemplo para utilar os arquivos de configurações corretos, de acordo com o ambiente escolhido para o build:

  1. Alterar o arquivo default.build
    <?xml version="1.0"?>
    <project name="NAnt Example" default="build">
    
      <property name="environment" value="local" />
      <property name="program.files.path" value="${environment::get-variable('PROGRAMFILES')}" />
      <property name="nant.contrib.path" value="${program.files.path}\nant-contrib\bin" />
      <property name="mstest.path" value="${program.files.path}\Microsoft Visual Studio 9.0\Common7\IDE\MSTest.exe" />
      <property name="test.project.path" value="Tests" />
      <property name="test.assembly" value="NAnt.Tests.dll" />
      <property name="test.container.path" value="${test.project.path}\bin\Release\${test.assembly}" />
      <property name="test.results.path" value="TestResults" />
    
      <target description="Configures" name="config">
        <property name="build.config.file" value="config.${environment}.xml" dynamic="true" />
        <if test="${file::exists(build.config.file)}">
          <echo message="Loading ${build.config.file}" />
          <include buildfile="${build.config.file}" verbose="true" />
        </if>
        <if test="${not file::exists(build.config.file) and environment != 'local'}">
          <fail message="Configuration file '${build.config.file}' could not be found." />
        </if>
      </target>
    
      <target name="clean">
        <delete>
          <fileset>
            <include name="**\bin\**" />
            <include name="**\obj\**" />
          </fileset>
        </delete>
        <delete dir="${test.results.path}" />
      </target>
    
      <target name="test">
        <mkdir dir="${test.results.path}" failonerror="true" />
        <copy file="${test.project.path}\${test.config.file}" tofile="${test.container.path}.config" failonerror="true" overwrite="true" />
        <exec
          program="${mstest.path}"
          commandline=" /testcontainer:${test.container.path} /resultsfile:${test.results.path}\TestResults.trx /detail:errormessage" />
      </target>
    
      <target name="build_ci">
        <property name="environment" value="ci" />
        <call target="build"/>
      </target>
    
      <target name="build" depends="config clean">
        <loadtasks assembly="${nant.contrib.path}\NAnt.Contrib.Tasks.dll" />
        <msbuild project="NAnt.sln" target="Rebuild">
          <arg line="/p:Configuration=Release" />
        </msbuild>
        <call target="test" />
      </target>
    
    </project>
  2. Criar dois arquivos de configuração para o projeto de teste, um chamado App.config e outro ci.App.Config (local e integração contínua), para simularmos os arquivos dos dois ambientes
  3. Criar um arquivo chamado config.local.xml no mesmo diretório do arquivo default.build (na raiz da solution)
    <project>
    	<property name="test.config.file" value="ci.App.config" />
    </project>
  4. Criar um arquivo chamado config.ci.xml no mesmo diretório do arquivo default.build (na raiz da solution)
    <project>
    	<property name="test.config.file" value="App.config" />
    </project>
  5. Executar o comando “nant build_ci

O que fizemos?

  • Linha 4 – Propriedade para definir o ambiente
  • Linha 8 – Propriedade com o diretório do projeto de testes
  • Linha 9 – Propriedade com o nome do arquivo do teste
  • Linha 14 a 22 – Tarefa para verificar se existe o arquivo de configuração a ser carregado, baseado na propriedade “environment
  • Linha 36 – Copiando o arquivo de configuração do teste, baseado na propriedade “test.config.file“, que é carregada no arquivo de configuração de cada ambiente (config.local.xml ou config.ci.xml). Caso seja um build local (nant build), irá sobrescrever o arquivo de configuração com o arquivo “App.config“, caso seja um build para integração contínua (nant build_ci), irá sobrescrever o arquivo “ci.App.config“.
  • Linha 42 a 45 – Tarefa para realizar o build para o ambiente de integração contínua.
  • Linha 47 – Adicionado a dependência da tarefa “config” antes de realizar o build

Agora nosso build pode ter arquivos de configurações diferentes para cada ambiente.

Criando um pacote para deploy

Normalmente, nós desenvolvedores, não temos acesso direto aos servidores de produção para realizar o deploy. Com isso, em alguns casos, temos que criar um pacote dos assemblies de uma aplicação e enviar para a equipe responsável que irá efetuar o deploy. Então, neste exemplo, vamos criar este pacote, com todos os arquivos necessários de configurações, específicos para o ambiente de produção:

  1. Alterar o arquivo default.build
    <?xml version="1.0"?>
    <project name="NAnt Example" default="build">
    
      <property name="environment" value="local" />
      <property name="program.files.path" value="${environment::get-variable('PROGRAMFILES')}" />
      <property name="nant.contrib.path" value="${program.files.path}\nant-contrib\bin" />
      <property name="mstest.path" value="${program.files.path}\Microsoft Visual Studio 9.0\Common7\IDE\MSTest.exe" />
      <property name="project.path" value="NAnt.Core" />
      <property name="project.binary.path" value="${project.path}\bin\Release" />
      <property name="project.assembly" value="NAnt.Core.dll" />
      <property name="test.project.path" value="Tests" />
      <property name="test.assembly" value="NAnt.Tests.dll" />
      <property name="test.container.path" value="${test.project.path}\bin\Release\${test.assembly}" />
      <property name="test.results.path" value="TestResults" />
      <property name="deploy.package.path" value="DeployPackage" />
      <property name="temp.path" value="Temp" />
    
      <target description="Configures" name="config">
        <property name="build.config.file" value="config.${environment}.xml" dynamic="true" />
        <if test="${file::exists(build.config.file)}">
          <echo message="Loading ${build.config.file}" />
          <include buildfile="${build.config.file}" verbose="true" />
        </if>
        <if test="${not file::exists(build.config.file) and environment != 'local'}">
          <fail message="Configuration file '${build.config.file}' could not be found." />
        </if>
      </target>
    
      <target name="clean">
        <delete>
          <fileset>
            <include name="**\bin\**" />
            <include name="**\obj\**" />
          </fileset>
        </delete>
        <delete dir="${test.results.path}" />
        <delete dir="${temp.path}" />
        <delete dir="${deploy.package.path}" />
      </target>
    
      <target name="test">
        <mkdir dir="${test.results.path}" failonerror="true" />
        <copy file="${test.project.path}\${test.config.file}" tofile="${test.container.path}.config" failonerror="true" overwrite="true" />
        <exec
          program="${mstest.path}"
          commandline=" /testcontainer:${test.container.path} /resultsfile:${test.results.path}\TestResults.trx /detail:errormessage" />
      </target>
    
      <target name="build_ci">
        <property name="environment" value="ci" />
        <call target="build"/>
      </target>
    
      <target name="build_production">
        <property name="environment" value="production" />
        <call target="package"/>
      </target>
    
      <target name="build" depends="config clean">
        <loadtasks assembly="${nant.contrib.path}\NAnt.Contrib.Tasks.dll" />
        <msbuild project="NAnt.sln" target="Rebuild">
          <arg line="/p:Configuration=Release" />
        </msbuild>
        <call target="test" />
      </target>
    
      <target name="package" depends="build">
        <mkdir dir="${deploy.package.path}" failonerror="true" />
        <mkdir dir="${temp.path}" failonerror="true" />
    
        <copy todir="${temp.path}" overwrite="true" verbose="true">
          <fileset basedir="${project.binary.path}">
            <include name="*/**" />
            <exclude name="*.pdb" />
            <exclude name="*.config" />
          </fileset>
        </copy>
    
        <copy file="${project.path}\${project.config.file}" tofile="${temp.path}\${project.assembly}.config" />
    
        <copy todir="${deploy.package.path}" overwrite="true">
          <fileset basedir="${temp.path}">
            <include name="*/**" />
          </fileset>
        </copy>
    
        <delete dir="${temp.path}" />
      </target>
    
    </project>
  2. Criar dois arquivos de configuração para o projeto principal, um chamado App.config e outro production.App.Config (local e produção)
  3. Criar um arquivo chamado config.production.xml no mesmo diretório do arquivo default.build (na raiz da solution)
    <project>
    	<property name="test.config.file" value="ci.App.config" />
    	<property name="project.config.file" value="production.App.config" />
    </project>
  4. Executar o comando nant “build_production

O que fizemos?

  • Linha 8 a 10 – Propriedades com os dados do diretório e assembly do projeto principal, que será feito o deploy
  • Linha 15 – Propriedade com o diretório que será criado, onde ficará o pacote para deploy
  • Linha 16 – Propriedade com o diretório temporário, onde organizaremos os arquivos gerados da compilação, antes de enviar para o diretório do pacote
  • Linha 37 e 38 – Apagando os diretórios gerados (deploy e temporário)
  • Linha 54 a 57 – Tarefa para gerar o pacote para deploy
  • Linha 68 – Criando o diretório do pacote para deploy
  • Linha 69 – Criando o diretório temporário
  • Linha 71 a 77 – Copiando todos os arquivos do diretório onde foi compilado o projeto, para o diretório temporário, exceto os arquivos com extensão “pdb” e “config
  • Linha 79 – Copiando o arquivo de configuração para o ambiente de produção (carregado da propriedade “project.config.file” do arquivo “config.production.xml“)
  • Linha 81 a 88 – Copiando os arquivos do diretório temporário para o diretório onde ficará o pacote para deploy
  • Linha 87 – Apagando o diretório temporário

Pronto, agora você terá um diretório chamado “DeployPackage” com seu pacote prontinho para ser enviado para a equipe responsável no deploy da aplicação.

Existem inúmeras outras funcionalidades para automatização de seus builds com o NAnt e o NAntContrib, porém, creio que com esses exemplos, voces consigam ter uma idéia de como criar builds automatizados para seus projetos .NET, mas é claro que, para cada projeto, você vai precisar ajeitar seu build conforme a necessidade.

Referências

Leitura recomendada

5set2009

Utilizando aspectos em C# com PostSharp

(1) comentário

Um pouco sobre programação orientada a aspectos (AOP)

Ao longo dos últimos anos, a medida em que o hardware foi se tornando um recurso cada vez mais barato e poderoso, o foco das linguagens de programação tem se voltado para clareza ao invés de performance.

Onde antes era necessário, por exemplo, ponderar com muito cuidado qual o melhor algoritmo para iterar uma lista, hoje esta é uma questão secundária. O que isto propicia a nós, desenvolvedores, é a possibilidade de focar nosso tempo e esforço em questões de clareza e manutenibilidade em nossos códigos.

Com estas novas preocupações em mente, surgiu o paradigma orientado a objetos, que visa traduzir da forma mais natural possível as entidades reais de nosso negócio, para classes e instâncias em nosso código.

Porém, um grande problema que aparece quando se desenvolve um software orientado a objetos é o espalhamento de responsabilidades em comum ao longo de várias classes. Alguns exemplos clássicos deste problema são:

  • Log;
  • Autenticação;
  • Abrir/Fechar conexões;
  • Tratamentos de exceção;

Estas preocupações normalmente encontram-se espalhadas ao longo do código de diversas classes, mesmo que o comportamento desejado seja exatamente igual em todas elas.

Para abordar este problema de espalhamento, surgiu o paradigma de programação orientada a aspectos. Este visa complementar a programação orientada a objetos, eliminando em grande parte o espalhamento de código para preocupações comuns em diversos pontos do código. A seguir, tentarei mostrar, utilizando um exemplo prático, as vantagens que o uso de aspectos podem trazer para a clareza do código desenvolvido.

Aprendendo por meio de exemplos

O problema imediato que o espalhamento de código similar gera é: para cada modificação que seja necessária neste código espalhado, será necessário modificá-lo manualmente em cada ponto – como nós, desenvolvedores, sabemos, tarefas manuais como esta estão fadadas a gerar erros ou inconsistências.

A seguir, implementaremos uma classe de conta corrente, que possibilitará depósitos e saques, começando, naturalmente, pelos testes:

[TestMethod]
public void WithdrawShouldDecreaseBalance()
{
    Account account1 = AccountBuilder.New
        .BelongingTo("MACSkeptic")
        .WithInitialBalance(200.95M)
        .IdentifiedBy(773)
        .Instance;

    Assert.AreEqual(100.95M, account1.Withdraw(100).CurrentBalance);
    Assert.AreEqual(0.0M, account1.Withdraw(100.95M).CurrentBalance);
    Assert.AreEqual(-10.0M, account1.Withdraw(10.00M).CurrentBalance);
}

[TestMethod]
public void DepositShouldIncreaseBalance()
{
    Account account1 = AccountBuilder.New
        .BelongingTo("MACSkeptic")
        .WithInitialBalance(-50.42M)
        .IdentifiedBy(666)
        .Instance;

    Assert.AreEqual(-0.42M, account1.Deposit(50).CurrentBalance);
    Assert.AreEqual(79.58M, account1.Deposit(80).CurrentBalance);
}

Para atender ao comportamento descrito acima, implementaremos a classe “Account”, denotada a seguir:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
    public class Account
    {
        public long Number { get; set; }
        public string Owner { get; set; }
        public decimal CurrentBalance { get { return this.currentBalance; } }

        private decimal currentBalance;

        public Account(long number, string owner)
            : this(number, owner, 0)
        {
        }

        public Account(long number, string owner, decimal initialBalance)
        {
            this.Number = number;
            this.Owner = owner;
            this.currentBalance = initialBalance;
        }

        public Account Deposit(decimal howMuch)
        {
            Console.WriteLine(string.Format(
                "New deposit transaction on account {0}.
                                       Balance before operation is ${1}.",
                this.Number,
                this.CurrentBalance));

            Console.WriteLine(string.Format(
                "Depositing ${0} on account {1}.", howMuch, this.Number));

            this.currentBalance += howMuch;

            Console.WriteLine(string.Format(
                "Finished deposit transaction on account {0}.
                                       Balance after the operation is ${1}.",
                this.Number,
                this.CurrentBalance));

            return this;
        }

        public Account Withdraw(decimal howMuch)
        {
            Console.WriteLine(string.Format(
                "New withdraw transaction on account {0}.
                                      Balance before operation is ${1}.",
                this.Number,
                this.CurrentBalance));

            Console.WriteLine(string.Format(
                "Withdrawing ${0} on account {1}.", howMuch, this.Number));

            this.currentBalance -= howMuch;

            Console.WriteLine(string.Format(
                "Finished withdraw transaction on account {0}.
                                   Balance after the operation is ${1}.",
                this.Number,
                this.CurrentBalance));

            return this;
        }
    }
}

Como pode ser visto, nossa conta bancária tem basicamente dois métodos, responsáveis por depositar (Deposit) e sacar (Withdraw) dinheiro. Por enquanto ainda não há tratamento de saldo nas saques, caso se tire mais dinheiro do que o disponível, o resultado será uma conta com saldo negativo.

Logo já conseguimos notar que o log (sendo realizado no próprio console, para manter o foco no que realmente interessa neste artigo) de operaçòes de depósito e saque é bem parecido, gerando um código claramente repetitivo.

Uma possível refatoração nos possibilita eliminar um pouco de repetição, fazendo uso da refatoração “extrair método”, temos:

public Account Deposit(decimal howMuch)
{
    this.LogTransactionStarting("deposit");
    this.LogTransactionDetails("Depositing", howMuch);
    this.currentBalance += howMuch;
    this.LogTransactionFinished("deposit");
    return this;
}
public Account Withdraw(decimal howMuch)
{
    this.LogTransactionStarting("withdraw");
    this.LogTransactionDetails("Withdrawing", howMuch);
    this.currentBalance -= howMuch;
    this.LogTransactionFinished("withdraw");
    return this;
}
private void LogTransactionStarting(string action)
{
    Console.WriteLine(string.Format(
        "New {2} transaction on account {0}.
                                 Balance before operation is ${1}.",
        this.Number,
        this.CurrentBalance,
        action));
}
private void LogTransactionDetails(string action, decimal howMuch)
{
    Console.WriteLine(string.Format(
        "{2} ${0} on account {1}.", howMuch, this.Number, action));
}
private void LogTransactionFinished(string action)
{
    Console.WriteLine(string.Format(
        "Finished {2} transaction on account {0}.
                                 Balance after the operation is ${1}.",
        this.Number,
        this.CurrentBalance,
        action));
}

O código já ficou mais limpo e com menos repetições. Porém, os métodos de “Deposit” e “Withdraw”, e mais especificamente a classe “Account” não deveríam estar preocupados em logar as ações. Usando apenas conceitos de orientação a objetos, o máximo que conseguiríamos fazer para melhorar o código acima seria mover os métodos de log para outra classe. Esta ação melhoraria nosso código, mas ainda assim teríamos as chamadas aos métodos de log na classe “Account”, tirando o foco no negócio, tanto durante a codificação, quanto quando se venha a ler o código no futuro.

Há uma conhecida frase sobre classes em orientação a objetos que diz que se você precisa utilizar “e” ao definir o que sua classe faz, provavelmente ela está aglomerando muitas responsabilidades. No caso, nossa classe de conta faz transações e gera logs de transações.

Neste ponto, podemos notar a natureza do log que precisamos. Temos:

  • Um evento de log no início dos métodos de depósito e saque;
  • Um evento de log com detalhes específicos sobre o depósito e saque sendo realizado no momento;
  • Um evento de log ao térmido dos métodos de depósito e saque.

Uma ferramenta bastante competente em auxiliar no uso de aspectos em .NET é o PostSharp, cujo slogan, “make sense, not code” (faça sentido, não faça código), diz bastante sobre a idéia central  sobre a qual se sedimenta o paradigma de aspectos. Para a sequência deste artigo será necessário o download do PostSharp (utilizaremos a versão 1.5), que pode ser realizado clicando aqui (é necessário registrar-se no site da ferramenta. O registro é gratuito e pode ser feito aqui).

Após fazer a instalação do PostSharp, devemos adicionar as referências às bibliotecas PostSharp.Laos e PostSharp.Public em nosso projeto, conforme imagem a seguir:

image

A documentação detalhada do PostSharp (em inglês) pode ser conferida clicando-se aqui.

Para nossa classe “Account” iremos utilizar dois aspectos:

  • OnMethodBoundaryAspect (ao redor do método): define pontos de entrada ao redor das chamadas de um método. Isto é, imediatamente antes e imediatamente depois de sua chamada. Este aspecto é ideal para o log antes e depois de uma transação em nossa classe “Account”;
  • OnMethodInvocationAspect (na chamada do método): define um ponto de entrada assim que um método é chamado, incluindo detalhes como os parâmetros informados nessa chamada específica. Este aspecto é ideal para o log de detalhes de transação em nossa classe “Account”.

Primeiramente, vamos ver como fica a implementação do aspecto “ao redor” na prática:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using Codevil.MACSkeptic.PostSharpExample.Entities;

namespace Codevil.MACSkeptic.PostSharpExample.Aspects
{
    [Serializable]
    public class LogAccountStatusBeforeAndAfterTransaction :
                                            OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            Account account = (Account)eventArgs.Instance;
            Console.WriteLine(string.Format(
                "New {2} transaction on account {0}.
                                    Balance before operation is ${1}.",
                account.Number,
                account.CurrentBalance,
                eventArgs.Method.Name));
        }
        public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
            Account account = (Account)eventArgs.Instance;
            Console.WriteLine(string.Format(
               "Finished {2} transaction on account {0}.
                                   Balance after the operation is ${1}.",
               account.Number,
               account.CurrentBalance,
               eventArgs.Method.Name));
        }
    }
}

O aspecto “OnMethodBoundary” possibilita a implementação dos métodos “OnEntry” (antes do método) e “OnExit” (após o método). No código acima pode-se reparar que nosso aspecto precisa de um parâmetro especificando qual a ação está sendo tomada e o código para log foi copiado diretamente do que tínhamos antes na classe “Account”. Um detalhe importante é que todas as classes que implementam algum aspecto do PostSharp devem ser serializáveis.

Vamos ver agora como fica o código de nossa classes “Account” fazendo uso deste aspecto:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;

namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
    public class Account
    {
        public long Number { get; set; }
        public string Owner { get; set; }
        public decimal CurrentBalance { get { return this.currentBalance; } }
        private decimal currentBalance;

        public Account(long number, string owner)
            : this(number, owner, 0)
        {
        }

        public Account(long number, string owner, decimal initialBalance)
        {
            this.Number = number;
            this.Owner = owner;
            this.currentBalance = initialBalance;
        }
        [LogAccountStatusBeforeAndAfterTransaction]
        public Account Deposit(decimal howMuch)
        {
            this.LogTransactionDetails("Depositing", howMuch);
            this.currentBalance += howMuch;
            return this;
        }
        [LogAccountStatusBeforeAndAfterTransaction]
        public Account Withdraw(decimal howMuch)
        {
            this.LogTransactionDetails("Withdrawing", howMuch);
            this.currentBalance -= howMuch;
            return this;
        }
        private void LogTransactionDetails(string action, decimal howMuch)
        {
            Console.WriteLine(string.Format(
                "{2} ${0} on account {1}.", howMuch, this.Number, action));
        }
    }
}

Podemos perceber os atributos adicionados nas linhas 27 e 34, que apenas informam um aspecto sobre o comportamento deste método, cuja implementação não interessa para a classe “Account”.

Agora, vamos criar um aspecto “OnMethodInvocation”, para o log de detalhes de transação:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using Codevil.MACSkeptic.PostSharpExample.Entities;

namespace Codevil.MACSkeptic.PostSharpExample.Aspects
{
    [Serializable]
    public class LogAccountTransactionDetails : OnMethodInvocationAspect
    {
        public override void OnInvocation(MethodInvocationEventArgs eventArgs)
        {
            Account account = (Account)eventArgs.Instance;
            Console.WriteLine(string.Format(
                "{2} ${0} on account {1}.",
                eventArgs.GetArgumentArray().First(),
                account.Number,
                eventArgs.Method.Name));
            eventArgs.Proceed();
        }
    }
}

Novamente, apenas movemos a implementação do método original de log que estava em nossa classe “Account” para dentro de nosso aspecto. O parâmetro “eventArgs” provê acesso, entre outras coisas:

  • À instância corrente da classe na qual o aspecto está sendo aplicado;
  • Ao método sendo chamado;
  • Aos parâmetros passados na chamada do método.

Um ponto importante a se notar no método acima é a chamada “eventArgs.Proceed” (proceder). Este comando diz ao weaver que o método original deve ser executado neste momento, ou seja, o fluxo original do método deve proceder sem alterações. Caso necessário, poderíamos, por exemplo: :

  • Modificar os parâmetros que estão sendo passados para o método original (basta passar o novo parâmetro para o método “proceed”);
  • Sequer chamar o método original neste ponto (basta omitir a chamada ao método “proceed”).

Com isso, conseguimos mover toda nossa preocupação com o log para os aspectos. Nossa classe “Account” atualizada fica assim:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;

namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
    public class Account
    {
        public long Number { get; set; }
        public string Owner { get; set; }
        public decimal CurrentBalance { get { return this.currentBalance; } }
        private decimal currentBalance;

        public Account(long number, string owner)
            : this(number, owner, 0)
        {
        }

        public Account(long number, string owner, decimal initialBalance)
        {
            this.Number = number;
            this.Owner = owner;
            this.currentBalance = initialBalance;
        }
        [LogAccountStatusBeforeAndAfterTransaction]
        [LogAccountTransactionDetails]
        public Account Deposit(decimal howMuch)
        {
            this.currentBalance += howMuch;
            return this;
        }
        [LogAccountStatusBeforeAndAfterTransaction]
        [LogAccountTransactionDetails]
        public Account Withdraw(decimal howMuch)
        {
            this.currentBalance -= howMuch;
            return this;
        }
    }
}

Antes de mais nada, utilizando uma expressão regular, podemos deixar nosso código ainda mais limpo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;

namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
    [LogAccountStatusBeforeAndAfterTransaction(
		AttributeTargetMembers = "regex:(Deposit)|(Withdraw)")]
    [LogAccountTransactionDetails(
		AttributeTargetMembers = "regex:(Deposit)|(Withdraw)")]
    public class Account
    {
        public long Number { get; set; }
        public string Owner { get; set; }
        public decimal CurrentBalance { get { return this.currentBalance; } }
        private decimal currentBalance;

        public Account(long number, string owner)
            : this(number, owner, 0)
        {
        }

        public Account(long number, string owner, decimal initialBalance)
        {
            this.Number = number;
            this.Owner = owner;
            this.currentBalance = initialBalance;
        }
        public Account Deposit(decimal howMuch)
        {
            this.currentBalance += howMuch;
            return this;
        }
        public Account Withdraw(decimal howMuch)
        {
            this.currentBalance -= howMuch;
            return this;
        }
    }
}

Repare que agora o código fonte de nossa classe “Account” não está mais poluído com a preocupação de manter um log das transações realizadas.

Quer mais? Ainda podemos fazer uma melhoria final, agora que temos um pouco mais de domínio sobre o funcionamento do PostSharp, podemos eliminar a utilização do aspecto “OnMethodBoundary” utilizado para os logs de início e término da transação. Para isso, basta realizarmos algumas pequenas modificações em nosso aspecto “OnMethodInvocation”, conforme denotado a seguir:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PostSharp.Laos;
using Codevil.MACSkeptic.PostSharpExample.Entities;

namespace Codevil.MACSkeptic.PostSharpExample.Aspects
{
    [Serializable]
    public class LogAccountTransactionDetails : OnMethodInvocationAspect
    {
        public override void OnInvocation(MethodInvocationEventArgs eventArgs)
        {
            Account account = (Account)eventArgs.Instance;

            LogTransactionStarting(eventArgs, account);
            LogTransactionDetails(eventArgs, account);
            eventArgs.Proceed();
            LogTransactionFinished(eventArgs, account);
        }
        private static void LogTransactionDetails(
			MethodInvocationEventArgs eventArgs, Account account)
        {
            Console.WriteLine(string.Format(
                "{2} ${0} on account {1}.",
                eventArgs.GetArgumentArray().First(),
                account.Number,
                eventArgs.Method.Name));
        }
        private static void LogTransactionStarting(
			MethodInvocationEventArgs eventArgs, Account account)
        {
            Console.WriteLine(string.Format(
                "New {2} transaction on account {0}.
                               Balance before operation is ${1}.",
                account.Number,
                account.CurrentBalance,
                eventArgs.Method.Name));
        }
        private static void LogTransactionFinished(
			MethodInvocationEventArgs eventArgs, Account account)
        {
            Console.WriteLine(string.Format(
               "Finished {2} transaction on account {0}.
                                Balance after the operation is ${1}.",
               account.Number,
               account.CurrentBalance,
               eventArgs.Method.Name));
        }
    }
}

Repare que primeiro logamos o início da transação (linha 17), depois os detalhes sobre parâmetros envolvidos na transação (linha 18), só então prosseguimos com a chamada do método original (linha 19), e por último logamos o estado final de nossa conta (linha 20). Com isso obtivemos um comportamento idêntico ao que tínhamos anteriormente com dois aspectos, porém utilizando apenas um. Por fim, basta ajustarmos a classe “Account” para não mais utilizar o aspecto “ao redor”:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Codevil.MACSkeptic.PostSharpExample.Aspects;

namespace Codevil.MACSkeptic.PostSharpExample.Entities
{
    [LogAccountTransactionDetails(
		AttributeTargetMembers = "regex:(Deposit)|(Withdraw)")]
    public class Account
    {
        public long Number { get; set; }
        public string Owner { get; set; }
        public decimal CurrentBalance { get { return this.currentBalance; } }
        private decimal currentBalance;
        private const decimal DefaultInitialBalance = 0;

        public Account(long number, string owner)
            : this(number, owner, DefaultInitialBalance)
        {
        }
        public Account(long number, string owner, decimal initialBalance)
        {
            this.Number = number;
            this.Owner = owner;
            this.currentBalance = initialBalance;
        }

        public Account Deposit(decimal howMuch)
        {
            this.currentBalance += howMuch;
            return this;
        }
        public Account Withdraw(decimal howMuch)
        {
            this.currentBalance -= howMuch;
            return this;
        }
    }
}

Ou seja, mesmo em um pequeno exemplo como este, substituímos pelo menos 6 linhas de código “alien” da classe “Account” original (considerando a melhor implementação possível sem aspectos, onde os métodos de log tivessem sido movidos para outra classe) que estavam relacionadas com a preocupação de manter um log de transações. No lugar delas, adicionamos apenas um atributo declarando um aspecto de comportamento esperado quanto às transações. Ressaltando, para evitar mal entendidos: a vantagem não é necessariamente escrever menos código, mas sim separar claramente as responsabilidades e escrever o código onde ele deve estar.

Fico por aqui, espero que tenham gostado e percebido o quanto aspectos podem facilitar o desenvolvimento e melhorar a manutenibilidade de código. Deixo uma recomendação final para que os interessados em usar aspectos em seus projetos leiam com atenção a documentação do PostSharp (ou de qualquer outra ferramenta que venham a usar) para conferir o que pode ser feito em termos de otimização e detalhes sobre performance (o PostSharp oferece vários recursos de inicialização de aspectos em tempo de compilação, diminuindo o overhead em tempo de execução).

Como sempre, o código base deste artigo está disponível no meu github, e pode ser baixado aqui. Sintam-se à vontade para deixar suas dúvidas, críticas e sugestões nos comentários e até a próxima ;).

Referências:

Leituras recomendadas:

31jul2009

Cheeseburgers, Decorators e Mocks

(3) comentários

Em São Paulo, eu sempre comi cheeseburgers feitos com pão, hamburguer e queijo. Mas quando eu fui para Itararé, cidade do interior do estado de São Paulo, descobri que eles também colocavam milho no sanduíche.

Para exemplificar, vamos imaginar que o cheeseburger de Ilhéus-BA, venha com molho de pimenta. Só para constar, eu nunca fui para Ilhéus, apesar de ser a cidade natal de meu pai. Então na verdade não tenho a mínima idéia de como seja o cheeseburger de lá.

teste

Vamos transportar esses três tipos de cheeseburgers para objetos e fazer alguns testes com eles. Usarei como plataforma .NET, linguagem C#, a ferramenta de testes unitários que vem com o Visual Studio 2008 e o Rhino Mocks como framework de criação de mocks.

.
Cheeseburgers com herança
Primeiramente criamos a classe Cheeseburger, que representa o cheeseburger comum de São Paulo. Nessa classe teremos uma propriedade para descrição e um método que retorna a quantidade de calorias do sanduíche.

public class Cheeseburger
{
    public Cheeseburger()
    {
        this.Description = "Bread, Hamburger, Cheese";
    }

    public string Description { get; protected set; }

    public virtual int Calories()
    {
        return 300;
    }
}

.
Os testes para a classe Cheeseburger são bem simples:

[TestClass]
public class CheeseburgerTest
{
    private Cheeseburger cheeseburger;

    [TestInitialize]
    public void Init()
    {
        this.cheeseburger = new Cheeseburger();
    }

    [TestMethod]
    public void Description_Of_Cheeseburger()
    {
        Assert.AreEqual("Bread, Hamburger, Cheese", this.cheeseburger.Description);
    }

    [TestMethod]
    public void Calories_Of_Cheeseburger()
    {
        Assert.AreEqual(300, this.cheeseburger.Calories());
    }
}

.
Agora vamos criar a classe CheeseburgerItarare que possuirá os mesmos membros de Cheeseburger. Como nós aprendemos na faculdade ou em algum curso que herança é um dos pilares da orientação a objetos e serve para reutilização de código, CheeseburgerItarare irá herdar de Cheeseburger.

public class CheeseburgerItarare : Cheeseburger
{
    public CheeseburgerItarare() : base()
    {
        this.Description += ", Corn";
    }

    public override int Calories()
    {
        return base.Calories() + 70;
    }
}

A diferença no cheeseburger de Itararé é o milho, então acrescentamos um texto para milho na descrição e sobrescrevemos o método Calories para somar 70 calorias ao valor que é retornada do método Calories da classe base Cheeseburger.

Fazemos o mesmo para o cheeseburger de Ilhéus: criamos uma classe CheeseburgerIlheus que herda de Cheeseburger e adicionamos a descrição e calorias equivalentes ao molho de pimenta. Veja o diagrama de classes:

Cheeseburgers com herança

Cheeseburgers com herança

.
Os testes para a classe CheeseburgerItarare ficam assim:

[TestClass]
public class CheeseburgerItarareTest
{
    private CheeseburgerItarare cheeseburgerItarare;

    [TestInitialize]
    public void Init()
    {
        this.cheeseburgerItarare = new CheeseburgerItarare();
    }

    [TestMethod]
    public void Description_Of_Cheeseburger_Itarare()
    {
        Assert.AreEqual("Bread, Hamburger, Cheese, Corn", this.cheeseburgerItarare.Description);
    }

    [TestMethod]
    public void Calories_Of_Cheeseburger_Itarare()
    {
        Assert.AreEqual(370, this.cheeseburgerItarare.Calories());
    }
}

Em princípio muito simples. Os testes unitários asseguram o comportamento de CheeseburgerItarare, ou seja, os valores de retorno da descrição e da quantidade de calorias, mas não testamos implementação. Em outras palavras, não conseguimos assegurar que quando o método Calories de CheeseburgerItarare é executado, uma chamada ao método Calories da classe base Cheeseburger é realizada.

Para testar essa chamada de método da classe base, precisamos usar mocks. Com isso, temos um problema de acoplamento, pois CheeseburgerItarare depende de Cheeseburger e com herança não conseguimos substituir a classe base por um mock. Podemos resolver isso usando composição ao invés de herança.

Antes de passarmos para composição, quero alertar que você precisa avaliar se testar chamadas de outras classes agregam valor aos testes que você está fazendo. Às vezes somente o comportamento da classe já é o suficiente para considerar uma classe testada. Você precisa ponderar o que é importante e necessário testar nos pedaços de código do seu sistema. Nesse pequeno exemplo em específico, estou considerando necessário testar as chamadas de método de Cheeseburger dentro das classes CheeseburgerItarare e CheeseburgerIlheus, por isso vamos modificar o design das mesmas.

.
Cheeseburgers com composição
Os princípios de orientação a objetos, entre outras coisas, nos dizem para:

  • Dar prioridade à composição em à relação à herança;
  • Programar para interfaces, não para para implementações;
  • Depender de abstrações, não de classes concretas.

Na solução de cheeseburgers com herança, violamos todos esses princípios. Então vamos consertar isso.

Primeiro, vamos extrair a interface da classe Cheeseburger:

public interface ICheeseburger
{
    string Description { get; }

    int Calories();
}

.
Nossa classe Cheeseburguer não muda em nada, só que agora implementa a interface ICheeseburguer:

public class Cheeseburger : ICheeseburger
{
    public Cheeseburger()
    {
        this.Description = "Bread, Hamburger, Cheese";
    }

    public string Description { get; protected set; }

    public virtual int Calories()
    {
        return 300;
    }
}

.
Teremos então uma mudança significativa nas classes CheeseburgerItarare e CheeseburgerIlheus, que não herdarão mais de Cheeseburger e implementarão a interface ICheeseburger. Veja o diagrama de classes como fica:

Cheeseburgers com composição

Cheeseburgers com composição

.
E a nova implementação da classe CheeseburgerItarare:

public class CheeseburgerItarare : ICheeseburger
{
    private ICheeseburger cheeseburger;

    public CheeseburgerItarare(ICheeseburger cheeseburger)
    {
        this.cheeseburger = cheeseburger;
        this.Description = this.cheeseburger.Description + ", Corn";
    }

    public string Description { get; protected set; }

    public int Calories()
    {
        return this.cheeseburger.Calories() + 70;
    }
}

A classe CheeseburgerItarare agora tem um campo privado do tipo ICheeseburger (linha 3), que será atribuído valor através de um parâmetro recebido no construtor (linha 7). Dessa forma, estamos compondo a classe Cheeseburger com alguma implementação de ICheeseburger, injetando essa dependência através do seu construtor. O método Calories delega sua chamada ao método Calories do objeto que foi injetado (linha 15) e soma as 70 calorias correspondentes ao milho.

Com esse design nós podemos criar um mock de ICheeseburger, passar esse mock para o construtor de CheeseburgerItarare e testar as chamadas de método e propriedade de ICheeseburger dentro da classe CheeseburgerItarare.

Vamos ver como ficam os testes agora:

[TestClass]
public class CheeseburgerItarareTest
{
    private MockRepository mocks;
    private ICheeseburger cheeseburgerMock;
    private CheeseburgerItarare cheeseburgerItarare;

    [TestInitialize]
    public void Init()
    {
        this.mocks = new MockRepository();
        this.cheeseburgerMock = this.mocks.DynamicMock();
    }

    [TestMethod]
    public void Description_Of_Cheeseburger_Itarare()
    {
        Expect.Call(this.cheeseburgerMock.Description).Return("Cheeseburger description");

        this.mocks.ReplayAll();
        this.cheeseburgerItarare = new CheeseburgerItarare(this.cheeseburgerMock);
        this.mocks.VerifyAll();

        Assert.AreEqual("Cheeseburger description, Corn", this.cheeseburgerItarare.Description);
    }

    [TestMethod]
    public void Calories_Of_Cheeseburger_Itarare()
    {
        Expect.Call(this.cheeseburgerMock.Calories()).Return(100);

        this.mocks.ReplayAll();

        this.cheeseburgerItarare = new CheeseburgerItarare(this.cheeseburgerMock);
        Assert.AreEqual(170, this.cheeseburgerItarare.Calories());

        this.mocks.VerifyAll();
    }
}

Para o teste da quantidade de calorias, na linha 30 gravamos uma chamada esperada do método Calories do mock de ICheeseburger definindo seu valor de retorno para 100. Instanciamos CheeseburgerItarare passando o mock como parâmetro na linha 34 e quando chamamos o método Calories na linha 35, esperamos o valor de 170 calorias: 100 do mock + 70 equivalente às calorias do milho. Finalmente, na linha 37 conseguimos assegurar que o método Calories do mock da interface ICheeseburger foi chamado na implementação do método Calories da classe CheeseburgerItarare.

A implementação e os testes da classe CheeseburgerIlheus seguem o mesmo esquema da classe CheeseburguerItarare.

Com isso, atingimos nosso objetivo de testar comportamento e implementação das classes de cheeseburgers.

Mas e se nós quisermos um novo tipo de cheeseburger, por exemplo, um que venha com cebola? Ah, é fácil, basta criar uma classe nova que implementa ICheeseburger com o mesmo esquema de injeção de dependência de uma implementação de ICheeseburger. Mas e se nós também quisermos criar outras combinações de ingredientes, como por exemplo:

  • Cheeseburger com milho e molho de pimenta;
  • Cheeseburger com milho e cebola;
  • Cheeseburger com milho, cebola e molho de pimenta.

Vamos ter que criar uma classe para cada combinação de ingredientes? E se ao invés de três ingredientes nós tivermos 15 ingredientes? Hum, bastante combinações, não? E se num futuro próximo aparecer novos ingredientes? Vamos ter que criar novas classes para todas as novas combinações de ingredientes?

.
Cheeseburgers com decoradores
Ao invés de criarmos inúmeras classes para inúmeras combinações de ingredientes de cheeseburgers, podemos modificar nosso design para criarmos ingredientes independentes e “decorar” os cheeseburgers com os ingredientes que quisermos e quando quisermos.

Isso segue um outro princípio de orientação a objetos: classes devem estar abertas para extensão, mas fechadas para modificação.

Usaremos um design pattern chamado Decorator, que é definido da seguinte forma:

Atribui responsabilidades adicionais a um objeto dinamicamente. Os Decorators fornecem uma alternativa flexível a subclasses para extensão de funcionalidades.

.
E aqui temos o diagrama de classe com a estrutura do padrão:

Decorator Design Pattern

Decorator Design Pattern

Onde:

  • Component define a interface para objetos que podem ter responsabilidades acrescentadas aos mesmos dinamicante;
  • ConcreteComponent define um objeto para o qual responsabilidades adicionais podem ser atribuídas;
  • Decorator mantém uma referência para um objeto Component e define uma interface que segue a interface de Component;
  • ConcreteDecorator acrescenta responsabilidades ao componente.

Chega de teoria e vamos para a prática nos nossos cheeseburgers.

A primeira coisa a fazer é criar a classe abstrata que servirá como Component. Vamos chamá-la de Sandwich.

public abstract class Sandwich
{
    public virtual string Description { get; protected set; }

    public abstract int Calories();
}

Note que marcamos a propriedade Description como virtual para suas subclasses poderem sobrescrevê-la.

Depois criamos outra classe abstrata que servirá como Decorator e a nomeamos como SandwichDecorator. Essa classe irá herdar de Sandwich.

public abstract class SandwichDecorator : Sandwich
{
    protected Sandwich sandwich;

    public SandwichDecorator(Sandwich sandwich)
    {
        this.sandwich = sandwich;
    }

    public override string Description
    {
        get
        {
            return this.sandwich.Description;
        }
    }

    public override int Calories()
    {
        return this.sandwich.Calories();
    }
}

Da mesma forma que fizemos no exemplo de cheeseburgers com composição, a injeção de dependência é feita pelo construtor (linhas 5 a 8), atribuindo o valor do parâmetro ao campo protegido do tipo Sandwich. A propriedade Description (linhas 10 a 16) é implementada delegando seu retorno para a propriedade Description da variável sandwich, ou seja, o objeto que foi injetado. O método Calories finalmente é implementado (linhas 18 a 21) e também delega sua chamada para a variável sandwich, mas chamando o método Calories da mesma.

Agora vamos modificar a classe Cheeseburger para herdar de Sandwich. Essa é a classe que servirá como ConcreteComponent.

public class Cheeseburger : Sandwich
{
    public Cheeseburger()
    {
        this.Description = "Bread, Hamburger, Cheese";
    }

    public override int Calories()
    {
        return 300;
    }
}

.
O último passo é criar as classes que servirão como ConcreteDecorator: Corn, OnionRings e PepperSauce. Segue o código da classe Corn:

public class Corn : SandwichDecorator
{
    public Corn(Sandwich sandwich) : base(sandwich)
    {

    }

    public override string Description
    {
        get
        {
            return base.Description + ", Corn";
        }
    }

    public override int Calories()
    {
        return base.Calories() + 70;
    }
}

O construtor da classe Corn chama o construtor da classe base SandwichDecorator passando o parâmetro do tipo Sandwich (linhas 3 a 6). A propriedade Description (linhas 8 a 14) é sobrescrita para retornar a descrição da classe base juntamente com a descrição de milho. O método Calories também é sobrescrito (linhas 16 a 19) e soma o retorno do método Calories da classe base com as calorias do milho. Em suma, toda a implementação é feita chamando os membros da classe base e adicionando comportando e/ou estado relacionado ao ingrediente milho quando necessário.

O mesmo esquema para as classes OnionRings e PepperSauce:

public class OnionRings : SandwichDecorator
{
    public OnionRings(Sandwich sandwich) : base(sandwich)
    {

    }

    public override string Description
    {
        get
        {
            return base.Description + ", Onion Rings";
        }
    }

    public override int Calories()
    {
        return base.Calories() + 140;
    }
}
public class PepperSauce : SandwichDecorator
{
    public PepperSauce(Sandwich sandwich) : base(sandwich)
    {

    }

    public override string Description
    {
        get
        {
            return base.Description + ", Pepper Sauce";
        }
    }

    public override int Calories()
    {
        return base.Calories() + 20;
    }
}

.
Aqui está o diagrama de classes dos cheeseburgers com decoradores:

Cheeseburgers com decoradores

Cheeseburgers com decoradores

.
Vamos ver como isso funciona através dos testes.

[TestClass]
public class CheeseburgerVariedTest
{
    private Sandwich cheeseburger;

    [TestInitialize]
    public void Init()
    {
        this.cheeseburger = new Cheeseburger();
    }

    [TestMethod]
    public void Itarare_Cheeseburger()
    {
        this.cheeseburger = new Corn(this.cheeseburger);

        Assert.AreEqual("Bread, Hamburger, Cheese, Corn", this.cheeseburger.Description);
        Assert.AreEqual(370, this.cheeseburger.Calories());
    }

    [TestMethod]
    public void Itarare_Cheeseburger_With_Onion_Rings()
    {
        this.cheeseburger = new Corn(this.cheeseburger);
        this.cheeseburger = new OnionRings(this.cheeseburger);

        Assert.AreEqual("Bread, Hamburger, Cheese, Corn, Onion Rings", this.cheeseburger.Description);
        Assert.AreEqual(510, this.cheeseburger.Calories());
    }

    //Outros testes
}

No primeiro teste decoramos a instância da classe Cheeseburger com milho (linha 15), embrulhando-a (wrapping) com a classe Corn. Já no teste de cheeseburger com milho e cebola, a variável cheeseburger é decorada duas vezes, uma vez com a classe Corn (linha 24) e outra com a classe OnionRings (linha 25). Cada vez que um cheeseburger é decorado, o decorador adiciona seu próprio comportamento antes e/ou depois de delegar para o objeto que ele decora.

Note que, como as classes Corn e OnionRings herdam de SandwichDecorator, que por sua vez herda de Sandwich, assim como a classe Cheeseburger, no final das contas estamos sempre lidando com a classe abstrata Sandwich. Por isso, através do polimorfismo, podemos atribuir uma instância da classe Corn e/ou OnionRings à uma variável do tipo Cheeseburger.

Quando o método Calories da variável cheeseburger é chamado na linha 28, estamos chamando o método Calories do último decorador, ou seja, da classe OnionRings. Depois contamos com a delegação para adicionar as calorias dos ingredientes. Veja o acontece:

  1. OnionRings chama o método Calories da classe base SandwichDecorator;
  2. SandwichDecorator delega sua chamada para o método Calories da classe Corn;
  3. Corn chama o método Calories da classe base SandwichDecorator;
  4. SandwichDecorator delega sua chamada para o método Calories da classe Cheeseburger;
  5. Cheeseburger retorna 300 calorias;
  6. SandwichDecorator retorna o valor das calorias que retornou da classe Cheeseburger;
  7. Corn adiciona suas 70 calorias ao retorno da classe base SandwichDecorator e retorna 370 calorias;
  8. SandwichDecorator retorna o valor das calorias que retornou da classe Corn;
  9. OnionRings adiciona suas 140 calorias ao retorno da classe base SandwichDecorator e retorna 510 calorias.

Complicado para entender? Dê uma olhada novamente no diagrama de classes “Cheesebugers com decoradores” e depois volte para o código. E se você debugar os testes, poderá ver passo-a-passo como as coisas acontecem. No final do post tem o link para você baixar o código completo.

.
Uma coisa interessante é que você pode testar isoladamente as classes ConcreteDecorator (Corn, PepperSauce, OnionRings ou qualquer outro decorador que você queira acrescentar). Por exemplo:

[TestClass]
public class CornTest
{
    private MockRepository mocks;
    private Sandwich sandwichMock;
    private Corn corn;

    [TestInitialize]
    public void Init()
    {
        this.mocks = new MockRepository();
        this.sandwichMock = this.mocks.DynamicMock();
    }

    [TestMethod]
    public void Description_Of_Cheeseburger_With_Corn()
    {
        Expect.Call(this.sandwichMock.Description).Return("Sandwich description");

        this.mocks.ReplayAll();

        this.corn = new Corn(this.sandwichMock);
        Assert.AreEqual("Sandwich description, Corn", this.corn.Description);

        this.mocks.VerifyAll();
    }

    [TestMethod]
    public void Calories_Of_Cheeseburger_With_Corn()
    {
        Expect.Call(this.sandwichMock.Calories()).Return(100);

        this.mocks.ReplayAll();

        this.corn = new Corn(this.sandwichMock);
        Assert.AreEqual(170, this.corn.Calories());

        this.mocks.VerifyAll();
    }
}

Aqui usamos mocks para testar as chamadas de métodos de Sandwich, assim como fizemos nos testes das classes CheeseburgerItarare e CheeseburgerIlheus no exemplo de cheeseburgers com composição.

Com esse design nós podemos ter novos ingredientes sem termos que nos preocupar com combinações, basta criar uma classe nova do ingrediente para decorar os sanduíches. Além disso, nós podemos criar também outros tipos de sanduíche. Por exemplo, podemos criar uma classe HotDog que herda da classe Sandwich. O ponto é: qualquer classe que herde da classe Sandwich pode ser decorada com os nossos ConcreteDecorators.

Design Patterns são soluções para problemas comuns e não necessariamente soluções prontas para o seu problema. Eles lhe ajudam a encontrar a sua solução para o seu problema específico. Fique à vontade para adaptá-los às suas necessidades.

Dúvidas, questionamentos, discórdias, sugestões? Deixe seu comentário.

O código completo com todas as classes e seus testes está disponível aqui.

.
Referências em inglês:

Referências em português:

.
Atualização em 14/09/2009: Fiz um post com a implementação desse exemplo de Decorator Pattern em Ruby. Você pode vê-lo aqui.

Post original:
http://prodis.pro.br/2009/07/29/cheeseburgers-decorators-e-mocks

.

1jul2009

.Net Architets Day – Boas práticas de arquitetura e engenharia de software

(1) comentário

dotnetarchitets No último sábado (27/06) rolou o .Net Architets Day 2009 voltado para arquitetura de software (com foco em .Net).

Pouco ou quase nada se falou sobre uso de alguma ferramenta específicas Microsoft, até porque o foco do grupo que já existe há algum tempo é exatamente a utilização do .Net com práticas de engenharia de software e arquitertura.

O evento, segundo a organização, não teve fins lucrativos e o valor foi revertido em brindes e coffe break além de algumas outras despezas (achei muito bacana a prestação de contas apresentada pelo Giovanni, idealizador do grupo).

Estes foram os temas apresentados, com um pequeno resumo de cada palestra. De acordo com a organização, as apresentações e as filmagens estarão disponíveis no site do grupo em breve.

Programando com prazer com Domain Driven Design (DDD)Giovanni Basi

A principal preocupação do arquiteto ou desenvolvedor de software deve ser com o futuro, ou seja, a “manutenibilidade” do sistema. Com o design baseado na lógica do domínio do cliente (DDD), tudo fica mais fácil, desde a forma de se comunicar (linguagem ubíqua) até a modelagem, desenvolvimento e manutenção do software.

Utilizando Injeção de dependência com Unity (Enterprise Library)Leandro Daniel

O acoplamento é um problema enorme em POO. Fazer testes em uma classe de negócio que dependa de uma outra que envia e-mail, por exemplo, ou adicionar uma nova funcionalidade a um pedaço que já esteja amarrado a uma implementação é triste demais. O Unity, um dos blocos da Enterprise Library (incorporado a partir da versão 4.0), foi criado para ajudar nessa tarefa injetando a dependência quando necessário. No entanto, é preciso avaliar o uso para não tornar seu software ainda mais acoplado :)

ASP.Net MVC: tome seu HTML de voltaVictor Cavalcante

Nesta palestra foi feita uma comparação entre o ASP.NET MVC e Web Forms, mostrando como ficamos como, com este último, muito presos à interface e fica difícil fazer testes enquanto que, com MVC separa a lógica da apresentação ficando mais flexível e testável. Por outro lado, MVC tem um preço, é necessário colocar a mão na massa, controlar o HTML, é necessário saber programar para web.

ORM – Sendo preguiçoso com NHibernateJuliano Oliveira

No início da palestra o Juliano trocou o título para “Sendo produtivo com NHibernate”. Este que é um dos frameworks mais conhecidos para mapeamento de objeto relacional é de grande valor na hora de desenvolver seu sistema e diminui bastante a dor de cabeça com criação de tabelas, colunas, constraints, etc. na base de dados. Foi mostrado também o NHProf uma ferramenta comercial para que funciona como o Profiler do SQL Server.

Testes: garantindo que seu código faz o que você querMauricio Aniche

Uma frase que eu tenho ouvido muito ultimamente, mas, graças a Deus, a grande maioria das vezes, só como ilustração “Tá pronto, só falta testar”, foi mencionada mais uma vez. E não é difícil ouvir isso nas empresas que ainda não adotam boas práticas de desenvolvimento. Foi feita uma bela apresentação de como os testes podem ajudar tanto a evitar bugs como na qualidade e na própria codificação, pois fica fácil entender a lógica e as regras de negócio fazendo testes. O uso de ferramentas de automatização de testes como o NUnit ou o Próprio MSTest do Visual Studio, o uso de frameworks de mock potencializam a produtividade.

Valeu muito a pena participar e já estou esperando o próximo.

4mai2009

Como medir o uso da CPU utilizando Performance Counters

(0) comentários

Na última semana no trabalho, dois desenvolvedores estavam procurando uma forma de recuperar a porcentagem de uso da CPU utilizando .NET. Eles encontraram uma solução utilizando Win32_Processor de WMI e classes do namesapce System.Management, escrevendo algo mais ou menos assim:

ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Processor");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

int load = 0;
int cpus = 0;

foreach (ManagementObject mo in searcher.Get())
{
    load += Convert.ToInt32(mo["LoadPercentage"]);
    numCpus++;
}

decimal cpuUsage = load / cpus;

Uma maneira mais fácil de fazer isso, e que atende ao propósito de medir recursos utilizados da máquina, é utilizar Performance Counters. Classes como PerformanceCounter e PerformanceCounterCategory, que são encontradas no namespace System.Diagnostics, lhe dão acesso não somente a informações do uso do processador, mas também informações sobre cachê de sistema, disco físico, memória, threads, entre outros.

O exemplo abaixo mostra como criar uma instância da classe PerformanceCounter para recuperar a porcentagem do uso da CPU naquele instante:

PerformanceCounter cpuCounter = new PerformanceCounter
{
    CategoryName = "Processor",
    CounterName = "% Processor Time",
    InstanceName = "_Total"
};

float cpuUsage = cpuCounter.NextValue();

Note que na propriedade CategoryName é informado “Processor”, a categoria referente ao processador da máquina, e na propriedade CounterName, o contador que se refere à porcentagem do tempo de processamento. Em algumas categorias de Performance Counter é necessário especificar o nome da instância do contador, através da propriedade InstanceName, como é o caso da categoria “Processor” do exemplo acima, onde está sendo utilizado a instância “_Total”. Na minha máquina, para a categoria “Processor”, também estão disponíveis as instâncias “0″ e “1″, que se referem aos dois núcleos do meu AMD Athlon™ 64 X2 Dual Core Processor 6000+.

Na categoria “Memory” não é necessário especificar o nome da instância. Veja abaixo o exemplo de como obter a quantidade de memória disponível:

PerformanceCounter ramCounter = new PerformanceCounter
{
    CategoryName = "Memory",
    CounterName = "Available MBytes"
};

float availableRam = ramCounter.NextValue();

Para recuperar todas as categorias disponíveis para medição, use o método GetCategories da classe PerformanceCounterCategory:

PerformanceCounterCategory[] categories;
categories = PerformanceCounterCategory.GetCategories();

A partir de uma categoria, você pode recuperar todas os nomes de contadores. Por exemplo, o código abaixo:

var category = new PerformanceCounterCategory("System");

foreach (PerformanceCounter counter in category.GetCounters())
{
    Console.WriteLine(counter.CounterName);
}

Gera a seguinte saída:

File Read Operations/sec
File Write Operations/sec
File Control Operations/sec
File Read Bytes/sec
File Write Bytes/sec
File Control Bytes/sec
Context Switches/sec
System Calls/sec
File Data Operations/sec
System Up Time
Processor Queue Length
Processes
Threads
Alignment Fixups/sec
Exception Dispatches/sec
Floating Emulations/sec
% Registry Quota In Use

Eu fiz duas aplicações console de exemplo utilizando Performance Counters:

  • Uma exibe a porcentagem de uso da CPU e a quantidade de MB de memória disponíveis, atualizando as informações a cada um segundo;
  • A outra lista todos os nomes de instâncias e contadores de algumas categorias.

Você pode baixar essas duas aplicações aqui.


Post original:
http://prodis.pro.br/2009/05/04/como-medir-o-uso-da-cpu-utilizando-performance-counters

.

29abr2009

Ruby in Steel, brincando com Ruby e Rails no Visual Studio 2008

(2) comentários

Para quem ainda está em dúvida se Ruby é legal ou porque ele é legal, e se é mais fácil desenvolver com Rails para Web ou não, aqui estão algumas dicas para fazer algumas “brincadeiras” nas horas de folga. Garanto que estas brincadeiras vão virar um vício em pouco tempo.
Ruby é uma linguagem apaixonante, mesmo para quem está começando, é muito fácil para escrever e a sensação é que as coisas vão fluindo tranquilamente e “sem dor”.

Detalhe, Ruby in Steel é um produto para o Visual Studio da Microsoft, existe a versão comercial, mas nos links abaixo tudo é “free”, de graça, isto mesmo. Claro que é uma versão minimalista com pouco recursos, mas o suficiente para iniciar a transição. Por quê iniciar a transição? Teste Ruby e teste o Ruby on Rails e logo, logo, Terminal será seu nome e Editor de Texto seu sobrenome, IDE será coisa do passado.

IDE Visual Studio 2008 com Ruby

Esta versão do Ruby in Steel inclui a versão Express (gratuita) do Visual Studio 2008.
Ruby in Steel – Personal Edition 2008

O e-book do Huw CollingBourne também é free, e explica Ruby de uma maneira fácil para quem desenvolve em .Net.
The book of Ruby – Jan 2009

Post original:
http://mauriciodeamorim.com.br/2009/04/11/brincando-com-ruby-e-rails-no-visual-studio-2008/

11mar2009

Grupo de .NET

(0) comentários

Recentemente ministrei uma palestra sobre Ruby on Rails para um grupo de arquitetos de .Net. Eles se reúnem periodicamente na Unip, na região da USP, para discutir sobre assuntos de tecnologia e arquitetura, com ênfase em plataforma .NET.

Eles mantém um site http://www.dotnetarchitects.net/ onde publicam as palestras que fazem nos seus encontros. Desta vez eu falei sobre Rails, mas nas reuniões anteriores falaram sobre Team System.

Um dos organizadores é o Giovanni Bassi, que é conhecido na comunidade .Net. Veja neste link sobre como participar tanto das reuniões presenciais quanto do Grupo de Discussão que eles mantém. Eles estão sempre atualizados e é um ótimo fórum para falar sobre ASP.NET MVC, Dynamic Data, NHibernate, LINQ, DDD, SOLID e muito mais. Aproveitem.

24nov2008

QCon – Frameworks and DDD: Keeping the Model Clean

(3) comentários

Atualização em 30/04/2009: O vídeo da apresentação está disponível no site InfoQ:
http://www.infoq.com/presentations/Clean-Model-Tim-McCarthy

Tim McCarthy é autor de “.NET Domain-Driven Design with C#: Problem – Design – Solution“, um livro que propõe mostrar os passos da implementação de uma aplicação real utilizando DDD. O livro é dividido em módulos, cada um identificando um problema, elaborando o design e implementando a solução.

Em sua apresentação, ele mostrou algumas técnicas para desacoplar o modelo de domínio da infra-estrutura da aplicação e ainda sim continuar usando recursos de frameworks em .NET.

Frameworks and DDD: Keeping the Model Clean - Tim McCarthy

A idéia é sempre deixar o domínio intacto, somente com o código do coração do software. Se você utilizar as famosas ferramentas de “arrastar e soltar”, vai poluir as entidades do domínio com código de infra-estrutura.

Quando falou a respeito do ADO.NET Entity Framework, o novo framework de mapeamento objeto-relacional da Microsoft, Tim se expressou indignado: “Oh, my God!”. Segundo ele, arrastar e soltar tabela por tabela do seu banco de dados, deixando o Visual Studio gerar um monte de código para você é algo não muito bom para se fazer.

Essas ferramentas de geração de código podem ser uma armadilha para desenvolvedores inexperientes. Para sistemas pequenos, sem grandes pretensões, isso pode ser uma solução rápida e que atende sua necessidade. Mas para grandes aplicações é preciso se concentrar no domínio e eliminar qualquer código que polua suas entidades de negócio.

Não é uma boa idéia criar um modelo de domínio fazendo a relação de uma entidade por tabela no banco de dados. As tabelas do banco de dados pertencem à infra-estrutura do sistema. O seu modelo de domínio deve ser rico e refletir seu negócio, o mais próximo da realidade possível. A partir do seu modelo de domínio é que você constrói uma infra-estrutura de persistência de dados.

A apresentação de Tim McCarthy foi repleta de exemplos reais (e rodando) de código. Num deles, mostrou uma classe de entidade do domínio onde havia uma referência using para o namespace Microsoft.SharePoint. Esse foi um tipico exemplo de entidade de negócio poluída.

Também tivemos um exemplo de utilização de repositórios (Repository), que segundo Tim, são um tipo de abstração da persistência, comparando-os como um tipo de coleção de dados, onde é possível listar, inserir, alterar e remover seus itens. Ele enfatizou que o classes do modelo de domínio podem usar repositórios, mas elas devem estar ligadas somente às interfaces dos repositórios, não acopladas às suas implementações.

Outra parte “prática” da apresentação foi a utilização de injeção de dependência (Dependency Injection) de repositórios nas classes de serviço e alteração do tipo de persistência via arquivo de configuração.

Tim também mostrou a implementação de uma unidade de trabalho (Unit of Work), onde a mesma não conversava diretamente com a base de dados.

A apresentação excedeu 10 minutos do tempo previsto, pois tinha muito código interessante a ser mostrado. Isso só acabou instigando a dar uma olhada no seu livro, que vem com o código fonte de todos os passos de construção de uma aplicação em .NET aplicando os padrões de DDD.

Só não esqueça que todo esse código somente vai poder lhe ajudar se você definir bem seu modelo de domínio, consistente e conciso com a realidade do seu negócio.
.