segunda-feira, 15 de novembro de 2010

Design centrado no usuário

Atualmente o design tem recebido uma atenção especial, visto que, além de ser o conjunto que é percebido pelo usuário, ele também pode significar redução de custos de treinamentos e manuais.

Pesquisando na internet sobre o assunto encontramos uma video aula de um curso de iPhone da Stanford sobre "How to Build an iPhone App that Doesn’t Suck! (In 10 Easy Steps)" do Steve Marmon (@marmon). Ele menciona 10 passos para se desenvolver aplicativos que não sejam “um lixo”. Porém, estes passos podem ser aplicados para quase qualquer projeto. Os passos comentados, estão a seguir:


1. Decida o que construir (Decide what to build)

Muitas vezes pensamos em uma solução levando em conta apenas necessidades secundárias, ou seja, aquelas que nos guiam a atender desejos e não o real problema que estamos nos propondo a resolver. Dessa forma, corremos o risco de não solucionar nenhum problema, ou ainda, de desenvolvermos uma solução ineficaz.

Para não cair nessa armadilha, procure enteder qual problema estamos nos propondo a resolver, pra quem estamos projetando e os tenhamos em mente ao desenvolver a solução. Focar em um grupo específico aumenta a chance de sucesso, pois o feedback desses usuários será de útil para melhorarmos o produto e conquistarmos os clientes.

Se o seu software for um produto interno, envolva os usuários finais (ou pessoas chave que representem e entendam as necessidades desses usuários), agora, caso seja um produto para o mercado, ou seja, você não tem como envolver o usuário, procure fazer pesquisas (faixa etária, classe social, nível de escolaridade, etc) para identificar bem as necessidades e guiar o desenvolvimento da interface do produto.

Conheça as personas.


2. Visite a app store (Visit the app store)

No mundo globalizado um produto inovador pode ser a composição de idéias brilhantes de produtos similares ou não, portanto, observe como aplicativos similares resolveram problemas que você está se propondo a resolver.

Eles já devem ter tido feedback de usuários e alterado o produto em função disso, e você, estará "recebendo" estes feedbacks gratuitamente.

Se existirem concorrentes, faça um benckmarking e compare as vantes e desvantagens em relação ao seu produto.


3. Explore as possibilidades de soluções (Explore possible solutions)

A primeira solução não é a única e tampouco a melhor!

Trabalhe com as limitações e aproveite os padrões para conseguir, com mais algumas tentativas, encontrar a solução ideal.


4. Rabisque (Sketch)

Para ter uma boa idéia, tenha várias, procure designs alternativos para o mesmo problema, faça várias propostas de solução!

Com 10 propostas, por exemplo, sua chance de ter algo realmente bom é muito maior. É assim que a Apple funciona. É assim que a IDEO funciona.

Rabisque e comunique suas idéias.


5. Construa um protótipo de papel (Build a paper prototype)

Um vício de longa data da área de desenvolvimento de softare e sair para a programação ou o design sem antes criar um protótipo, e quando ele é feito, a única preocupação é com relação aos campos que tem que ter.

Hoje há mais itens a ser levado em consideração, e aproveite que nesta fase, o custo de alteração é muito baixo. Pense, quanto tempo você gasta para alterar uma página no protótipo? E para reprogramar uma tela? Apresente o protótipo para pessoas chaves, analise suas reações, colha feedbacks e veja se o caminho que você está tomando é o correto.

"Fail early to succeed sooner".

6. Abra o omnigraffle (Fire up omnigraffle)

Aqui ele fala do omnigraffe para o caso específico do iPhone, porém, a mensagem é para que use uma ferramenta que permita criar um protótipo (Sketchflow?). Este protótipo será esteticamente parecido com a versão final, mas sem precisar gastar tempo programando.

A idéia é a mesma da etapa anterior, permitir alterações sem elevar o custo.

7. Faça tudo denovo (Do it all again)

Já ouviu a frase "A melhor época de começar uma faculdade é quando se termina"? Isso quer dizer que, quando terminamos os estudos é que descobrimos o que precisavamos aprender.

Sendo assim, como todo design centrado no usuário, a parte mais importante do processo é iterar. Veja onde está com problemas, volte, questione se a solução dada é a melhor para aquele caso e pense em várias alternativas de correção para o problema encontrado.

Prototipe antes de alterar, teste e siga assim até que o produto esteja estável.

8. Agora você pode programar (Okay, you can code finally)

Agora pode partir para a programação do seu produto.

Use padrões de design e programação como MVC, que possibilitem alterações futuras sem muito trabalho, desvinculando o código da parte visual/apresentação.

9. Abra seu aplicativo para beta testers (Beta test your app)

Utilize os testes para levantar os bugs que passaram desapercebidos pelos testes em protótipos e pelos programadores, como bugs relativos à satisfação dos usuários.

Um exemplo, no iPhone, pode ser a rolagem da tela estar muito "lerda". Os programadores vão achar que a tela está excelente, porque está rolando, porém os usuários, após algum tempo de uso, se sentirão incomodados com isso.

O Tweetie foi o primeiro app de twitter a conseguir uma rolagem rápida, e teve grande destaque por conta disso.

10. Lance (Release)

Se você fez tudo direito, este será um momento feliz e de boas noites de sono, senão, prepare-se para os problemas que virão (e-mails de bugs, reclamações, comentários negativos sobre o aplicativo, etc.).

