quarta-feira, 30 de março de 2011

Redes Sociais no Ambiente Corporativo

Quando você leu o título o que lhe veio a cabeça? Twitter? Facebook?

Hum, se sua resposta for sim, sinto em lhe dizer, mas você está confundindo MÍDIA SOCIAL com REDE SOCIAL.

“Qualquer pessoa ficaria chateado, qualquer um de nós ficaria abatido, desmotivado, mas não Joseph Klimber” e você também não precisa.

Então, o que é uma rede social?

Rede social é uma estrutura composta por pessoas conectadas que compartilham valores e objetivos comuns. Isso quer dizer que o grupo que se reúne todos os domingos para jogar bola, as pessoas que participam do Campus Party ou o grupo de voluntários da GRAAC são todos redes sociais (só que em diferentes níveis).

Com a aparição de novas tecnologias como a internet a interação entre as pessoas cresce com rapidamente. Então, com a criação de mídias sociais como o Youtube, o Twitter ou Facebook essa velocidade aumentou ainda mais. São milhões de pessoas colaborando, compartilhando, co-criando, presentes em todos os lugares e em busca de um mundo melhor.

“Empresas que compreendem a importância de aproveitar o poder dos comportamentos colectivos para impulsionar mudanças positivas nos negócios serão bem-sucedidas.” – Gartner

E porque na minha empresa não é assim?

Quando dentro de uma empresa buscamos conhecimento para atingirmos uma meta, realizar um trabalho, só que a máquina da empresa, a obrigação, a rotina, seus processos não permitem troca de conhecimento, e esse ambiente inadequado, inibe a interação das pessoas.

Ninguém interage se não vê essa troca.

Para estabelecer relações precisam ser criadas conexões, as quais podem gerar ou não relações de confiança, onde geralmente, a troca de conhecimento pessoal começa a acontecer.

Talvez você já tenha experimentado isso de alguma forma. Você já conversou sua vida pessoal com algum colega do trabalho que você não confia? Mas já deve ter considerado o comentário de um desconhecido sobre um produto que você queria comprar.

Uma vez que as pessoas começam a trocar informações pessoais elas se viciam nisso e esse caminho evolui para o ambiente profissional.

Nessas conexões haverá pessoas com diversos perfis e nosso trabalho será identificar o perfil de cada um e incentivá-las. Os principais perfis são os CRIADORES (aquelas que geram conteúdo para outras pessoas), CRÍTICOS (aquelas que opinião sobre os conteúdos gerados), PARTICIPANTES (aquelas que participam comentando ou interagindo com o conteúdo gerado) e os ESPECTADORES (aquelas que apenas acompanham esses conteúdos).

Você deve estar se indagando, isso é bonito, mas sem controle vai virar uma bagunça!

O “controle” muda para moderação, bom senso, política de boa conduta. E isso a própria rede é capaz de “controlar” se as pessoas críticas entenderem a política da rede. Alias, VOCÊ DEVERIA SER UMA DESSAS PESSOAS COM PERFIL CRÍTICO!

Estamos na era da Economia da Colaboração. As pessoas agora interagem para a CO-CRIAÇÃO do conhecimento em busca de um mundo melhor. Então, permita de alguma forma que seu cliente ou colaborador faça parte do processo criativo e produtivo do projeto.

E porque as pessoas dedicam seu precioso tempo na co-criação de um produto?

1. Fazer parte de algo maior
2. Mostrar que fazem parte daquilo
3. Pura diversão
4. Oportunidade de colaborar
5. Recompesa financeira

Já está acontecendo. É real! Não conhece a CAMISETERIA, o NESPRESSO, o FIAT MIO, o DESIGN LAB ELECTROLUX ou ainda o WIKIPEDIA.

Contudo, nada disso faz sentido se não aprendermos, não compartilharmos, não inovarmos, não criticarmos, não participarmos. E para isso, são necessárias estratégias, capacitação, metodologias específicas e projetos.

