SOLID: Inversão de Controle com Dependency Injection – Parte 2

Na primeira parte desse post, discutimos de forma breve o que é Inversão de Controle (IoC), apresentando 2 formas bem conhecidas de aplicar esse princípio:

• ‘Service Locator Pattern
• ‘Dependency Injection Pattern’.

Nele citamos algumas vantagens que nos levaram a escolher DI como a nossa solução de IoC. Já nessa segunda parte, vamos apresentar com detalhes as funcionalidades do container Unity, que foi a API usada no nosso projeto.

Porque Unity?

Existem diversas implementações de containers de IoC no mercado. Esta comparação mostra o desempenho e as features de mais de 20 containers diferentes em .NET. No entanto, como essa era uma ferramenta nova (para nós do M3) e já utilizávamos o Enterprise Library Data Application Block para fazer operações com o banco de dados, acabamos optando pelo Unity, até agora com ótimos resultados.

Se no momento da decisão de utilizar o container nós soubéssemos que tantas outras implementações existiam – teríamos considerado além do Unity, apenas algumas das bibliotecas mais populares, como Castle Windsor, Spring.NET e NInject – ainda assim é provável que tivéssemos optado pelo Unity. No entanto, uma ótima segunda opção teria sido o Simple Injector, pois tem um grande conjunto de features úteis e ainda assim é extremamente rápido.

Utilizando o container

Para instalar o container Unity e começar a utilizá-lo em um projeto, basta procurar por “Unity” no ‘Package Manager’ do nuget e instalar o pacote. Para informações sobre essa ferramenta e como utilizá-la consulte o site oficial do nuget.

Com a referência no lugar, estamos prontos para programar utilizando Unity. A interface do container é bastante similar a da nossa classe fake “Container” do primeiro exemplo. Obviamente isso foi pré-condicionado ao fato de que é esse que estamos usando. Contudo, isso não quer dizer que as demais implementações se desviam muito desta: a maioria dos containers trabalha com os mesmos conceitos e utiliza uma camada pública similar.

Vejamos então como fica o código anterior de registro das classes, utilizando este novo container:

    using Microsoft.Practices.Unity;

    static class Program
    {
        private static IUnityContainer _container;

        [STAThread]
        static void Main()
        {
            _container = new UnityContainer();
            _container
                .RegisterType<ICommandValidator, CommandValidator>()
                .RegisterType<CommandHandler>()
                .RegisterType<Form1>();

            Application.Run(_container.Resolve<Form1>());
        }
    }

Até o momento falamos superficialmente sobre as funcionalidades básicas de um container, mas o Unity tem diversas opções adicionais que podem vir a ser úteis no desenvolvimento do dia a dia. Vejamos algumas dessas features e como o container Unity as implementa:

- Modo de configuração:

Em todos os exemplos criados, nós utilizamos o código C# para registrar as nossas classes no container. Geralmente esse modo de registro chamado de ‘programático’ é suportado por todas as implementações de containers e é uma forma bastante satisfatória de efetuar os registros, tanto por não ser muito verbosa quanto por oferecer verificação em tempo de compilação sobre os tipos. No entanto, essa não é a única forma de se registrar classes no container Unity: Existem aplicações que precisam de mais flexibilidade para alternar entre implementações de uma mesma interface. Estas precisam que isso seja feito sem requerer uma recompilação do projeto. Sendo assim, a configuração via arquivos ‘.config’ suportada pelo Unity é uma boa opção. Apesar de perder a verificação em tempo de compilação, ela nos permite fazer essas trocas dinâmicas sem afetar a aplicação. Em ambientes mais avançados é possível inclusive persistir esse tipo de configuração em um banco de dados ou no registro do Windows, utilizando um IConfigurationSource customizado e os overloads de LoadConfiguration (às vezes é difícil achar bons exemplos sobre o Enterprise Library, mas esse artigo bastante antigo deve dar uma ideia de como funciona esse mecanismo genérico de ‘configuration sources’) . O exemplo abaixo utiliza um arquivo de configuração ‘app.config’ para fazer os mesmos registros da versão em código:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>

  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <assembly name="WindowsFormsApplication1"/>
    <namespace name="WindowsFormsApplication1"/>

    <container>
      <register type="ICommandValidator" mapTo="CommandValidator" />
      <register type="CommandHandler" />
      <register type="Form1" />
    </container>
  </unity>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
</configuration> 

Para mais informações sobre a configuração do container via arquivo de configuração consulte a página oficial no MSDN.

Enquanto o novo código, baseado agora em uma configuração externa, fica assim:

    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.Configuration;

    static class Program
    {

        private static IUnityContainer _container;

        [STAThread]
        static void Main()
        {
            _container = new UnityContainer();
            _container.LoadConfiguration();

            Application.Run(_container.Resolve<Form1>());
        }
    }

Ao utilizar os tipos em Microsoft.Practices.Unity.Configuration, a dll System.Configuration deve ser também incluída no projeto.

Note como o método “LoadConfiguration” utiliza a informação presente no app.config para criar os registros no container. Esse método tem alguns overloads que recebem um configSection e/ou o nome de um container mas, como definimos apenas uma configuração dele e utilizamos o nome da section como “unity” (que é o padrão), não foi necessário informar nenhum desses parâmetros. Esse tipo de flexibilidade pode ser relevante em um cenário mais complexo onde existem diversos containers na mesma aplicação, ou quando é preciso separar logicamente diversas configurações no ‘.config’.

Além da diferenciação entre runtime (configuração em código) e designtime (através de configuração via IConfigurationSource ou arquivo ‘.config’), existe uma outra categoria de registro de tipos, chamada de ‘Registro por convenção’.

Ela se baseia em critérios de avaliação para fazer os registros em diversas dlls ao mesmo tempo no container, sendo assim um facilitador quando existem dezenas de classes a serem registradas. Esse mecanismo só foi introduzido no Unity na versão 3.0. Esse blog explica como a feature funciona nessa nova versão.

Como a versão 3.0 do Unity precisa do .Net Framework 4.5, o nosso time não pode ainda desfrutar de seus benefícios (o novo .Net não é mais suportado em plataformas que ainda são essenciais aos nossos clientes, como Windows Server 2003) e, portanto, optamos por uma solução alternativa para utilizar o registro por convenção, o ‘Unity Auto Registration’. Voltaremos a este assunto quando falarmos das alterações no M3 na terceira parte desse post.

- Modos de injeção:

Ainda não foi mencionado exatamente como o container constrói as instâncias das nossas classes e injeta as suas dependências. O Unity segue um algoritmo relativamente simples para isso. Vejamos os passos em alto nível desse processo:

1 – ‘Constructor Injection’