Bem vindo ao mundo moderno do design!

Compartilhe suas idéias conosco e até a próxima!

 

terça-feira, 9 de novembro de 2010

Reflection de Alta de Performance

Pesquisando no Google vemos alguns bons artigos explicando que Reflection é lento. Está certo, alguns métodos do Reflection, como GetXXX (do MethodInfo, PropertyInfo, FieldInfo, etc.), são cerca de 100 vezes mais lento que acessar diretamente uma propriedade ou executar um método.

O quanto o Reflection é lento?

Vamos criar um pequeno exemplo para analisarmos a performance do Reflection. Esse exemplo terá uma classe simples que contém apenas 1 método:

namespace CustomNamespace
    public interface ICustom
    {
        void CallMe(int x);
    }

    public class CustomClass : ICustom
    {
        public void CallMe(int x)
        {
            x = x + 10;
            return;
        }
    }
}

Simples, não? Bom, vamos criar uma classe para consumir e checar a performance em situações de extress. Vamos criar objetos do tipo CustomClass em um loop e executar o método CallMe com valores randomicos de duas formas: 1. Sem Reflection, 2. Com Reflection

Para o teste com Reflection, vamos criar uma classe (ReflectionCalculator) com alguns métodos:

public partial class ReflectionCalculator
{
private Type dummyType;
public Type DummyType
{
get
{
dummyType = dummyType ??
CurrentAssembly.GetType("CustomNamespace.CustomClass",
true,
true);
                return dummyType;
}
}

private MethodInfo method;
public MethodInfo Method
{
get
{
this.method = this.method ??
this.DummyType.GetMethod("CallMe", BindingFlags.Instance |
BindingFlags.Public, null, new Type[] { typeof(int) },
null);
return this.method;
}
}

private object dummyObject;
public object DummyObject
{
get
{
if (this.dummyObject == null)
{
dummyObject = Activator.CreateInstance(this.DummyType);
}
return dummyObject;
}
}
       
public void ReflectionBasedCall(int value)
{
this.Method.Invoke(this.DummyObject, new object[] { value });
}

public void NormalCall(int value)
{
this.MyClass = this.MyClass ?? new CustomClass();
this.MyClass.CallMe(20);
        }
}

Então, se olhar para as duas chamadas ReflectionBasedCall e NormalCall verá que ambos fazem a mesma coisa. A abordagem de reflexão deve primeiro obter um Type usando Assembly.GetType (que evetualmente vasculha toda a hierarquia do objeto inteiro) e depois o GetMethod no tipo encontrado. Portanto quanto maior o tamanho do Assembly mais lento será. Por fim, usamos o Activator.CreateInstance para criar a instância do objeto.

Agora, e se executassemos os métodos 1.000.000 de vezes usando esse código e imprimissemos o tempo em segundos, ele irá se parecer com:

static void Main(string[] args)
{
var watcher = new System.Diagnostics.Stopwatch();
var calculator = new ReflectionCalculator();

watcher.Start();
for (int i = 0; i < 1000000; i++)
{
calculator.NormalCall(i);
}
watcher.Stop();

Console.WriteLine("Time Elapsed for Normal call : {0}",
watcher.Elapsed.TotalSeconds);
watcher.Reset();

watcher.Start();
for (int i = 0; i < 1000000; i++)
{
calculator.ReflectionBasedCall(i);
}
watcher.Stop();
Console.WriteLine("Time Elapsed for Reflection call : {0}",
watcher.Elapsed.TotalSeconds);

Console.ReadLine();
}

Abaixo, a saída da execução:

 

Reflection + Lambda Expression = Dynamic Delegates


Na versão 3.0 do Microsoft .Net Framework, juntamente com o Linq, foi introduzido o Lambda Expression, que é uma função anonima que pode conter expressões e funções e pode ser usada para criar Delegates ou tipos de árvores de expressão.

Criar delegates em runtime é atualmente a melhor maneira de fazer o código Reflection ser executado muito rápido. A idéia básica é criar delegates de cada MethodInfo que precisamos executar e eventualmente fazer cache dele em memória.

Para isso, precisamos construir um Action<> (no caso de método VOID) ou Func<> (no caso de método que retorne algo) para cada método:

public static Action<object, T> CallMethod(this MethodInfo methodInfo)
{
if (!methodInfo.IsPublic) return null;

var returnParameter = Expression.Parameter(typeof(object), "method");
var valueArgument = Expression.Parameter(typeof(T), "argument");

var setterCall = Expression.Call(Expression.ConvertChecked(returnParameter,
methodInfo.DeclaringType),
                             methodInfo,
                             Expression.Convert(valueArgument, typeof(T)));
return Expression.Lambda<Action<object, T>>(setterCall, returnParameter,
valueArgument).Compile();
}

A expressão lambda acima cria um delegate do tipo Action onde “object” representa a instância da classe no qual executaremos o método e “T” representa o tipo do argumento do método. O código acima cria uma expressão: 

(x, y) = return x.[method](y)


Com isso, podemos guardar na memória a referência construída dinamicamente:


private MethodInfo methodInfo;
public MethodInfo CachedMethodInfo
{
get
{
this.methodInfo = this.methodInfo ?? this.GetMethodInfo();
return this.methodInfo;
}
}