Como é o ambiente na sua empresa? É colaborativo? Participativo? As pessoas se sentem confortáveis em expressarem suas opiniões? Torcem o nariz quando houvem falar de rede social? E qual sua opinião sobre o assunto?


PARTICIPE DA DISCUSSÃO NO FACEBOOK!




Artigos Recomendados:

>>   Pensando em ERGONOMIA e USABILIDADE
>>   Design centrado no usuário

 

terça-feira, 29 de março de 2011

Prepare-se para o C# 5 – Parte 2

Demorou mais chegou... rs

No último artigo mostramos que a expressão document = await Fetchasync(urls [i]) do C# 5.0 era realizada assim:

state = State.AfterFetch;
fetchThingy = FetchAsync(urls[i]);
if (fetchThingy.SetContinuation(archiveDocuments))
  return;
AfterFetch: ;
  document = fetchThingy.GetValue();

Nesse modelo, um método assíncrono retorna um Task<T>, e por hora, vamos assumir que o FetchAsync também retorne um Task<Document>. Então, o código atual será realizado assim:

fetchAwaiter = FetchAsync(urls[i]).GetAwaiter();
state = State.AfterFetch;
if (fetchAwaiter.BeginAwait(archiveDocuments))
  return;
AfterFetch: ;
  document = fetchAwaiter.EndAwait();

A chamada a FetchAsync cria e retorna um Task<Document>. Ao chamar este método ele imediatamente retorna um Task<Document> que de alguma forma busca o documento desejado de forma assíncrona. Para fazer alguma coisa quando ele estiver completo buscamos na tarefa por um Awaiter, e ele fornece 2 métodos: BeginAwait, assina a continuação para a tarefa (executado após a finalizada a tarefa) e o EndAwait, que extrai o resultado da tarefa finalizada.

Como esses métodos são implementados na Task (para métodos void) ou Task<T> (para métodos que retornem valor) como fica os métodos que não retornam Task ou Task<T>?

Nesse caso é utilizado a mesma estratégia do LINQ, ou seja, se tivermos:

From c in customers where c.City == "London"

Isso é traduzido para:

Customers.Where(c => c.City == "London")

E uma resolução de overload tenta encontrar o melhor método "Where" checando se a implementação de "Customers" implementa tal método, caso contrário, busca por métodos de extensão.

Com o padrão GetAwaiter/BeginAwait/EndAwait é a mesma coisa, é feita a resolução de overload na transformação da expressão, verificado o método ou a extensão adequeada.

O insight é que assincronia não requer paralelismo, mas o paralelismo exige assincronia, e muitas das ferramentas úteis para o paralelismo podem ser usadas para assincronia sem paralelismo.

Por isso o uso do Task, ele não possui paralelismo inerente e podemos representar unidades de trabalho pendentes que pode ser paralelizada e não requer multithreading, além de, já possui mecanismos de cancelamento entre outras características úteis da TPL (Task Parallel Library).

Voltando ao exemplo da busca de documentos, ele foi deliberadamente planejado para demostrar o pulo do gato, o CPS (Continuation Passing Style), para controlar até mesmo orquestrações simples de 2 tarefas assíncronas de métodos void.

Então, vamos falar um pouco sobre composição de métodos assíncronos.

Suponha que o método ArchiveDocuments retornasse o número total de bytes arquivados:

long ArchiveDocuments(List<Url> urls)
{
  long count = 0;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = Fetch(urls[i]);
    count += document.Length;
    Archive(document);
  }
  return count;
}

Agora, vamos reescrevê-lo de forma assíncrona usando um CPS:

void ArchiveDocumentsAsync(List<Url> urls, Action<long> continuation)
{
  // de alguma forma executa a busca de forma assíncrona,
  // depois invoca sua continuação
}

Dessa forma, o chamador do ArchiveDocumentsAsync precisaria ser escrito em um CPS de modo que a continuidade possa ser passada. E se o retorno fosse um resultado? Então, seria uma confusão.

No modelo TAP (Task Asynchrony Pattern, nome provisório para essa feature de composição de métodos assíncronos), ao invés disso, diriamos que o tipo que representa o trabalho assíncrono e retorna um valor é um Task<T>. Em C# 5, você poderia simplesmente escrever:

async Task<long> ArchiveDocumentsAsync(List<Url> urls)
{
  long count = 0;
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    count += document.Length;
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
  return count;
}

E o compilador se encarregará de reescrever e gerar algo como:

Task<long> ArchiveDocuments(List<Url> urls)
{
  var taskBuilder = AsyncMethodBuilder<long>.Create();
  State state = State.Start;
  TaskAwaiter<Document> fetchAwaiter = null;
  TaskAwaiter archiveAwaiter = null;
  int i;
  long count = 0;
  Task archive = null;
  Document document;
  Action archiveDocuments = () =>
  {
    switch(state)
    {
      case State.Start: goto Start;
      case State.AfterFetch: goto AfterFetch;
      case State.AfterArchive: goto AfterArchive;
    }
    Start:
    for(i = 0; i < urls.Count; ++i)
    {
      fetchAwaiter = FetchAsync(urls[i]).GetAwaiter();
      state = State.AfterFetch;
      if (fetchAwaiter.BeginAwait(archiveDocuments))
        return;
      AfterFetch:
      document = fetchAwaiter.EndAwait();
      count += document.Length;
      if (archive != null)
      {
        archiveAwaiter = archive.GetAwaiter();
        state = State.AfterArchive;
        if (archiveAwaiter.BeginAwait(archiveDocuments))
          return;
        AfterArchive:
        archiveAwaiter.EndAwait();
      }
      archive = ArchiveAsync(document);
    }
    taskBuilder.SetResult(count);
    return;
  };
  archiveDocuments();
  return taskBuilder.Task;
}

Vamos fazer um teste de mesa aqui para clarear as coisas.

O que acontece quando a lista está vazia? Nós criamos um contrutor de tarefas, um delegate que retorna void e invocamos ele de forma assíncrona. Ele inicializa a variável ”count” com 0, executa a label start, pula o loop, diz para o helper “você tem um resultado” e retorna. Finaliza o delegate. O taskbuilder é solicitado para uma tarefa, e uma vez que ele sabe que a tarefa foi concluida, retorna a tarefa concluida que simplesmente representa o número 0.

E caso existam vários documentos de arquivos? Novamente criamos um taskbuilder e um delegate que serão invocados assíncronamente. No primeiro loop começamos uma busca assíncrona, assinamos o delegate como sua continuação e retornamos do delegate. O taskbuilder contrói uma tarefa que representa “estou trabalhando de forma assíncrona no corpo do ArchiveDocumentsAsync” e retorna essa tarefa. Ao ser concluída, invoca sua continuação e o delegate inicia de novo “do ponto onde ele parou” graças a máquina de estado. Tudo continua exatamente como antes, porém, direferente da versão void, o Task<long> retornado para o ArchiveDocumentsAsync sinaliza que finalizou (invocando sua continuação) e o delegate diz ao task builder para definir o resultado.

NOTA:Tal qual o LINQ, o TAP pode ser extensível por qualquer tipo que tenha um GetAwaiter, que retorne um tipo que tenha BeginAwait, EndAwait e assim por diante, para que possa ser usado nas expressões “await”. Contudo, métodos marcados para serem assíncronos devem retornar void, Task ou Task<T>.

Existem situações onde sintaxe com tokens como “where” do LINQ são mais naturais e outras onde são a sintaxe “fluent” .Where(c => ...), no TAP haverá métodos com nomes como WhenAll ou WhenAny para compor e orquestrar tarefas assíncronas:

List<List<Url>> groupsOfUrls = whatever;
Task<long[]> allResults = Task.WhenAll(
      from urls in groupsOfUrls
      select ArchiveDocumentsAsync(urls));
long[] results = await allResults;

O que isto faz? Bem, o ArchiveDocumentsAsync retorna um Task<long>, então a query retorna um IEnumerable<Task<long>>. WhenAll pega a sequência de tarefas e produz uma nova tarefa, que de forma assíncrona aguarda cada um deles, preenche um array com o resultado e invoca sua continuação com o resultado quanto ele estiver disponível.