No primeiro momento, o construtor com o maior número de dependências é procurado, via reflection, no tipo solicitado. Quando é encontrado, o mesmo processo se repete recursivamente para todas as suas dependências, até que seja encontrado um construtor completamente independente, que é então invocado. Nesse ponto, a classe é criada e o processo retorna criando todas as demais instâncias que antes não podiam ser construídas por falta de parâmetros, obtendo no fim uma instância com todas as dependências criadas. Essa primeira etapa é conhecida como ‘Constructor Injection’, e é a forma mais robusta de injeção por garantir que o objeto será criado em um estado válido. Se por alguma razão não for possível resolver algum dos parâmetros de um construtor em toda a cadeia, uma exceção é disparada indicando onde o problema ocorreu.

É possível controlar qual construtor será chamado pelo container, sobrescrevendo a regra citada anteriormente. Isso é feito através do atributo InjectionConstructor e pode ser interessante ou até necessário em alguns casos onde existam múltiplos construtores para o mesmo tipo.

2 – ‘Property Injection’

Em seguida, com o objeto já construído, o container passa para uma segunda etapa, chamada de ‘Property Injection’. Todas as propriedades com um setter público e que foram marcadas com o atributo Dependency são resolvidas automaticamente. A diferença nesse ponto é que essas dependências por propriedades são consideradas ‘dependências opcionais’, significando que, caso não seja possível resolvê-las, ‘null’ é retornado (em vez de uma exceção ser disparada).

3 – ‘Method Injection’

Por último, a etapa de ‘Method Injection’ é processada. Aqui, os métodos públicos marcados com o atributo InjectionMethod são chamados da mesma forma que os construtores, resolvendo todas as dependências especificadas. Estas dependências são obrigatórias (por padrão) nesses métodos de injeção, da mesma forma que os parâmetros do construtor. Note que é possível que uma classe tenha diversos métodos marcados com esse atributo, apesar de isto não ser uma prática comum. Nesse caso, é importante não assumir que eles serão chamados em uma ordem específica, pois a ordenação das chamadas depende da ordenação criada pelos métodos em System.Reflection, que não garantem ordenação.

Tanto na primeira etapa quanto na terceira, pode-se especificar que uma dependência é opcional (ou seja, deve retornar null em vez de disparar uma exceção) utilizando o atributo OptionalDependency no argumento desejado.

Depois da execução dessas três etapas com sucesso, o nosso objeto está pronto e é retornado ao “chamador”.

Apesar de toda essa flexibilidade oferecida pela API do Unity, é importante ressaltar aqui algumas boas práticas em relação ao uso de containers em geral. Um dos grandes incentivos para a utilização desse design pattern é o nível baixo de intrusão no código (sendo esse um fator decisivo na nossa escolha no M3 por ele no lugar de um ServiceLocator). Em boa parte das situações, o único ponto do código que irá se referir a um container é o ponto de configuração – que, como dito antes, costuma ser um ponto central de inicialização. Isso é bem proveitoso, pois faz com que as nossas classes continuem independentes do container e nos permite inclusive trocar a biblioteca de container com pouquíssimo esforço. No nosso exemplo até aqui, mudar a aplicação para utilizar o Simple Injector ou o Castle Windsor seria extremamente trivial.

Mas, quando começamos a utilizar esses controles customizados, deixamos nossas classes dependentes da API específica do container. Ao utilizar Property Injection, por exemplo, somos obrigados a incluir a biblioteca Unity (caso a classe em questão não esteja no mesmo projeto da classe onde o container é configurado, o que é bastante comum), o namespace Microsoft.Practices.Unity e o atributo Dependency. Apesar de esses mecanismos existirem em praticamente todas as APIs de containers, o uso excessivo deles é considerado uma má prática, sendo preferível sempre utilizar a injeção padrão via construtor. Apesar desse aviso, existem situações reais onde de fato ficamos impedidos de utilizar a injeção via construtor, e então, esses controles extras passam a ser a nossa única opção. Voltaremos a falar sobre esse problema mais adiante quando os ajustes feitos no M3 forem discutidos.

- Ciclo de vida das instâncias no container

Assim como demais containers, o Unity nos permite, no momento do registro de um tipo, especificar como instâncias serão tratadas durante a execução da aplicação. Esse comportamento pode ser especificado através de overloads dos métodos de registro que recebem um . O LifetimeManager é a classe base utilizada em toda a API para implementação de políticas de gerenciamento do ciclo de vida das instâncias/tipos cadastrados no container. O Unity já vem com algumas implementações distintas dessa classe, contemplando diversos cenários sem que seja necessário criar um LifetimeManager customizado. Esses gerenciadores embutidos na API são os seguintes:

. ExternallyControlledLifetimeManager:

Registra o tipo sem que haja qualquer influência do container em nenhum ponto do seu ciclo de vida. Pode ser interessante para registrar membros de bibliotecas externas que já são mantidos por outros mecanismos. Provavelmente o gerenciador mais especializado e o menos utilizado.

. TransientLifetimeManager:

Um objeto novo é construído cada vez que é necessário resolver o tipo. Esse é o modo padrão de registro quando um LifetimeManager não é especificado.

. PerResolveLifetimeManager />:

Apenas um objeto do tipo é criado durante um Resolve. Inicialmente parece equivalente ao TransientLifetimeManager, mas difere em um ponto importante: durante um Resolve, é possível que um tipo seja uma dependência de mais de uma classe da cadeia sendo construída. Nesse caso, esse objeto será criado uma única vez durante o processo e compartilhado entre essas classes.

. PerThreadLifetimeManager:

Apenas um objeto do tipo é criado para cada thread que acessa o container.

. ContainerControlledLifetimeManager:

Apenas uma instância do tipo é criada em todo o container. Requisições subsequentes retornam sempre a mesma instância. Tem uma função semelhante ao design pattern Singleton. Bastante útil para instâncias que são custosas de construir ou que mantém um estado que deve ser compartilhado entre diversas classes. Utilizado em diversos pontos na migração do M3 onde existiam classes singletons.

. HierarchicalLifetimeManager:

Especialização do ContainerControlledLifetimeManager que, em vez de sempre retornar a mesma instância para uma hierarquia de containers, faz com que cada container filho receba um objeto diferente.

Como citado acima, ao utilizar o método RegisterType o LifetimeManager aplicado é o TransientLifetimeManager por padrão. Contudo, chamar RegisterType não é a única forma de cadastrar tipos no container. Existe também o RegisterInstance que, em vez de criar uma instância do tipo no momento do Resolve, espera uma instância já pronta no momento do registro. Nesse caso o ContainerControlledLifetimeManager é utilizado por padrão.

- Pontos de Extensão:

A API Unity, como toda biblioteca bem projetada, apresenta diversos pontos de extensão para que seja possível adequá-la a praticamente qualquer sistema existente. Por exemplo, é possível criar um LifetimeManager que trate as instâncias de uma forma alternativa aos LifetimeManagers padrão ou criar novos blocos xml de configuração utilizando o mesmo configSection original através de extensões do schema padrão e utilizar esses novos elementos de configuração em outras extensões.

. UnityContainerExtension:

O ponto mais comum de extensão, no entanto, é via implementação e registro da classe UnityContainerExtension. O mecanismo de extensão do container utilizando essa classe é razoavelmente complexo, pois requer um conhecimento tanto de estruturas internas quanto de padrões utilizados na implementação do container. Através dessa classe, existem basicamente dois pontos iniciais de extensão, que são os métodos Initialize (utilizado quando a extensão é adicionada ao container), e Remove (chamado quando a extensão é removida). A partir daí, boa parte do pipeline de criação é exposto através de uma propriedade na classe base, Context.

A classe ExtensionContext exposta via ‘Context’ por sua vez possui alguns eventos que podem ser utilizados para fazer operações em pontos específicos (como ChildContainerCreated ou Registering) e expõe, via propriedades, coleções importantes, em especial BuildStrategies. O pipeline de criação implementa o Chain of Responsibility Pattern através dessa coleção, ou seja, estratégias de manipulação são adicionadas a essa coleção e posteriormente chamadas em sequência até que uma delas marque ‘BuildComplete = true’ no contexto de build. Essas estratégias são encapsuladas na classe base BuildStrategy. São bastante comuns extensões do container Unity que adicionam uma estratégia no momento de criação (Initialize) para tratar tipos em um certo ponto do pipeline. Veremos um exemplo de uma extensão nesse formato mais adiante, quando discutirmos a integração do container no M3.

Quando uma extensão é criada, basta utilizar um dos métodos de registro de extensões, AddNewExtension (que registra um tipo como uma extensão equivalentemente ao RegisterType) ou o AddExtension (que registra uma extensão já criada previamente similar ao RegisterInstance). Da mesma forma que o restante das características de configuração do container, o registro das extensões pode também ser feito em design time, via arquivo de configuração.

Uma peculiaridade do método AddNewExtension é que o próprio container é utilizado para construir a instância da extensão, sendo possível então a injeção de dependências também nessas extensões caso se faça necessário. Nessa situação, como a inicialização é feita logo após a chamada ao Add, é esperado que essas dependências tenham sido registradas em um ponto anterior do código. Note que, por esse motivo, ao utilizar injeção nas extensões não é possível diretamente utilizar apenas um conjunto de registros no arquivo de configuração, pois através dele, as extensões são sempre registradas antes dos tipos. Assim, é preciso registrar as dependências previamente no container (antes de chamar LoadConfiguration) ou optar por dois trechos de configuração no .config e consequentemente duas chamadas ao LoadConfiguration em sequência. O fato de o LoadConfiguration ser aditivo torna esse tipo de configuração possível, como demonstra esse exemplo:


  <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <container name="MyExtensionDependencies">
      <register type="IDependency" mapTo="Dependency" />
    </container>
    <container>
      <register type="ITypeProcessedByMyExtension" mapTo="TypeProcessedByMyExtension" />
      <extension type="MyApplication.MyExtension, MyApplication" />
    </container>
  </unity>

O novo código, agora baseado em uma configuração com , fica assim:


  private static IUnityContainer _container;

  [STAThread]
  static void Main()
  {
      _container = new UnityContainer();
      _container
          .LoadConfiguration("MyExtensionDependencies")
          .LoadConfiguration();

      Application.Run(_container.Resolve<Form1>());
  }

Ou configurado diretamente em código fica assim:


   private static IUnityContainer _container;

   [STAThread]
   static void Main()
   {
       _container = new UnityContainer();
       _container
           .RegisterType<IExtensionDependency, ExtensionDependency>()
           .AddNewExtension<MyExtension>()
           .RegisterType<ITypeProcessedByMyExtension, TypeProcessedByMyExtension >();

       Application.Run(_container.Resolve<Form1>());
   }

Os registros no trecho acima serão feitos na seguinte ordem: IDependency, MyExtension, ITypeProcessedByMyExtension.

Conclusão

Essa segunda parte deve ser suficiente para dar uma noção mais realista de como um Container de IoC funciona. Vimos que o nível de flexibilidade do Unity é bastante grande (a maioria das features discutidas aqui existem também em outros containers, e alguns deles têm ainda mais opções!), mas vimos também que a curva de aprendizado para utilizar todo o potencial da ferramenta é significativa. O próprio time do M3 ainda não experimentou alguns pontos mais avançados, como o mecanismo de interceptação que permite ampliar a funcionalidade de classes através de comportamentos automaticamente acoplados via proxies dinâmicos (similar ao objetivo de AOP (Aspect Oriented Programming)), ou o uso de uma hierarquia de containers (os child containers no caso do Unity), que dá o poder de controlar camadas diferentes da aplicação com containers específicos.

A próxima e última parte falará sobre os problemas encontrados e como eles foram solucionados no contexto do projeto M3, utilizando o container Unity. Ficará claro também que, ao aplicar esse padrão, diversos outros problemas que antes estavam “dormentes” passam a ficar evidentes e novamente passíveis de refatoração, sendo este um ponto extremamente positivo para a saúde do projeto, voltando ao propósito inicial de utilizar os princípios SOLID.

Escrito por Juliano Gonçalves.

SOLID: Inversão de Controle com Dependency Injection – Parte 1

Nesses últimos meses, algumas mudanças interessantes foram feitas pelo time do projeto M3 em relação à estrutura do projeto. Esta série de posts será dedicada a uma dessas mudanças estruturais: a aplicação de ‘Inversão de Controle’ (Inversion of Control), ou ‘Inversão de Dependências’ (Dependency Inversion), uma técnica que ganha cada vez mais força na comunidade de desenvolvedores por principalmente promover o desacoplamento entre as classes.

Desse ponto em diante, irei me referir a esse tópico como simplesmente IoC. O intuito deste texto não é apresentar todos os conceitos relacionados a esse assunto exaustivamente ou indicar a melhor forma de usar IoC, mas sim dar uma breve descrição de cada processo e mostrar, na prática, como esses conceitos foram aplicados no nosso principal sistema.

Esse post será dividido em três partes. Nessa primeira, explicarei de forma sucinta o conceito geral de IoC e como ele pode ser aplicado genericamente para promover o desacoplamento entre classes e suas dependências. Na parte 2, falarei sobre a aplicação dos conceitos introduzidos aqui por meio de uma API real de IoC, conhecida por Unity. Por fim, abordarei os desafios encontrados e benefícios obtidos na adaptação de partes do código do M3 para uso desta API.