private MethodInfo GetMethodInfo()
{
Type myClass = this.CurrentAssembly.GetType("CustomNamespace.CustomClass",
true,
true);
return myClass.GetMethod("CallMe", BindingFlags.Instance |
BindingFlags.Public, null, new Type[] { typeof(int) }, null);
}

private Action<object, int> _methodcallDelegate = null;
public Action<object, int> MethodCallDelegate
{
get
{
this._methodcallDelegate = this._methodcallDelegate ??
this.CachedMethodInfo.CallMethod<int>();

return this._methodcallDelegate;
}
}

Vamos executar o código de teste mais uma vez para ver o quão eficiente é essa técnica:


Veja que a diferença em relação a uma abordagem hard-coded caiu drasticamente expandindo as possibilidades de criação de algoritmos genéricos e de alta performance.

Você tem dicas e truques? Compartilhe! Envie para social@apolineo.com.br que colocaremos no blog.

Fique a vontade também de enviar suas sugestões e críticas.

Download: Código Fonte

Até a próxima!

domingo, 3 de outubro de 2010

Boas práticas para gerência de requisitos, de acordo com os modelos MPS.BR e CMMI

A atividade de gerência de requisitos tem por objetivo mapear os requisitos identificados na fase inicial de um projeto e rastreá-los até os artefatos finais, permitindo a validação da consistência entre os mesmos e as requisições originalmente vindas dos solicitantes, conforme definido pelo modelo CMMI [SEI, 2006].

No entanto, [SOMMERVILLE, 2007] ressalta o fato de que os requisitos de sistema mudam constantemente, em virtude do amadurecimento na compreensão das pessoas envolvidas acerca do que desejam que o software faça ou ainda de modificações no hardware, software ou ambiente organizacional do sistema.

Deste modo, um controle efetivo dos requisitos vindos do usuário e de suas mudanças, assume papel maior que simplesmente atender a especificação de uma área de processo e sim de garantir a adequação do produto final ao seu meio externo.

A gerência de requisitos representa assim, a figura de ligação entre as áreas de planejamento de projeto, solução técnica e gerência de configuração. Um reflexo disso é o fato de que este é um dos processos que não aceitam qualquer exclusão no modelo MPS.BR [SOFTEX, 2008].

Sendo fundamental a ambos os modelos de referência, esta área de processo recebe enfoques semelhantes em cada um deles. Porém ambos apresentam apenas listas de necessidades que devem ser atendidas e não uma proposta de artefatos para sua realização.

Deste modo, empresas que tenham interesse em melhorar seus processos precisam buscar referências externas a respeito de como proceder, gerando margem para implementações custosas e pouco eficientes daquilo que deveria ser uma otimização de processos.

Este artigo tem por objetivo apresentar uma contextualização entre o processo de gerência de requisitos e as necessidades impostas pelos modelos CMMI e MPS.BR, definindo um conjunto mínimo de artefatos para cumprimento das mesmas, que possam ser utilizados por empresas de pequeno ou médio porte.

São descartadas neste artigo as necessidades referentes à área de processo de definição de requisitos – formalizada em [SEI, 2006] - uma vez que a mesma não é o
foco deste estudo.

Modelos de Referência


Dentre os diversos modelos propostos ao redor do mundo, foram selecionados os modelos de referência CMMI e MPS.BR, sendo o primeiro selecionado por sua aceitação mundial e representatividade de mercado, enquanto o segundo representa um movimento que propõe uma aproximação ao cenário das empresas brasileiras.

O modelo de referência para melhoria de software brasileiro (MR-MPS) indica ainda uma equivalência [SOFTEX, 2008] entre seus processos e as áreas de processo definidas no modelo CMMI, conforme ilustra a abaixo.



CMMI e a Gerência de Requisitos

Dentro do modelo CMMI, a gerência de requisitos é uma área de processo componente do Nível 2 de Maturidade, conhecido como “Gerenciado”. Neste nível, os processos da empresa devem ser executados dentro de um padrão e por pessoas habilitadas, de acordo com controles definidos [SEI, 2006].

A gerência de requisitos tem aqui o papel de garantir que todos os artefatos produzidos sejam coesos com a necessidade do cliente, por meio da identificação dos requisitos que originaram cada implementação. É vista também como ponto de partida para o planejamento de atividades e entregas, uma vez que a área de gerência de configuração pode definir seus pacotes em virtude de agrupamentos de requisitos.

Outro aspecto relevante é a possibilidade de rastrear o impacto na solução de falhas no software ou nas solicitações de mudanças vindas dos usuários.

MPS.BR e a Gerência de Requisitos

Diferentemente do CMMI, dentro do modelo MPS.BR, o processo de gerência de requisitos é visto como parte do nível mais baixo de maturidade possível, o Nível G, conhecido como “Parcialmente Gerenciado” [SOFTEX, 2007].

Este enfoque coloca um maior destaque nas tarefas que deste processo, colocando-o num papel de ligação entre artefatos e requisitos, objetivando que a rastreabilidade com relação às necessidades originalmente mapeadas seja mantida. Trata-se então de uma diferença sutil, que demanda atenção extra às tarefas que o
compõe.

Com relação à avaliação de impacto em solicitações de mudanças ou correções de falhas, a definição é similar ao proposto pelo CMMI.