Da mesma forma o WhenAny pega uma sequência de tarefas e cria uma tarefa que invoca sua continuação com o primeiro resultado quando qualquer uma das tarefas estiverem finalizadas.

Existirão outras combinações de tarefas e helpers relacionados. Veja mais exemplos no CTP e repare que como não é possível modificar um Task existente os combinadores foram provisóriamente adicionados na classe TaskEx,mas, serão movidos para o Task até a versão final.

Até a próxima!


Artigos Recomendados:

>>   Prepare-se para o C# 5 – Parte 1
>>   Reflection de Alta de Performance
>>   Extension Methods = Manutenibilidade


segunda-feira, 14 de fevereiro de 2011

Nós temos Arquiteto de Software! E arquitetura?

O termo arquiteto de software está virando uma commodity dentro das software houses desse meu Brasil.

Esse papel tão importante se apresenta como um diferencial nos serviços prestados aos olhos do cliente e um imenso custo aos olhos da empresa, que sem hesitar, desprezam a idéia de encarar de forma correta o processo de desenvolvimento de software.

Mas, afinal, o que é arquitetura de software?


Bom, a arquitetura de software trata o interesse de 3 visões:

  • Usuário: como os usuários usarão o sistema?
  • Negócio: como manter a aplicação flexível e gerenciável?
  • Sistema: quais os requisitos de performance, internacionalização, segurança e configuração? Quais tendências impactam no design agora ou após a distribuição da aplicação?

O processo de definir estruturas de solução fornece e avalia alternativas de projeto atuando na organização geral de controle, definindo a atribuição de funcionalidade a componentes de projeto, buscando extrair alto desempenho e escalabilidade, sem esquecer claro, de atender requisitos técnicos e operacionais.

O arquiteto de software é o profissional que:

  • Possui grande compreensão (arriscamos a incluir também “ou facilidade de compreensão”) do negócio e das tecnologias pertinentes para trabalhar na modelagem da solução;
  • Possui entendimento dos apectos técnicos para o desenvolvimento de sistemas para trabalhar na análise de viabilidade;
  • Conhece bem as técnicas de levantamento, modelagem e desenvolvimento para trabalhar em provas de conceito, simulações e experimentos;
  • Entende as estratégias de negócio da instituição onde atua para analisar tendências tecnológicas;

O que é um software bem arquitetado?


Alguns podem dizer que é aquele que faz o que o(s) usuário(s) quer(em). Mas, para avaliarmos se um software está bem projetado temos que identificar os atributos que desejaríamos encontrar nele.

O software bem arquitetado fornece:



E como construímos um software bem arquitetado?


Nosso trabalho começa junto da etapa de levantamento de requisitos buscando identificar:

  • Peculiaridades de comportamento;
  • Informações do domínio da aplicação;
  • Restrições sobre a operação;

A lista abaixo pode ser utilizada como um check-list inicial do que levantar de requisito não-funcional:

  • Usabilidade
    • Facilidade de aprender
    • Facilidade de usar
  • Manutenção
    • Reparo
    • Evolução
  • Confiabilidade
    • Disponibilidade
    • Taxa de falhas
  • Desempenho
    • Tempo
    • Espaço
  • Reusabilidade
    • Da aplicação
    • Dos subsistemas
    • Dos objetos
    • Dos módulos
  • Portabilidade
  • Segurança
    • Disponibilidade
    • Integridade
    • Confidencialidade
    • Operacional

E os complementando estão os atributos do projeto que guiam a concepção de solução para que o produto final satisfaça aos requisitos identificados. Esses atributos são:



Em uma arquitetura podemos identificar os componentes (subsistemas), conectores, mecanismos de interação, topologia e restrições semânticas (por exemplo, a camada de interface só pode acessar dados através da camada de negócios).

O estilo arquitetural, ou o conjunto de padrões, fornecem um conjunto de requisitos não-funcionais e atributos de projeto que permitem distinguir uma arquitetura de outra e prever as características desejadas no sistema.