Em teoria

IoC é um dos componentes de um conjunto bem conhecido de princípios voltados ao desenvolvimento de software orientado a objetos. Esse conjunto é normalmente encontrado através do acrônimo SOLID. Cada letra desse acrônimo representa uma sigla de um entre cinco princípios de programação que, quando aplicados, tornam o desenvolvimento de software mais robusto, fazendo com que o esforço para manter e ampliar o mesmo seja menor ao longo do tempo. Apesar de todos os princípios serem igualmente importantes, nosso foco a partir daqui será diretamente em IoC.

Existem algumas técnicas que podem ser utilizadas para aplicar o conceito de IoC, sendo as duas mais comuns: o ‘Service Locator Pattern’ e o ‘Dependency Injection Pattern’. Ambos se baseiam em um componente central, responsável por obter instâncias de todas as classes nele registradas (similar neste quesito a uma Factory genérica ou ao Activator do .NET.

Service Locator VS Dependency Injection

Tomemos como base um conjunto de classes extremamente simples, que servirá para mostrar as duas técnicas citadas anteriormente:

    public class Command
    {
        public string Name { get; set; }
    }

    public interface ICommandValidator
    {
        void Validate(Command c);
    }

    public class CommandValidator : ICommandValidator
    {
        public void Validate(Command c)
        {
            if (c == null)
                throw new ArgumentNullException("c");

            if (String.IsNullOrEmpty(c.Name) || c.Name.Length != 4)
                throw new Exception("Nome deve ter exatamente 4 caracteres");
        }
    }

    public class CommandHandler
    {
        private readonly ICommandValidator _validator;

        public CommandHandler()
        {
            _validator = new CommandValidator();
        }

        public void Handle(Command c)
        {
            _validator.Validate(c);
            Console.WriteLine(c.Name);
        }
    }

Note como o CommandHandler cria um novo Validator dentro do construtor: Isso cria um acoplamento entre a classe e a sua dependência, de modo que, se amanhã desejarmos implementar um novo validador, precisaremos alterar o código em todos os pontos para trocar a criação do validador para a nossa nova classe, que também implementa ICommandValidator. Obviamente, nesse exemplo simples isso não seria um problema, mas em um grande projeto, que utiliza essa classe em dezenas de pontos distintos potencialmente em várias dlls diferentes, tal modificação já seria complicada. Esse é exatamente o ponto que será atacado pelas duas técnicas mencionadas.

Utilizando o ServiceLocator

Imaginem o ServiceLocator como um dicionário de diferentes tipos para implementações, que contém métodos para adicionar uma instância para um determinado tipo e para recuperar a instância registrada através do tipo especificado. Utilizando esse mecanismo, o novo CommandHandler seria construído da seguinte maneira:

        public CommandHandler()
        {
            _validator = ServiceLocator.GetService<ICommandValidator>();
        }

Note que agora o nosso objeto passa a ter uma dependência com o próprio ServiceLocator (que é uma classe estática, nesse caso) mas nós ganhamos o poder de trocar a implementação do validador a qualquer momento sem precisar alterar o CommandHandler. O registro desse Validator passa para um outro ponto da aplicação (geralmente central, tal como o método ‘Main’ em um Console Application ou o evento ApplicationStart em um Web Application).

Utilizando Dependency Injection

Dependency Injection (DI) é o nome do processo de passar as dependências para as classes que as precisam. Em geral, esse mecanismo é implementado de forma automática através de um ‘Dependency Injection Container’, uma classe que compartilha diversas particularidades com a implementação do ServiceLocator, mas que tem uma propriedade especial extremamente importante: a capacidade de descobrir dependências em um tipo especificado e as construir automaticamente. Antes de entrar no mérito de como isso é feito, o nosso construtor ficaria dessa forma utilizando DI:

        public CommandHandler(ICommandValidator validator)
        {
            _validator = validator;
        }

Note a simplicidade desse novo método. Agora, não existe nenhuma dependência externa na nossa classe. O seu construtor passa a ser um ‘contrato’ que estabelece o que ela precisa para funcionar. Nesse momento, você se pergunta como construir, então, um CommandHandler, já que agora ele precisa que um validador seja especificado. Antes isso era abstraído do consumidor da classe pelo ServiceLocator; os usuários do CommandHandler não precisavam se preocupar com o validador, e apenas utilizavam um ‘new CommandHandler()’, por exemplo.

Essa foi uma dúvida que eu mesmo tive, quando comecei a migração de partes do nosso código. “Ok, mas e agora, como eu crio essas classes?”. A resposta é bem mais óbvia do que parece: continuar utilizando DI na classe consumidora: Em vez de criar um CommandHandler, a classe recebe (novamente por meio do construtor) um CommandHandler. Assim, a responsabilidade de nos fornecer as implementações é novamente relegada para o próximo consumidor. Isto continua até que uma raiz seja atingida, da qual toda a execução é iniciada.

Novamente a título de demonstração, imaginemos uma aplicação do tipo Windows Forms, que tipicamente contém apenas uma linha responsável por iniciar a infraestrutura de janelas:

        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

Suponhamos agora que o nosso ‘Form1’ precise de um CommandHandler. Nós então colocamos essa informação no construtor do form, da mesma forma que fizemos no construtor do próprio CommandHandler quando este precisava de um Validator. Como não temos mais para onde transferir a responsabilidade de criação dos objetos (estamos na raiz da aplicação!), precisamos agora construir o Form1, mas fazemos isso de uma maneira peculiar:

        private static Container _container;

        [STAThread]
        static void Main()
        {
            _container = new Container();
            _container
                .Register<ICommandValidator, CommandValidator>()
                .Register<CommandHandler>()
                .Register<Form1>();

            Application.Run(_container.Resolve<Form1>());
        }

Apesar de existirem diversas implementações de containers para diversas linguagens, os nomes das classes e métodos mostrados nesse exemplo não refletem diretamente nenhuma delas, servindo apenas para dar uma visão geral do tipo de funcionalidade que um container típico provê.

O código acima obtém uma instância do form através de uma chamada ao container explícitamente. Note que apesar de o nosso form agora precisar de um CommandHandler no seu construtor, isso não é especificado em momento algum. O que ocorre é que o método ‘Resolve’ percorre o tipo especificado procurando por dependências, criando cada uma delas de forma recursiva. Ele faz isso até que toda a árvore de objetos esteja pronta, devolvendo uma instância de um Form1 já com o CommandHandler e o seu CommandValidator devidamente preenchidos. Cada uma dessas ‘construções’ é feita analisando o estado atual dos registros feitos no container. Por exemplo:

     _container.Register<ICommandValidator, CommandValidator>()

Essa linha faz com que todas as tentativas subsequentes de ‘resolver’ o tipo ICommandValidator retornem uma instância de um CommandValidator. Isso caracteriza um mapeamento de tipos. Ao contrário do ServiceLocator, nesse ponto ainda não criamos nenhuma instância do CommandValidator. Se por algum motivo não utilizássemos o nosso form em nenhum ponto do código, esse validador nunca ia ser criado.

Conclusão

Até aqui, pudemos ter uma ideia de como IoC pode nos ajudar a escrever aplicações mais robustas e melhor preparadas para mudanças futuras. Vimos também uma comparação prática entre duas técnicas (DI e Service Location) exemplificando como promover o desacoplamento entre classes e suas dependências. Apesar de os dois métodos discutidos serem bastante poderosos, DI acabou sendo a nossa solução de IoC: pela vantagem de estabelecer claramente as dependências entre os módulos e por evitar mais uma dependência (com o ServiceLocator). Em um segundo momento, mostraremos mais características que uma API real de DI (Unity) nos provê, estudando suas particularidades e benefícios, colocando essa solução numa posição ainda mais favorável em comparação ao uso de um Service Locator.

Até mais!

Escrito por Juliano Gonçalves.

TUTORIAL – Android – Parte 4 – Persistência de Dados

Com a última parte do nosso tutorial sobre desenvolvimento Android, você já terá conhecimento suficiente para começar a desenvolver aplicações voltadas para problemas e necessidades reais. Caso não tenha visto, vale indicar:

- TUTORIAL – Android – Parte 1 – Primeiros passos
- TUTORIAL – Android – Parte 2 – Conhecendo suas ferramentas
- TUTORIAL – Android – Parte 3 – Construção de Telas

Já que pouca coisa realmente útil poderia ser feita se tudo começasse do zero toda vez que o usuário a fechasse, devemos conhecer as formas de persistir as informações de uma aplicação. Hoje vamos abordar as duas formas mais simples de guardar informações de forma persistente no Android, utilizando Shared Preferences ou o Storage (interno ou externo) do dispositivo. Para isso vamos tomar como base o aplicativo que desenvolvemos no passo anterior, que pode ser baixado neste link (no Eclipse, utilize a opção Importar e escolha Projeto Existente para o Workspace, selecionando a pasta descompactada), e alterá-lo para o exemplo de hoje.

O que o nosso aplicativo faz é salvar as informações que foram inseridas na tela, utilizando a opção selecionada – que será a Activity principal. Em seguida, assim que o botão Exibir for pressionado, serão lidas as informações que foram previamente salvas. O primeiro passo é construir a tela principal de forma semelhante à apresentada abaixo, utilizando os conceitos da parte 3 do nosso tutorial. Vale salientar que a tela que exibirá o que foi salvo não precisa ser modificada.

Agora precisamos decidir como efetivamente salvar e ler essas informações, que é o ponto principal que veremos hoje:

- De que forma vamos salvar os dados dos campos da tela (baseando-se no estado dos Radio Buttons)

- Onde será realizada a leitura os dados (após o botão Salvar ser pressionado), que serão exibidos na segunda tela assim que o botão Exibir for pressionado.

Então, vamos começar definindo o método que será executado no uso do botão Salvar:

// Evento de clique do botão salvar.
	public void botaoSalvar(View view) {

		// Instância do elemento de opção.
		RadioButton opcaoSharedPreferences = (RadioButton) findViewById(R.id.radioButton_SharedPref);

		// Recuperamos os valores dos campos da tela.
		EditText campoTexto = (EditText) findViewById(R.id.editText_Texto);
		EditText campoNumero = (EditText) findViewById(R.id.editText_Numero);

		String texto = campoTexto.getText().toString();
		long numero = Long.parseLong(campoNumero.getText().toString());

		// Teste da opção selecionada.
		if (opcaoSharedPreferences.isChecked()) {
			salvarSharedPref(texto, numero);
		} else {
			salvarInternalStorage(texto, numero);
		}
	}

Instanciamos o Radio Button da esquerda, que representa a opção de Shared Preferences, recuperamos também as informações digitadas e testamos qual opção foi selecionada, passando estes para que sejam salvos.

Dica: troque os identificadores de cada componente no arquivo de layout (XML) para facilitar a identificação.

Shared Preferences – Escrita

Utilizando essa forma para armazenar informações, é possível salvar entradas do tipo chave-valor, onde se associa um “nome” a uma determinada informação para que depois se possa recuperá-la justamente através deste nome. O Android salva tudo em um arquivo XML dentro da estrutura interna “de disco” em cada aplicação. Mas não precisamos nos preocupar com leitura ou interpretação do XML, o Android oferece funções que abstraem essas questões, deixando para o programador apenas a tarefa de salvar e depois buscar novamente o que foi salvo. Essa opção é indicada para poucas informações e que sejam simples, como números, strings e valores booleanos. Por isso esse mecanismo é geralmente utilizado para armazenar as preferências que o usuário definiu na aplicação.

private void salvarSharedPref(String texto, long numero) {

		// Cria ou abre.
		SharedPreferences prefs = getSharedPreferences("preferencias_1", Context.MODE_PRIVATE);

		// Precisamos utilizar um editor para alterar Shared Preferences.
		Editor ed = prefs.edit();

		// salvando informações de acordo com o tipo
		ed.putString("TEXTO", texto);
		ed.putLong("NUMERO", numero);

		// Grava efetivamente as alterações.
		ed.commit();
	}

Primeiro criamos (ou abrimos se já existir) o SharedPreferences informando o nome do arquivo e o modo de acesso que ele deve ter. Depois criamos um Editor, que nada mais é que uma classe auxiliar para escrever no arquivo, e salvamos o que foi passado, informando uma chave para cada informação. Por último realizamos o commit, para efetivamente escrever tudo no arquivo.

Storage – Escrita

A opção do Storage nada mais é que um espaço “em disco” que cada aplicação tem onde é possível salvar arquivos. Existe a opção do Internal Storage (espaço na estrutura de arquivos interna da aplicação, que é o mesmo onde fica(m) o(s) arquivo(s) de Shared Preferences) e do External Storage, que geralmente é um espaço no SDCard – podendo ser público (pastas de música ou fotos por exemplo) ou da aplicação – e nem sempre estará disponível (se o SDCard for removido, por exemplo). Os arquivos salvos dessa forma possuem mais liberdade no que diz respeito ao QUE, QUANTO e DE QUE FORMA os dados serão salvos, mas consequentemente se tem um pouco mais de trabalho para manipulá-los. Existem várias maneiras (classes) disponíveis de manipulação de arquivos, mas vamos utilizar uma que acredito ser mais simples, principalmente por ter métodos que aceitam diretamente strings ao invés de apenas bytes.

private void salvarInternalStorage(String texto, long numero) {
		FileWriter fileWriter = null;

		try {
			// Cria o arquivo onde serão salvas as informações.
			File file = new File(getFilesDir().getPath()+"/arquivo_1.txt");
			fileWriter = new FileWriter(file, true);

			fileWriter.append(texto);
			fileWriter.append("n");// Quebra de linha.
			fileWriter.append(String.valueOf(numero));

			// Escreve no arquivo.
			fileWriter.flush();

		} catch (IOException e) {
			Log.e("Erros", "Erro ao salvar usando Internal Storage", e);
		} finally {
			// Fecha os recursos.
			if (fileWriter != null) {
				try {fileWriter.close();}
				catch(Exception e){}
			}
		}
	}

Primeiro criamos o arquivo no diretório do Internal Storage (retornado pelo método getFilesDir()) e depois criamos uma instância da classe auxiliar de escrita no arquivo, informando que queremos usar o modo append (escreve a partir do fim do arquivo, sem apagar ou sobrescrever o que já existia antes). Escrevemos uma informação em cada linha (escrevendo a “quebra de linha”) para facilitar a leitura posteriormente, e efetivamos a escrita das informações no arquivo. O acesso a arquivos em disco pode gerar erros em vários pontos, por isso precisamos do tratamento de exceções, mas por enquanto não vamos abordar esse ponto.

private void salvarExternalStorage(String texto, long numero) {

		// Obtém o estado do storage.
		String mediaState = Environment.getExternalStorageState();

		// Testa se ele está disponível.
		if (mediaState.equals(Environment.MEDIA_MOUNTED)) {

			FileWriter fileWriter = null;
			try {
				// Cria o arquivo onde serão salvas as informações.
				File file = new File(getExternalFilesDir(null).getPath(), "/arquivo_2.txt");
				fileWriter = new FileWriter(file, true);

				fileWriter.append(texto);
				fileWriter.append("n");// Quebra de linha.
				fileWriter.append(String.valueOf(numero));

				// Escreve no arquivo.
				fileWriter.flush();

			} catch (IOException e) {
				Log.e("Erros", "Erro ao salvar usando External Storage", e);
			} finally {
				// Fecha os recursos.
				if (fileWriter != null) {
					try {fileWriter.close();}
					catch(Exception e){}
				}
			}
		}
	}

Para utilizar o External Storage, precisaríamos apenas testar antes se ele está disponível e indicar o caminho correto na criação do arquivo. Para escrever nesse local ainda precisamos adicionar uma permissão no Manifest da aplicação.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

Acessando O Que Foi Salvo

// Evento de clique do botão exibir.
	public void botaoExibir(View view) {
		// Instância do elemento de opção
		RadioButton opcaoSharedPreferences = (RadioButton) findViewById(R.id.radioButton_SharedPref);

		// Cria o intent indicando qual a opção foi usada para salvar os dados.
		Intent intent = new Intent(this, SecondActivity.class);

		if (opcaoSharedPreferences.isChecked())
			intent.putExtra("opcao", "shared_pref");
		else
			intent.putExtra("opcao", "int_storage");

		// Inicia a nova tela.
		startActivity(intent);
	}

De forma semelhante ao que fizemos para o botão Salvar (na verificação da opção) e para passar informação para a próxima Activity (como utilizamos no post anterior), iniciamos a nova tela passando a indicação de qual opção de armazenamento deve ser lida para exibição dos dados:

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Recupera o intent que iniciou a atividade e a mensagem.
		Intent intent = getIntent();
	    String opcao = intent.getStringExtra("opcao");

	    String conteudoSalvo;
	    if (opcao.equalsIgnoreCase("shared_pref")) {
	    	conteudoSalvo = acessaSharedPreferences();
		} else {
			conteudoSalvo = acessaInternalStorage();
		}

	    // Cria uma view para exibir as informações salvas.
	    TextView textView = new TextView(this);
	    textView.setTextSize(30);
	    textView.setText(conteudoSalvo);

	    // Define que o conteúdo exibido pela tela é o campo que
	    // irá exibir as informações.
	    setContentView(textView);
	}