A Questão da Rastreabilidade


Uma das exigências comuns ao nível G do MPS.BR e ao nível 2 do CMMI é a rastreabilidade entre um requisito e todos os artefatos envolvidos na implementação
decorrente dele, conforme apresentado em [SEI, 2006] e [SOFTEX, 2007].

Esta prática permite uma maior efetividade na gerência de configuração, que visa justamente garantir que cada versão do software permita isolar uma versão compatível dos componentes envolvidos em sua realização, chamada baseline. Realizar
esta tarefa sem uma referência sólida a quais telas ou componentes são afetados por
determinado requisito, dificultaria a tarefa de isolar a versão, inviabilizando a geração de baselines.

No entanto, este rastreio se complica quando consideramos que um requisito pode ser atendido por uma tela inteira ou por uma simples caixa de texto. [MEDEIROS, 2006]

Levando em conta que um documento de caso de uso pode ser utilizado para refletir uma tela ou um único método, desde que este seja suficientemente relevante, tem-se uma opção se solução: mapear casos de uso versus requisitos funcionais.

Por meio desta prática é possível identificar no detalhe os artefatos que serão afetados cada vez que uma mudança é realizada em um caso de uso específico, bem como prover uma análise de impacto mais eficaz antes de planejar uma tarefa.

A elaboração de uma planilha descrevendo esta relação, em conjunto à existência de um documento padronizado para a elicitação de requisitos, onde seja possível não apenas mapear as necessidades, mas também a interdependência entre requisitos (sejam
eles funcionais ou não) permite atender a todas as necessidades especificadas pelos modelos CMMI e MPS.BR, conforme descrito em seus documentos de referência.

Artefatos para a Gerência de Requisitos


Levando em conta os aspectos discutidos anteriormente, propõe-se que a gerência de requisitos seja atendida por meio da adoção de artefatos que reflitam tarefas consideradas padrão de mercado, no que diz às atividades realizadas pelos envolvidos
em sua realização.

Os documentos propostos são: Documento de Visão, Matriz de Rastreabilidade e Documento de Especificação de Caso de Uso, compatíveis com o Rational Unified Process [RATIONAL, 2009], processo de engenharia de software que se baseia na definição de disciplinas para atribuição de tarefas e responsabilidades no desenvolvimento do software.

Visando atender os modelos de referência observados, é necessário que os artefatos possuam sessões específicas para mapeamento das relações de dependência com os diversos requisitos elicitados, conforme definido. Cada um destes artefatos acompanha todo o ciclo de vida do desenvolvimento do software, permeando suas diferentes fases. A seguir, cada um deles será observado em mais detalhes, bem como a relação entre artefatos, usos e fases do ciclo de vida.

Por meio deles, as cinco práticas específicas exigidas pelo CMMI são totalmente atendidas, bem como os cinco resultados esperados propostos melo MPS.BR.

Documento de Visão


Um documento relacionando as necessidades do usuário, seu entendimento por parte do analista de requisitos e o conjunto de requisitos (funcionais ou não) propostos para atender cada uma das necessidades mapeadas. Este documento é validado pelos usuários e pode ser utilizado para definição do escopo inicial do projeto.

Fases do Ciclo de Desenvolvimento em que é empregado e atividades realizadas:
  • Pré-Venda: Análise do Problema, Definição de Requisitos de Negócio e Requisitos de Usuário, Definição de Requisitos Funcionais e Requisitos Não Funcionais;
  • Análise e Design: Relação entre: Requisitos de Usuário e Requisitos de Negócio, Requisitos de Negócio e Requisitos Não Funcionais, Requisitos de Usuário e Requisitos Funcionais.

Práticas Específicas do CMMI atendidas [SEI, 2006]:
  • SP 1.1. Obter um entendimento dos requisitos;
  • SP 1.2. Obter aprovação dos requisitos.

Resultados Esperados do MPS.BR atendidos [SOFTEX, 2007]:
  • GRE 1. O entendimento dos requisitos é obtido junto aos fornecedores de requisitos;
  • GRE 2. Os requisitos de software são aprovados utilizando critérios
    objetivos.

Matriz de Rastreabilidade


Documento responsável por estabelecer uma relação bidirecional entre os requisitos originais e os componentes do produto final (software). Caso um requisito não possua nenhum caso de uso equivalente mapeado na planilha, uma inconsistência de projeto é efetivamente localizada.

Fases do Ciclo de Desenvolvimento em que é empregado e atividades realizadas:
  • Análise e Design: Relação entre Casos de Uso e Requisitos Funcionais;
  • Validação e Homologação: Possibilita o Rastreio e comparação entre uma implementação e a necessidade que a originou.

Práticas Específicas do CMMI atendidas [SEI, 2006]:
  • SP 1.3. Gerenciar as mudanças de requisitos;
  • SP 1.4. Manter rastreabilidade bidirecional entre os requisitos;
  • SP 1.5. Identificar inconsistências entre projeto e requisitos.

Resultados Esperados do MPS.BR atendidos [SOFTEX, 2007]:
  • GRE 3. A rastreabilidade bidirecional entre os requisitos e os produtos de trabalho é estabelecida e mantida;
  • GRE 4. Revisões em planos e produtos de trabalho do projeto são realizadas visando identificar e corrigir inconsistências em relação aos requisitos;
  • GRE 5. Mudanças nos requisitos são gerenciadas ao longo do projeto.