Os estilos de arquiteturas mais comuns são:

:: CAMADAS

Estruturar o sistema num conjunto de camadas onde cada uma agrupa um conjunto de tarefas num determinado nível de abstração.

vantagem
  • flexibilidade
desvantagem
  • alto grau de dependência
  • performance

:: OBJETOS

Permite a organização do programa em tipos de dados abstratos, que interagem através de troca de mensagens, podem ser modificados, reutilizados, distribuídos e executados sequencialmente ou em paralelo.

vantagem
  • manutenibilidade
  • performance
desvantagem
  • acoplamento
  • reuso


Atualmente, a combinação de estilos, por exemplo, camadas e objetos, guiam o desenvolvimento de software. A imagem abaixo ilustra a recomendação da Microsoft para a criação de sistemas de informação:



Os atributos que guiam o Microsoft Architecture Guide 2.0 são:
  • Separação de conceitos: divida as funcionalidades da aplicação (alta coesão) e minimizar pontos de interação (baixo acoplamento);
  • Responsabiliade única: cada componente deve ser responsável por apenas uma funcionalidade;
  • DRY (Don’t Repeat Yourself): específique suas intenções em apenas um lugar;
  • YAGNI (You Ain’t Gonna Neet It): projete apenas o necessário;
  • LoD (Least Knowledge): um componente ou objeto não deveria conhecer os detalhes internos de implementação

Esperamos que esses conceitos acadêmicos com uma visão prática possam ajudá-lo a vencer os desafios do dia-a-dia.

Na próxima falaremos sobre Arquiteturas de Domínios Específicos (DSSA) e Arquiteturas de Interface com o Usuário.

Até a próxima!



Artigos Recomendados:

>>   Pensando em ERGONOMIA e USABILIDADE
>>   Reflection de Alta de Performance
>>   Boas práticas para gerência de requisitos, de acordo com os modelos MPS.BR e CMMI
>>   Extension Methods = Manutenibilidade
>>   Alinhamento Estratégico de TI – Parte 1

 

terça-feira, 25 de janeiro de 2011

Prepare-se para o C# 5 – Parte 1

É fantástico trabalhar com uma plataforma que evolua junto com a complexidade que enfrentamos em nosso dia-a-dia.

Olhando a evolução da linguagem vemos que paramos (ao menos deveriamos ter parado) de escrever certos tipos de código. Por exemplo, com a adição do iterator blocks e do anonymous methods no C# 2.0, o compilador pode armazenar a continuação de “o que vem depois” e encapsular em uma classe os métodos que acessam varíaveis locais.

Com o LINQ e o query comprehensions (C# 3.0) fica a cargo do compilador construir o modelo de objetos correto para construir as chamadas, árvores de expressão e simplifica muito a escrita de código que ordena, filtra, associa, agrupa e sumariza dados.

Hoje em dia vivemos a integração de sistemas. Desenvolvemos aplicativos .NET para rodar dentro de uma rede social em Python. Nossos aplicativos precisam tanto interoperar com sistemas legados quanto sistemas SAP. E para simplificar isso foi criado o dynamic type (C# 4.0), onde fica a cargo do compilador descobrir como gerar o código em tempo de compilação que faça a análise do Dynamic Language Runtime em tempo de execução.

Também estamos utilizando muito algoritmos de programação assíncronos. E a tendência, com sistemas baseados em nuvem, é popularizar esse tipo de algoritmo.

Com o C# 5.0 você será capaz de pegar esse código síncrono:

void ArchiveDocuments(List urls)
{
  for(int i = 0; i < urls.Count; ++i)     Archive(Fetch(urls[i]));
}

E implementando o FetchAsync e ArchiveAsync transformá-lo em:

async void ArchiveDocuments(List urls)
{
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null) await archive;
    archive = ArchiveAsync(document);
  }
}

O compilador do C# 5.0 irá gerar o código da máquina de estado, os lambdas, as continuações e checar se a tarefa foi concluída para você. Show!