Na criação da nova tela, vamos testar a opção passada e recuperar os dados da fonte correspondente, exibindo em tela ao final.

Shared Preferences – Leitura

private String acessaSharedPreferences() {
		// Acesso às Shared Preferences usando o nome definido.
		SharedPreferences prefs = getSharedPreferences("preferencias_1", Context.MODE_PRIVATE);

		// Acesso às informações de acordo com o tipo.
		String texto = prefs.getString("TEXTO", "não encontrado");
		long numero = prefs.getLong("NUMERO", 0);

		// Formata um string com todo o conteúdo separado por linha.
		return (texto + "n" + numero);
	}

Mais simples ainda que escrever no Shared Preferences é ler as informações dele. Basta acessá-lo da mesma forma e recuperar cada informação pela sua chave identificadora (o seu “nome”), informando após o que deve retornar caso nada seja encontrado para aquela chave. Devolvemos tudo em um string só para simplificar.

Internal Storage – Leitura

private String acessaInternalStorage() {

		String line;
		String conteudo = "";

		BufferedReader br = null;
		try {
			// Acessa o arquivo.
			br = new BufferedReader(new FileReader(getFilesDir().getPath()+"/arquivo_1.txt"));

			// Faz a leitura, uma linha por vez, até o fim do arquivo,
			// gerando um string com todo o conteúdo separado por linha.
			while ((line = br.readLine()) != null) {
				conteudo += line;
				conteudo += "n";// Quebra de linha.
			}

		} catch (Exception e) {
			Log.e("Erros", "Erro ao ler arquivo do Internal Storage", e);
		} finally {
			if (br != null) {
				try {br.close();}
				catch(Exception e){}
			}
		}

		// Retorna o conteúdo do arquivo.
		return conteudo;
	}