Documento de Especificação de Caso de Uso


Além do tradicional Diagrama de Casos de Uso, cabe elaborar um documento detalhando cada caso de uso, de acordo com sua especificidade [MEDEIROS, 2006]. Neste documento é realizada a especificação da implementação que será realizada pelos desenvolvedores, já com o foco nos artefatos que serão produzidos.

Embora esteja muito mais relacionado à área de processo de definição de requisitos do que à gerência propriamente dita, sua realização é essencial à construção da Matriz de Rastreabilidade, que vincula os casos de uso a seus requisitos.

Fases do Ciclo de Desenvolvimento em que é empregado e atividades realizadas:
  • Análise de Design: Detalha a solução para uma necessidade traduzida em requisito funcional e permite a validação da solução proposta pelo Analista de Requisitos ou de Negócio.

Práticas Específicas do CMMI atendidas [SEI, 2006]:
  • SP 1.4. Manter rastreabilidade bidirecional entre os requisitos.

Resultados Esperados do MPS.BR atendidos [SOFTEX, 2007]:
  • GRE 3. A rastreabilidade bidirecional entre os requisitos e os produtos de trabalho é estabelecida e mantida.

Conclusão


Por meio da utilização de três artefatos de confecção relativamente simples – Documento de Visão, Matriz de Rastreabilidade e Documento de Especificação de Caso de Uso - é possível atender a todas as exigências estabelecidas à área de processo de Gerência de Requisitos, de acordo com os modelos de referência analisados.

Porém, o nível de interdependência entre os documentos evidencia a necessidade de um processo eficiente para a definição dos requisitos, uma vez que uma falha nesta atividade resultará em um conjunto inconsistente de requisitos no documento de visão, refletidos em casos de uso que não traduzem a real expectativa do cliente.

Estes casos de uso e requisitos serão relacionados na matriz de rastreabilidade e futuramente, quando forem recebidas solicitações de mudanças em determinados pontos do sistema, a tarefa de localizar o ponto de origem do problema e os demais casos de uso relacionados a este mesmo requisito terá sua complexidade incrementada.

Outra questão que se torna evidente é a margem de falha humana, pois ao discutir a elaboração dos referidos artefatos sem a proposta de um pacote especializado de ferramentas surge a possibilidade de que determinado campo não seja preenchido, mesmo que de forma não intencional. Deste modo, cabe a cada empresa avaliar a relação entre custo e benefício da adoção de meios automatizados para a construção e relacionamento dos documentos.

É interessante ressaltar também que o modelo MPS.BR prega compatibilidade com o CMMI e, no que diz respeito à gerência de requisitos, seus resultados esperados correspondem diretamente às práticas específicas impostas pelo modelo norteamericano, de modo que os artefatos propostos poderiam ser elaborados considerando apenas o CMMI como critério de satisfação.

No anexo você encontra modelos de documentos ilustrando os conceitos abordados: Gerencia_Requisitos_slides.pdf

Referências Bibliográficas
[SEI, 2006] CMMI Product Team. CMMI for Developmment, Version 1.2. Pittsburg, EUA: Carnegie Mellon University – Software Egineering Institute, 2006.

[SOMMERVILLE, 2007] Sommerville, Ian. Engenharia de Software, 8ª Edição. São Paulo: Pearson Addison Wesley, 2007.

[SOFTEX, 2008] FAQ Implementação MR-MPS - Dúvidas sobre o Guia Geral e Guia de Implementação. http://www.softex.br/mpsBr/_faq/faqImplementacao.asp.
Recuperado em 01/12/2008.

[SOFTEX, 2007] Associação para Promoção da Excelência do Software Brasileiro - SOFTEX. MPS.BR – Guia Geral, Versão 1.2, junho 2007.

[MEDEIROS, 2006] Medeiros, Ernani Sales de. Desenvolvimento de Software com
UML 2.0: definitivo. São Paulo: Pearson Makron Books, 2006.

[RATIONAL, 2009] Technical resources and best practices for the Rational® software
platform. http://www.ibm.com/developerworks/rational. Recuperado em 03/02/2009.

Autor: Ângelo Amaral
Sobre: Pós-graduado em Engenharia de Software pelo ITA com especialização em modelos de construção de aplicações comerciais utilizando motores de regras de negócio, possui experiência na implantação de scrum e coordenação de projetos e sistemas de varejo.
Contato: angelo.amaral@gmail.com

quarta-feira, 8 de setembro de 2010

Dynamic CRUD no .NET 4.0

No último artigo fizemos um overview sobre programação dinâmica e as possibilidades que ela oferece, e terminamos, com uma idéia sobre um CRUD a partir do banco. Hoje veremos como isso poderia ser feito.

A idéia é usarmos o DLR (Dynamic Language Runtime) para que nosso código compilado ignore a estrutura estática dos tipos que podem ser detectados na compilação, ou seja, ter a verificação de tipos somente no momento da execução. Dessa forma, vamos até o momento de dados, obtemos o schema da tabela que desejamos e criamos a estrutura de um objeto correspondente a ela. Depois disso, montamos a rotina para inserção de registros na base (na tabela ao qual a entidade se refere) e criamos na entidade o método que executa esse rotina.