Esses recursos foram apresentados no último PDC pelo Anders Hejlsberg e atualmente está sendo desenvolvido o CTP do protótipo do compilador do C# 5.0.

Se você se animou com a notícia e quer saber mais visite Asynchronous Programming for C#.

QUE TAL LER A PARTE 2?



Veja também:

>>  Reflection de Alta de Performance
>>  Dynamic CRUD no .NET 4.0
>>  Programação Dinâmica no C# 4.0
>>  Extension Methods = Manutenibilidade

quinta-feira, 6 de janeiro de 2011

Pensando em ERGONOMIA e USABILIDADE

Você já leu um livro onde as páginas não estejam numeradas, sem índice ou resumo? Ou ainda, se sentiu p@#! da vida por procurar uma rua que não tenha placa indicando o nome?

A ergonomia e a usabilidade são constituídos de pequenos detalhes que são notados apenas quando não estão presentes, e estes detalhes ou qualidades, fazem com que os usuários se sintam confiantes e satisfeitos por atingirem seus objetos com menos esforços, em menos tempo e com menos erros.

Programas de software (e até mesmo bibliotecas de código - API) e suas interfaces são ferramentas cognitivas, capazes de modelar representações, abstrair dados e produzir informações, facilitando a percepção, o raciocínio, a memorização e a tomada de decisão para cada tipo de usuário, inteligência, estilo cognitivo e personalidade.

A engenharia de usabilidade surge como resposta a necessidade de desenvolver programas de software interativo com usabilidade.

Enquanto a engenharia de software se preocupa com o núcleo funcional, a estrutura de dados, os algoritmos, os recursos computacionais, a engenharia de usabilidade se preocupa com a lógica de funcionamento, apresentações, estruturas de diálogos e lógica de operação.

Para desenvolvermos uma interface agradável, intuitiva, eficiente e fácil de operar temos que projetar a:

  • Condução: a inteface deve aconselhar, orientar, informar e conduzir o usuário na interação com o sistema;
  • Carga de Trabalho: elementos da interface que têm papel importante na redução da carga cognitiva e perceptiva do usuário e no aumento da eficiência do diálogo;
  • Adaptabilidade: a interface fornece diversas maneiras de realizar uma tarefa, deixando ao usuário a liberdade de escolher e dominar uma delas no curso de aprendizado;
  • Gestão de Erros: mecanismos que permitem evitar ou reduzir a ocorrência de erros e que favoreçam sua correção caracteriza a interface segura;

Durante toda etapa de desenvolvimento das interfaces deverão avaliadas a ergonomia e a usabilidade da solução proposta buscando identificar:

  • Barreiras: o usuário esbarra sucessivas vezes e não aprende a transpor sem ajuda externa um aspecto da interface fazendo com que ele desista de utilizar a função do sistema temporária ou definitivamente;
  • Obstáculos: o usuário esbarra algumas vezes, mas aprende a transpor um aspecto da interface, porém, perde desempenho nas próximas realizações da tarefa;
  • Ruídos: aspecto da interface que mesmo não sendo barreira ou obstáculo causa uma diminuição de seu desempenho na tarefa.

O aspecto não determinístico do projeto de interfaces sugere que o ciclo de desenvolvimento seja essencialmente evolutivo, iterativo e centrado no usuário (ver DESIGN CENTRADO NO USUÁRIO).

A figura abaixo ilustra esse ciclo:



Os projetistas devem identificar e definir com cuidado os requisitos de usabilidade e, durante o desenvolvimento, certificar-se de que são corretos, assegurar que o protótipo esteja indo na direção correta e que satisfaz tais requisitos antes de ser liberada para implementação ou para o mercado.

Essas abordagens e métodos são iniciativas recentes e efetivas para o desenvolvimento da engenharia de usabilidade e certamente o ajudará nos desafios diários.




Gostou? Siga-nos e compartilhe suas experiências e opiniões deixando seu comentário.



Posts Relacionados

Design centrado no usuário

 

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!