Igualmente como ocorreu na escrita, vamos passar o caminho onde criamos o arquivo e utilizar uma classe auxiliar (dessa vez de leitura de arquivos), além de um buffer que vai nos permitir fazer a leitura linha a linha (por isso salvamos separando por linhas) sem nos preocuparmos com a quantidade que deve ser lida por vez.
Utilizamos então um “laço” para ler o arquivo até o seu fim (quando o método readLine() retornar null).

No emulador ou em um dispositivo com “root” é possível ver o sistema de arquivos da aplicação no Eclipse, através da perspectiva DDMS, na aba File Explorer. Cada aplicativo possui uma pasta com o seu nome no caminho /data/data/. Lá você vai encontrar os arquivos que criamos e pode até exportá-los para o seu computador, utilizando o botão de Salvar (ícone de um disquete) no canto superior direito, podendo conferir o que foi salvo pelo aplicativo.

A leitura do External Storage é muito parecida com a que utilizamos no Internal, bastando testar a disponibilidade e usar o caminho correspondente. A pasta onde os arquivos são salvos nesse caso é a /mnt/sdcard/Android/data//files/. Para testar o uso do External Storage, apenas substitua os métodos de escrita e leitura no código da aplicação.

Para a maioria das aplicações essas possibilidades de armazenamento são plenamente suficientes, mas se for necessário um mecanismo mais sofisticado, o Android possibilita o uso de base de dados SQLite, mas isso já é assunto para um outro post. Até lá!