Para que tudo isso seja possível precisamos conhecer o intermediario de toda essa “mágica”, portanto, lhes apresento o “ExpandoObject”.

A classe ExpandoObject pertence ao namespace System.Dynamic e é definida no assembly System.Core e representa um objeto cujos membros podem ser dinamicamente adicionados e removidos em tempo de execução.

public sealed class ExpandoObject :
    IDynamicMetaObjectProvider,
    IDictionary<string, object>,
    ICollection<KeyValuePair<string, object>,
    IEnumerable<KeyValuePair<string, object>>,
    IEnumerable,
    INotifyPropertyChanged


Na verdade, é a interface “IDynamicMetaObjectProvider” quem permite que um objeto seja compartilhado no DLR (Dynamic Language Runtime) por programas escritos de acordo com o modelo de interoperabilidade DLR, ou seja, apenas os objetos que implementam essa interface podem ser compartilhados em linguagens dinâmicas do .NET.

Dynamic CRUD


A idéia aqui é criarmos um objeto contendo todas as propriedades da tabela e os métodos do CRUD (do Inglês Create, Read, Update e Delete), porém, por questões de simplicidade, nosso exemplo focará apenas na inserção de dados (Create).

A figura abaixo representa a estrutura que criaremos:



O grande astro dessa solução é classe “Entity”, que é a responsável por criar uma instância e adicionar as propriedades (de acordo com as colunas do banco) e o método de inclusão de dados.

O primeiro passo é criar uma instância de “ExpandoObject”, da mesma forma que qualquer outro objeto .NET, exceto pelo fato de que a variável para armazenar a instância é do tipo “dynamic”.

dynamic entity = new ExpandoObject();

Após buscarmos o schema da tabela na base de dados vamos adicionar as propriedades, e para isso, você apenas atribui a ela um novo valor, por exemplo:

entity.ExpandoEntityName = name;

Porém, não sabemos o nome das propriedades, afinal, os recuperamos a partir do schema da tabela, como fica?

Lembra que o “ExpandoObject” implementa a interface “IDictionary”? Então, é a parte “string” ou “key” é o nome da propriedade ou método, e a parte “object” ou “valor”, é valor da propriedade ou o delegate do método. Sendo assim, convertemos nosso “ExpandoObject” para “IDictionary” e através de um loop criamos todas as propriedades:

var e = (entity as IDictionary<string, object>);
foreach (var property in propertyList)
{
    e[property.Name] = "";
}


O mesmo serve para a criação do método de inserção:

entity.Insert = new Action(() => {
    SqlClientPlugin.Insert(tableName, entity, propertyList);
});

O uso dessa estrutura nos permitiria criar um código assim:

var company = Entity.NewEntity("Teste");

company.Name = "Apolineo Serviços de Informática Ltda.";
company.WebSite = "http://www.apolineo.com.br";

company.Insert();

Bom, isso é só uma possibilidade de uso de programação dinâmica e esperamos que sirva de estopim para outras grandes idéias de uso.

O código de exemplo pode ser obtido no link abaixo:
ExpandoObjetoTestSolution.rar

Até a próxima!

segunda-feira, 16 de agosto de 2010

Programação Dinâmica no C# 4.0

A programação com a plataforma Microsoft .NET Framework se baseia na programação com tipos de dados estáticos, ou seja, toda expressão é de um tipo conhecido no momento da compilação e possíveis erros são conhecidos com antecedência.

Ao usar reflexão você pode resolver dependências de tipo em tempo de execução e habilitar seu código para funcionar baseado em uma interface sem ter que saber o tipo concreto, e essa técnica, usada desde a versão 1.0 do .NET Framework, foi amplamente utilizada para criar estruturas IoC (Inverse of Control – Insersão de Controle). Porém, apesar da flexibilidade, a interface do objeto de destino é fixa e imutável, ou seja, sempre existe uma interface conhecida por trás de qualquer chamada realizada através da reflexão.

O .NET Framework 4.0 tráz novos recursos que lhe permitirão ir além dos tipos estáticos. A tipificação dinâmica atrasa qualquer verificação de tipo até o momento da execução, ou seja, ignora a estrutura estática dos tipos no momento da compilação.

Com objetos dynamic, é possível definir a interface do objeto de maneira programática e ter a clareza formal de objetos estáticos e a flexibilidade dos tipos dinâmicos.

Com essa técnica é possível implementar um CRUD a partir do schema do banco de dados, criar um ponto de acesso centralizado para atender solicitações de requisições de interfaces com o usuário e fazer o tratamento necessário (navegação, integrar com serviços de negócios, etc) através de uma string (ASP.NET MVC? AJAX?), estruturar em objetos um XML, enfim, as possibilidades são muitas e a criatividade é o limite.

No próximo artigo apresentarei falaremos de objetos Expando e mostrei a idéia do CRUD a partir do schema do banco.

Até mais!

quarta-feira, 14 de julho de 2010

Extension Methods = Manutenibilidade

Com a versão 3.0 do .Net Framework tivemos, dentre outros recursos, a introdução do Extension Method, que é uma técnica para adicionar comportamento (método) a um determinado tipo sem a necessidade de alterar o código original ou criar tipos derivados.

