quarta-feira, 20 de abril de 2011

Event-based Asynchronous Pattern (EAP). O futuro padrão para as interfaces das aplicações?

Sabe quando seu aplicativo vai realizar uma tarefa demorada e ele “para de responder” enquanto não termina de processar? Esse é o cenário de grande parte dos aplicativos atualmente.

Com a versão 2.0 do Microsoft .Net Framework foi introduzido um pattern endereçado a esse tipo de cenário chamado Event-based Asynchronous Pattern (EAP), oferecendo as vantagens de uma aplicação multithreaded e "escondendo" a complexidade inerente a esse tipo de design.

Com ele, você pode:

  • Executar tarefas demoradas, como downloads e operações de banco de dados, em "background", sem congelar sua aplicação;
  • Executar operações simultaneamente e receber notificações quando cada uma for concluída;
  • Aguardar que recursos fiquem disponíveis sem congelar sua aplicação;
  • Comunicar-se com as operações assíncronas pendentes usando o modelo de eventos;

A efeito de exemplo e aprendizado, o cenário que vamos trabalhar é a leitura de arquivos texto para importação de dados no sistema.

Começamos a implementação do pattern criando as classes de argumento dos eventos que criaremos para nos comunicarmos com o chamador. Essas classes devem herdar do AsyncCompletedEventArgs ou ProgressChangedEventArgs (no caso de informação referente ao progresso da execução).

No nosso caso, a classe padrão (ProgressChangedEventArgs) é suficiente para notificarmos o progresso da leitura e mapeamento dos dados do arquivo. Então, o argumento para a notificação da finalização do processamento ficará:

public class MapCompletedEventArgs : AsyncCompletedEventArgs
{
  public IEnumerable<Customer> CustomersList { get; private set; }
  public MapCompletedEventArgs(IEnumerable<Customer> customersList,
  Exception error, bool isCancelled, object userState)
    : base(error, isCancelled, userState)
  {
    this.CustomersList = customersList;
  }
}

O próximo passo é criar os handles (delegates) para as operações de avisar o progresso e quando o processamento estiver terminado:

public delegate void MapProgressChangedEventHandler(ProgressChangedEventArgs e);
public delegate void MapCompletedEventHandler(MapCompletedEventArgs e);

Agora, criaremos os respectivos eventos para informar o progresso e quando a execução estiver concluída:

public event MapProgressChangedEventHandler MapProgressChanged;
public event MapCompletedEventHandler MapCompleted;

Para garantir o uso da thread correta na execução dos eventos precisamos criar callbacks para serem executados via AsyncOperation:

private SendOrPostCallback onProgressReportDelegate;
private SendOrPostCallback onCompletedDelegate;

A última variável que precisamos é um dicionário para controlar as múltiplas requisições:

private HybridDictionary userStateToLifetime = new HybridDictionary();

No construtor da classe inicializamos os callbacks:

public CustomerMapper()
{
  onProgressReportDelegate = new SendOrPostCallback(ReportMapProgress);
  onCompletedDelegate = new SendOrPostCallback(ContentFileMappingCompleted);
}

private void ContentFileMappingCompleted(object operationState)
{
  OnMapCompleted(operationState as MapCompletedEventArgs);
}

private void OnMapCompleted(MapCompletedEventArgs e)
{
  if (MapCompleted != null)
  {
    MapCompleted(e);
  }
}

private void ReportMapProgress(object state)
{
  OnMapProgressChanged(state as ProgressChangedEventArgs);
}

private void OnMapProgressChanged(ProgressChangedEventArgs e)
{
  if (MapProgressChanged != null)
  {
    MapProgressChanged(e);
  }
}

E finalmente chegamos aos métodos de mapeamento assíncrono do arquivo, o qual, criamos uma operação assíncrona através da classe AsyncOperationManager:

public IAsyncResult MapAsync(string filePath)
{
  var asyncOp = AsyncOperationManager.CreateOperation(filePath);

  lock (userStateToLifetime.SyncRoot)
  {
    if (!userStateToLifetime.Contains(filePath))
    {
      userStateToLifetime[filePath] = asyncOp;
    }
  }

  var action = new Action<string, AsyncOperation>(MapFileWorker);
  return action.BeginInvoke(filePath, asyncOp, null, null);
}

Disponiblizaremos um método para ser utilizado caso o usuário queira cancelar a operação a qualquer momento:

public void CancelReadAsync(string filePath)
{
  var asyncOp = userStateToLifetime[filePath] as AsyncOperation;
  if (asyncOp != null)
  {
    lock (userStateToLifetime.SyncRoot)
    {
      userStateToLifetime.Remove(filePath);
    }
  }
}

No método que realiza o processamento da requisição precisamos garantir que o evento referente a finalização do processamento seja executado em caso de sucesso, cancelamento ou falha. E para executarmos o evento utilizamos o método PostOperationComplete da operação assíncrona que criamos:

private void MapFileWorker(string filePath, AsyncOperation asyncOp)
{
  Exception error = null;
  IEnumerable<Customer>customersList = null;
  if (!TaskCanceled(asyncOp.UserSuppliedState))
  {
    try
    {
      customersList = MapFileLogic(filePath, asyncOp);
    }
    catch (Exception ex)
    {
      error = ex;
    }
  }

  if (!TaskCanceled(asyncOp.UserSuppliedState))
  {
    lock (userStateToLifetime.SyncRoot)
    {
      userStateToLifetime.Remove(asyncOp.UserSuppliedState);
    }
  }

  var arg = new MapCompletedEventArgs(customersList, error,
    TaskCanceled(asyncOp.UserSuppliedState), asyncOp.UserSuppliedState);

  asyncOp.PostOperationCompleted(onCompletedDelegate, arg);
}

Para o evento de acompanhamento do progresso usamos o método Post da operação assíncrona:

var arg = new ProgressChangedEventArgs(progressPercentage, asyncOp.UserSuppliedState);
asyncOp.Post(this.onProgressReportDelegate, arg);

Ao final, a classe que utilizará o método assíncrono ficará assim:

var mapper = new CustomerMapper();
mapper.MapCompleted += new CustomerMapper.MapCompletedEventHandler(mapper_MapCompleted);
mapper.MapProgressChanged += new CustomerMapper.MapProgressChangedEventHandler(mapper_MapProgressChanged);
mapper.MapAsync(Environment.CurrentDirectory + @"\CustomerTextFile1.txt");

E talvez você esteja se pergutando “Legal, e por que eu deveria me importar com isso?”.

Essa é uma tendência de programação que ganhou força com o Silverlight e parece ser o padrão para se programar no Windows Phone 7.

Será que esse pattern se tornará padrão para programação de aplicações no futuro?

Você já usou esse padrão?

Até a próxima!

DOWNLOAD EXEMPLO:
EAP.TEXTFILEMAPPER.RAR


Artigos Recomendados:

>>   Software para Pessoas do Séc XXI
>>   Prepare-se para o C# 5 – Parte 2
>>   Prepare-se para o C# 5 – Parte 1
>>   Reflection de Alta de Performance
>>   Extension Methods = Manutenibilidade

terça-feira, 12 de abril de 2011

Software para Pessoas do Séc XXI

Antigamente quando você dizia “Chefe, chefe, estou com uma grande idéia”, ele te dizia, “Calma. Respira fundo que passa.”

Hoje, as necessidades mudaram. As pessoas estão se despedindo dos seus chefes e apenas em 2011 foram 163.679 formalizações de empreendedores individuais segundo o Sebrae.

Antes, ao comprar uma televisão você virava um consumidor, agora, ao comprar qualquer eletrônico você se torna um produtor. Já percebeu quanto material é produzido por pessoas como você em eventos, festas, desastres, etc? Somos mais rápido que a imprensa!

O tempo virou o bem social coletivo. Relevância, a palavra chave nesse mar de informação. E co-criação a saída de uma vida moderna sem significado. Você conhece o Camiseteria? Ou o Electrolux Design Lab? Talvez o Fiat Mio? Provavelmente o Nespresso? Mas com certeza o Wikipedia!

As pessoas mudaram. Você mudou! Só que as empresas ainda criam software como na época do “Departamento de Processamento de Dados”? rs

"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

Em projetos de softwares modernos além da arquitetura do software, da estrutura de dados, da distribuição e da sergurança precisamos considerar as capacidades e limitações dos usuários, de forma a tornar o trabalho deles mais eficaz, eficiente e agradável.

Para isso, primeiramente sua metologia deve promover o envolvimento do usuário no projeto.

Como o usuário é a pessoa que mais conhece sobre o contexto do seu trabalho, sem desconsiderar o risco de perda de tempo e recursos em função da variabilidade e subjetividade que caracterizam as atividades com usuários, devemos desenvolver técnicas para planejar, organizar e executar o envolvimento adequado, que pode ser:

  • Informativo: o usuário é visto como fonte de informação que são extraídas através de técnicas de entrevistas, questionários ou observação;
  • Consultivo: o projetista, valendo-se ou não das informações coletadas, elabora soluções de projeto e pede que o usuário as verifique e emita uma opinião;
  • Participativo: quando a corporação transfere ao usuário o poder sobre as decisões de projeto e requer alto engajamento (que deve vir desde a alta gerência);

Durante a especificação do sistema e o desenvolvimento do protótipo você deve trabalhar os 5 fatores da experiência do usuário:

  • Utilidade: percepção do usuário quanto a funcionalidade lhe agregar algum valor dentro do seu contexto;
  • Usabilidade: diz respeito à eficácia, eficiência e satisfação do usuário na realização de seus objetivos com o sistema;
  • Disponibilidade: refere-se aos elementos da interface que fornecem feedback sobre o estado do sistema, mecanismos que evitem perda de informação, etc;
  • Estética: refere-se ao apelo visual da aplicação, à sua atratividade para o usuário;
  • Processo off-line: complementa a experiência do usuário, como a confiança no nome da empresa, a segurança dos dados, suporte, treinamento, campanha de marketing, entre outros;

O IPhone é um exemplo de produto criado com esse tipo de abordagem.

O que achou? Deixe seus comentários.

Até o próximo!




Artigos Recomendados:

>>   Redes Sociais no Ambiente Corporativo
>>   Pensando em ERGONOMIA e USABILIDADE
>>   Design centrado no usuário

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