Escrito por Renan Drabach.

Gestão de Projetos Mobile

Para atingir bons resultados em projetos de software cada vez mais há interesse em entender sobre as práticas de gestão e a sua aplicação. Neste post irei apresentar algumas áreas dentro da gestão juntamente com o know-how que adquiri com os projetos de mobilidade que participei e que venho participando, apresentando os pontos que devem ser observados e monitorados durante o decorrer do projeto.

O primeiro passo é identificar as partes interessadas, que essencialmente são as pessoas envolvidas no projeto e que serão influenciadas tanto durante como ao término dele. Pensando em um projeto de mobilidade, os usuários finais serão os mais interessados, pois conforme a forma de trabalho pode haver uma infinidade de dispositivos, modelos, hardware, sistemas operacionais, etc. Em uma empresa que trabalha com Bring Your Own Device (BYOD), isso pode ser uma peça chave para identificar as plataformas de desenvolvimento. Quando há esta possibilidade, até mesmo os patrocinadores do projeto podem decidir qual plataforma e dispositivos serão utilizados e igualmente implicará diretamente no Escopo, Custo, Tempo, Recursos Humanos, Aquisições e Riscos (especificamente, caso seja uma plataforma nova).

Um dos principais guias de boas práticas para um Gerente de Projetos (GP) é o PMBok, onde são apresentadas práticas de gestão de projetos segundo a Project Management Institute (PMI). Um Gerente de Projeto, seguindo essas boas práticas, deve ser capaz de gerir qualquer projeto, tanto de TI, Produção, Construção entre outros. Porém sabe-se que cada área possui suas particularidade e que quanto mais conhecimento você possuir mais fácil será tomar decisões e liderar uma equipe.

Gerenciamento de Escopo

Com as partes interessadas ouvidas e analisadas, é possível ver que temos um escopo com mais restrições e premissas do que em um sistema Web ou Desktop. O escopo de um projeto de mobilidade deve definir quais dispositivos, plataformas e modelos serão abordadas, para que ao final do projeto não haja problemas de divergências e reclamações, como por exemplo para objetivo X funciona e para Y não. No mundo da mobilidade só a alteração do modelo de um dispositivo pode representar o funcionamento ou não de uma aplicação.

Outra questão importante é em relação os Casos de Uso/Funcionalidades que serão abordados. Nunca prometa uma funcionalidade que você não tenha certeza que sua equipe técnica tenha conhecimento para garantir sua execução. Atualmente possuímos dispositivos que permitem alterações mais “hardcore” com maiores possibilidades, porém ainda há os que detém de maior segurança impedindo muitas ações ou utilizações do próprio hardware. Como sugestão sempre faça uma prova de conceito antes de dar qualquer garantia.

Também não devemos esquecer do operacional do sistema. Defina no escopo se o sistema irá trabalhar de forma online ou offline, se irá utilizar comunicação de dados ou SMS, qual será a operadora, qual será a cobertura, etc. Como todo sistema de mobilidade depende de uma infra-estrutura que não temos controle, não garanta 100% de operacionalidade e procure deixar bem detalhado se você irá permitir a comunicação entre o seu sistema mobile: Esse pode ser um dos maiores riscos do projeto.

Na EAP, caso você trabalhe com diversas plataformas, não esqueça de quebrar o desenvolvimento de cada caso de uso para cada plataforma.

Gerenciamento de Custos

Dentro dos custos do projeto, deve ser levado em consideração se haverá necessidade de inscrição nas plataformas de dispositivos a serem desenvolvidos. Por exemplo, para desenvolvimento do iOS é necessário o pagamento de uma taxa anual, que limita a uma quantidade X de dispositivos para teste e publicação da app no Apple Store. Outro custo que pode ser envolvido é na aquisição de dispositivos tanto para testes como para desenvolvimento. Conforme a(s) plataforma(s) escolhida(s), esse custo pode ser elevado.

O custo com a mão de obra também pode ser um dificultador: Diferentemente de um profissional que desenvolva unicamente em .Net, Java, PHP, tanto Web como Desktop, você vai precisar de profissionais com perfis entre pleno e sênior que tenha conhecimento nas plataformas para que possam usufruir ao máximo os recursos que cada plataforma pode oferecer. Não há necessidade que todos sejam plenos ou seniors, mas é importante que tenha ao menos um líder técnico para cada plataforma e possam dar suporte tanto ao desenvolvimento como para a arquitetura do sistema. Normalmente por essa mão de obra ser mais especializada ela acaba sendo mais cara, mas com certeza será de grande ajuda no projeto.

Gerenciamento de Recursos Humanos

Conforme recém mencionado, é importante que sua equipe tenha conhecimento em sistemas de mobilidade, pois participará da definição de funcionalidades e daquilo que poderá ser feito. Em contraste aos sistemas Web e Desktop, a mobilidade permite que as funcionalidades sejam desenvolvidas em infinitas formas de integração com os dispositivos e servidores pela internet. Porém para poder fazer tudo que é solicitado, é necessário verificar os talentos que há dentro de cada equipe e explorar suas capacidades.

Gerenciamento do Tempo

Quanto maior o conhecimento nas plataformas e tecnologias, menor será o tempo de análise, projeto e desenvolvimento.

Em geral é assim para qualquer área, mas dentro do desenvolvimento de software isso é um grande diferencial entre um desenvolvedor Java e um Desenvolvedor Android, pois não estamos falando só da linguagem mas sim de hardware, ubiquidade, User Experience, conhecimento das interfaces… e o perfil do profissional vai de acordo com o aproveitamento do tempo estimado.


Gerenciamento de Riscos

Todo e qualquer projeto possui seus riscos, mas é importante salientar que quanto mais conhecimento a equipe e seu gestor tiver, mais fácil de identificar os riscos, até mesmo para quantificá-los e qualificá-los a fim de prever futuros problemas. Atualmente temos cada vez mais dispositivos, tecnologias, oportunidades de integração e qualquer detalhe dentro desses itens oferece um risco determinado que deve ser previsto. Conhecimento e análises devem ser constantes.

Conclusão