Uma boa estratégia de uso de métodos de extensão contribui para um a manutenibilidade, que é um aspecto da qualidade de software que se refere ao esforço necessário para diagnosticar deficiências ou causas de falhas, ou localizar as partes a serem modificadas para corrigir os problemas ou à facilidade de modificação ou adaptação, ao esforço necessário para realizar alterações ou para adequar o produto a eventuais mudanças de ambiente operacional.

Imagine um método, em uma classe de negócio, que realiza o a venda de um produto e da baixa no estoque:

if (IsValid(sale) && AllItemsAreValids(sale.SaleDetailsList))
{
    var products = VerifyProductStockAvailability(sale);

    using (var trans = new DataTransaction())
    {
        var daoSale = new DAOSale();
        var daoProduct = new DAOProduct();

        daoSale.Insert(sale);

        for (int i = 0; i < sale.SaleDetailsList.Count; i++)
        {
            daoSale.InsertDetail(sale.SaleDetailsList[i]);
        }

        for (int i = 0; i < products.Count; i++)
        {
            daoProduct.UpdateStock(products[i]);
        }

        trans.Complete();
    }
}

Refatorando o código, aplicando métodos de extensão e buscando uma melhor lêxia do código, podemos chegar em resultados como:

if (sale.IsValid() && sale.SaleDetailsList.HasAllItemsValid())
{
    var products = VerifyProductStockAvailability(sale);

    using (var trans = new DataTransaction())
    {
        sale.Insert();
        products.UpdateStock();
        sale.SaleDetailsList.Insert();
        trans.Complete();
    }
}

Muitos podem argumentar que a diferença é pouca, o código é legível e de boa manutenibilidade das duas formas, eu concordo, porém, na segunda ele é muito mais fácil e rápido para ler e entender, além de que, com o uso dos métodos de extensão introduzimos um nível a mais de abstração.

Um recurso simples, porém, poderoso!

Como você o utiliza em seus projetos? Deixe seu comentário.

Até a próxima!

segunda-feira, 12 de julho de 2010

Validação, Verificação e Testes de Software

Qualidade não é um diferencial de mercado para as empresas conseguirem vender mais e lucrar mais, hoje, é um pré-requisito que devem conquistar para conseguir colocar seus produtos no mercado global.

Para obter qualidade, devemos, por exemplo, estar em conformidade com requisitos de clientes, antecipar-se e satisfazer os desejos dos clientes, ou ainda, escrever tudo o que se deve fazer e fazer tudo o que foi escrito.

E o que leva a falta de qualidade? A inabilidade de um software em cumprir a especificação de requisitos operacionais de sua responsabilidade, ou a habilidade de produzir efeitos indesejados são chamados de FALHAS (FAIL) e são a manifestação física (ERROR) de um DEFEITO (FAULT – imperfeição ou anomalia existente no código-fonte de um programa), o que a produtos pouco confiaveis, ou seja, de baixa qualidade.

O teste de software é um conjunto de atividades que pode ser planejada antecipadamente e realizada sistematicamente, ou seja, consiste num estudo das fases do software (desde a análise até a entrega do produto). Existem dois tipos de atividade: A verificação, que faz com que o software funcione corretamente de acordo com especificações; e a validação, que se refere ao conjunto de atividades que garantem ao software construído funcionar de maneira razoável de acordo com as exigências do cliente.

A figura abaixo ilustra quando testar, como testar e o que testar:



Esperamos que esse tipo de informação lhe seja útil no momento de avaliar como garantir ou melhorar a qualidade dos software feitos.

Até a próxima!

sexta-feira, 25 de junho de 2010

Alinhamento Estratégico de TI – Parte 3

Nessa série de artigos buscamos avaliar como as empresas de tecnologia de organizam para competir e obter sucesso nesse acirrado mercado, além de, fazer uma análise para guiar a estratégia de competitividade.

Numa análise horizontal da área de software temos que:



Dessa forma, vamos abordar nosso útimo modelo de processo de desenvolvimento de software, o modelo orientado a produtos.

Esse tipo de modelo possui como característica o desenvolvimento e suporte de produtos de software (software pacote, ferramenta de desenvolvimento, componentes e produtos complexos) e tem na programação (design patterns), no design (arquitetura) e nos testes de sistema suas atividades principais, além de, foco de qualidade no produto.

Com isso, o gerenciamento do projeto deve se concentrar na duração do projeto (seu maior risco) e na qualidade do produto. O modelo ISSO 9126 deve ser o referencial para esse tipo de empresa.

E não precisamos nem dizer que inovações no produto é o fator chave para o sucesso.

Esperamos que essas informações sejam utéis a você tanto quanto tem sido para nós.

Agradecimentos especiais ao André Leme Fleury por essa brilhante análise.

Até a próxima!

segunda-feira, 21 de junho de 2010

Alinhamento Estratégico de TI – Parte 2

No artigo anterior apresentamos alguns fatores que empresas que projetam, desenvolvem ou implementam sistemas de software específicos (customizados), ou seja, orientados a clientes deveriam considerar para se diferenciar.

Hoje abordaremos o modelo de processo de desenvolvimento de software para empresas que possuem uma quantidade razoável de clientes e customizações de produtos.

Esse tipo de empresa tem como caracterísca serviços relacionados com customização, treinamento ou operação de software e tem como principais atividades a programação e a modelagem de sistemas. Seu foco é em pesquisa e desenvolvimento de um portfólio de serviços, que basicamente é composto por:

• Customização (fábrica de software)
• Implantação
• Treinamento
• Outsourcing

Para esse tipo de empresa, orientada a serviços, o fator chave para o sucesso é a gestão do projeto de acordo com as especificações contratuais e seu maior risco é o custo e a duração do projeto. Portanto, um foco de qualidade baseado em projeto e fundamentado no PMBok são muito importantes para o sucesso comercial da empresa.

Seu business é serviço? Think ahead!

No próxima, e último, falaremos sobre empresas de produtos.

Até mais!

quarta-feira, 9 de junho de 2010

Alinhamento Estratégico de TI – Parte 1

A atividade principal da sua empresa é tecnologia? Como ela se organiza para competir no mercado?

Investir em metodologia, arquitetura, gestão, treinamento, etc, são importantes para todas as empresas, porém, dependendo do posicionamento de mercado da sua empresa alguns pontos são mais importantes que outros.

Hoje, vamos discutir sobre empresas que projetam, desenvolvem, implementam sistemas de software específicos (customizados), ou seja, orientadas a clientes.

Por que orientadas a cliente? Porque, os produtos/serviços são sistemas de acordo com especificações do cliente e o fator chave para o sucesso é atender e superar as expectativas do cliente. Com isso, o principal ponto da gestão do projeto é gerenciar as expectativas do cliente e as mudanças nos requisitos do projeto (o principal risco).

Esse tipo de empresa deveria ter como estratégia competitiva a excelência operacional através de um processo de produção otimizado (por exemplo, baseado no CMMI), pois quanto mais otimizado o processo, maior a previsibilidade e produtividade da produção de um produto customizado.

Sua empresa se enquandra nesse perfil?

Na próxima falaremos sobre empresas de serviços.

Até mais!

sexta-feira, 4 de junho de 2010

Pare. Identifique o Problema. Avalie Como Usar a Tecnologia Para Resolver.

A cerca de 8 anos acompanhamos a evolução da plataforma .NET da Microsoft, e em especial, a linguaguem C#. Isso claro, sem falar da UML, da Orientação a Objetos e Padrões de Projeto.

Tivemos oportunidades de colocar em prática muitos dos conceitos para que pudessemos sair de situações comerciais desfavoráveis. Por exemplo, imagine só, você se apresenta como referência na tecnologia, vende uma consultoria de estabilização de um pequeno sistema de B2B, e sem olhar o produto, estima em 1 mês, afinal, um sistema pequeno (22 telas de cadastro, 4 telas de média complexidade e 1 muito complexa) não deve ser tão dificil de estabilizar, certo?

Porém, conforme se passaram os dias, o que se viu foi que o sistema estava tão ruim que, certamente, 1 mês seria pouco. E logo se passaram quase 2 semanas.

Então, resolvemos analisar o sistema e a situação, e olhando o sistema funcionar vimos que todas as telas seguiam um padrão para as operações básicas (inclusão, alteração, pesquisa e exclusão), logo surgiu a luz, vamos implementar um conjunto de classes base que fizesse todo o CRUD, uma tela base que implementasse toda a lógica de utilização dessas classes, e então, alteramos a herança das telas e alteramos apenas para passar os dados específicos de cada item (lembrando que tudo isso foi em 2005 com .Net 1.1).

Levamos pouco mais de 1 dia para desenvolver e estabilizar as classes base, e mais outro dia, para alterar todas as telas. Pronto! O sistema estava estável e ganhamos 4 dias por conta da solução técnica.

E com um pouco de refatoração nesse conjunto de classes, pudemos aproveitá-las em outro projeto, o qual, em apenas 1 mês, fizemos o levantamento, a documentação, o desenvolvimento, a homologação e a implementação de um sistema de controle de ações de marketing com distribuidores (12 telas de cadastro, 2 telas de negócio e 3 relatórios).

A evolução desse trabalho é o lob Framework. Avalie o uso nos seus projetos e pare de investir tempo e dinheiro para criar e testar a infra-estrutura da aplicação.

Versão Trial em http://ww.apolineo.com.br/products.aspx

sexta-feira, 28 de maio de 2010

TI... o desafio nosso de cada dia

É com muita satisfação que damos ínicio a um novo desafio em nossa empresa.

Através desse espaço esperamos compartilhar idéias e experiências, sejam boas ou ruins, dos projetos que estivermos envolvidos. Com isso, esperamos criar um ambiente de debate virtual que leve a debates presenciais e uma troca de experiências ricas para todos da comunidade de soluções baseadas em software.

Nos da Apolineo acreditamos que o sucesso está na utilização prática dos conceitos teóricos que circundam nossa área de estudo, porém, vemos no dia-a-dia, que muitas empresas partem para estratégias do rambo (aquela onde o melhor programador/desenvolvedor resolvedor de problemas luta contra tudo e contra todos para manter o software funcionando). Mas sabemos também que há outras empresas, exceções de mercado, que possuem uma política voltada ao ser humano, pois afinal, são eles que fazem a empresa, não são?

O pior disso tudo é que não conseguimos evoluir como profissionais e acabamos tendo que dedicar muito tempo do nosso lazer em estudos para nos mantermos competitivos no mercado.

Você sofre desses problemas?