Em resumo, se atente ao que você irá prometer no escopo, pois existem inúmeras variáveis a mais que em um projeto apenas Web ou Desktop. Busque ter na equipe ao menos um membro especializado nas plataformas e/ou requisitos de tecnologias, pois eles serão muito úteis para o desenvolvimento e suporte na gestão do mesmo.

Caso tenha algum comentário ou mais sugestões para gestão de projetos mobile, deixe seu comentário!

Escrito por Giovani Barili.

Mobiltec é presença em evento DEVRS.NET – TDD e IOC e palestra “Plataforma .NET Vale a pena?”

TDD

No dia 26 de março de 2013 aconteceu em Porto Alegre o EVENTO DO GRUPO DE USUÁRIOS .NET do RS. Os assuntos em pauta foram IoC (Inversion of Control), DI (Dependency Injection) e TDD (Test Driven Development).

Participando com bastante frequência de eventos desse caráter, não pudemos deixar de reparar que nessa oportunidade os palestrantes foram além dos conceitos e implementação de código. Idéias de como introduzir a filosofia de TDD dentro da empresa foi um ponto marcante e sem dúvida um diferencial.

Diogo Lucas palestrou sobre injeção de dependência. Bernardo Bosak de Rezende e Guilherme Elias palestraram sobre a diferença entre testes unitários e TDD.

Conceitos de IoC:
Inversão de controle em projetos orientados a objeto é um padrão muito usado. Aplica-se conceitos como polimorfismo, interface e herança, buscando o reaproveitamento de código, redução do acoplamento e redução de testes no projeto.

Supomos que uma classe A necessite utilizar uma classe B. Deve-se retirar da classe A a responsabilidade de criar a nova instância da classe B.
Para isso, utilizamos a Injeção de Dependência (DI). A classe A recebe um objeto já instanciado da classe B, através de:

  • Injeção via Construtor
  • Injeção via Propriedades (get/set)
  • Injeção via Interface
  • Injeção usando um framework (Spring/Unity)

O conceito de IoC é bem mais abrangente do que isso e estamos apenas citando um exemplo simples. É importante ressaltar que o impacto que essa forma de implementação realiza em aplicações com várias camadas pode desencadear uma série de benefícios a longo prazo para o projeto.
Como exemplo disso, reduzir o acoplamento e aumentar a coesão das classes auxilia na implementação das camadas de sua aplicação. Reduz os ajustes necessários nas classes já criadas para o funcionamento com a nova camada.
A implementação de testes em aplicações com IoC se torna mais objetiva e confiável.

Durante as palestras foram apresentados comparativos entre ferramentas para realizar a injeção de dependência mas deixaremos para fazer a análise delas em um post exclusivo sobre IoC e DI, em breve.

Um pouco sobre TDD:
Significa Desenvolvimento Orientado a Testes e trata-se de uma alternativa ao desenvolvimento tradicional. A partir dela, é realizada a implementação de casos de teste para daí sim implementar seu código.
Entre as diversas vantagens na utilização de TDD, podemos destacar a maior confiabilidade no código implementado.

O TDD consolida-se como uma prática cada vez mais importante dentro de empresas de T.I.

Alguns profissionais da área apontam o TDD como algo negativo pela quantidade de código gerado que é muito maior, além de maior tempo de implementação. Entretanto, a confiabilidade, a redução do tempo de testes manuais e a redução da necessidade de correção de códigos acabam sendo um diferencial que compensa essa prática.

Falaremos mais sobre TDD em um post exclusivamente direcionado para o assunto, colocando em prática alguns trechos de código, ferramentas e desafios encontrados.

As palestras ocorridas serviram, principalmente, para abrir os olhos de quem ainda não utiliza IoC e TDD de que é uma tendência forte e, para alguns projetos, vital!

No final do evento, todos os participantes puderam tomar um café e conversar com os organizadores e palestrantes, fechando a noite com algumas discussões muito legais sobre as tecnologias .NET!

Guilherme Elias, Luiz Roberto Lethang Rodolpho e Bernardo Bosak de Rezende durante o encerramento do evento.

Outro evento que rolou durante a semana, no dia 27 de março:
Plataforma .NET Vale a pena?

Ahmed Alejo e Roger Silva participaram na mesma semana de uma palestra sobre a relevância da plataforma .NET, na faculdade Decision.

O palestrante Ubirajara Mendes Nunes Junior, com perfil de evangelista de produtos da Microsoft, destacou as facilidades da escolha do Framework .NET para iniciar um projeto (de TI) de uma startup. A mais importante delas, sem dúvida para quem está empreendendo é que o custo inicial para desenvolver um software no plataforma .NET é zero, pelo menos no que diz respeito a utilização das ferramentas necessárias para desenvolver nesta plataforma – que em geral necessitam de licenças caras, tutoriais, treinamentos etc. O programa da Microsoft que proporciona estas facilidades às startups se chama BizSpark, onde a Microsoft oferece todo suporte necessário ao empreendedor durante dois anos (sem nenhum custo). Depois deste período, é necessário pagar as licenças necessárias para continuar a usufruir dos benefícios da plataforma. Consideramos este tempo ideal, pois é possível mensurar o crescimento e a oportunidade do negócio dele, tendo o conhecimento e a resposta se ainda vale investir ou não.

O palestrante também explicou que a plataforma Microsoft.NET é um estratégia de negócio da Microsoft que visa uma abrangência comum de todas as áreas mais conhecidos de TI, que inclui:

  • Armazenamento de dados(Cloud Storage) → Microsoft Azure
  • Ambiente Mobile → Microsoft Windows Phone 7
  • Ambiente Destop → Microsoft Windows 8
  • Ambiente Web → Microsoft ASP.NET(Web Forms, MVC, Web Pages, Web Matrix)
  • Aplicações/Arquiteturas Orientado a Serviços(SOA) → Windows Communication Foundation (WCF)
  • Desenvolvimento Rápido de Aplicação → Microsoft ASP.NET Web Matrix
  • Mapeamento objeto-relacional (ORM): ADO.NET Entity Framework(EF)
  • Jogos → Microsoft XNA

Em ambos os eventos destacaram a viabilidade e o potencial da plataforma, incentivando a todos aprofundar nos assuntos de maior interesse. Além de serem gratuitos, realizaram sorteios e distribuíram descontos em cursos na área. Durante a mesma semana, duas grandes oportunidades de aprendizado num curto espaço de tempo com importantes profissionais do mercado! A Mobiltec fica feliz em participar destes eventos e trazer o conhecimento para dentro de casa. Parabéns à estas iniciativas!

Esperamos que você tenha aproveitado bastante do nosso review e fique atento aos próximos posts!

Escrito por Luiz Roberto Lethang Rodolpho, Ahmed Alejo e Roger Silva.