domingo, 24 de dezembro de 2006

Aprofundando os conceitos de Design by Contract

No último post sobre DbC, descrevi seus conceitos básicos e algumas considerações necessárias para sua aplicação. Farei agora um pequeno aprofundamento, descrevendo alguns aspectos importantes para sua aplicação prática.

 

Resumo rápido

  • Um contrato aplicado à escrita de código compreende na verificação de certas condições que devem ser verdadeiras durante a execução do programa. Se não forem, significa que o programa violou o contrato definido e conseqüentemente possui bugs.
  • Geralmente as linguagens de programação possuem uma rotina (uma função, um método ou mesmo uma macro) chamada Assert, que podemos utilizar para verificar se os contratos estão sendo cumpridos.

Contratos geralmente possuem:

  • Pré-condições: Condições esperadas; Valores ou estados previstos de serem recebidos;
  • Pós-condições: Resultados esperados; Valores ou estados previstos de serem resultantes;
  • Invariáveis: Condições que permeiam (estão presentes em) todos os contratos e que são imutáveis.

Sob os aspectos de implementação, da escrita de código, podemos dizer que as pré-condições tipicamente validam os parâmetros de entrada de uma rotina; as pós-condições validam os estados e valores resultantes da rotina; e as invariáveis, validam estados que devem permanecer verdadeiros, imutáveis, antes das pré-condições e após as pós-condições.

As invariáveis geralmente são utilizadas em classes, de forma a garantir que todas as suas instâncias sempre possuam determinados estados.

 

Mais sobre Assert

Por Assert interromper a execução do programa, gerando uma descrição da falha no cumprimento da condição, a utilizamos somente em modo de depuração (debug mode). No modo de depuração efetuamos todos os testes no programa em busca de falhas, até que o software seja considerado suficientemente estável para ser liberado. Neste ponto, compilamos o software em modo de liberação (release node) fazendo com que os Asserts sejam desabilitados. Desta forma, o programa não termina ao entrar num estado inválido, apesar de poder funcionar de maneira imprevisível.

Logo, a qualidade dos testes efetuados em modo de depuração será a garantia de que o software não está operando fora do esperado em modo de liberação.

Exemplo (C++):

void Funcionario::DefinirDiaDoPagamento(
  const int dia)

{
        // Contrato: O dia do pagamento deve ser
        // sempre igual, independente do mes em
        // que e' pago.
        
        // Logo, considerando que ha' um mes que
        // possui 28 dias (fevereiro) de quatro em
        // quatro anos, o dia do pagamento deve
        // variar entre 1 e 28, para poder ser
        // SEMPRE igual.

 
        #ifdef _DEBUG

        assert( ( dia >= 1 ) && ( dia <= 28 ) );
        #endif
 
        
_diaDoPagamento = dia;
}

Com a ajuda do exemplo, podemos notar alguns aspectos:

  • O método não trata o valor do dia fornecido. Pelo contrário, ele estabelece um contrato (com uma pré-condição) de que o valor do dia já esteja tratado (validado) e dentro de limites esperados.
  • O contrato só será validado em modo de depuração.
  • Ao retirar a pré-condição, o funcionamento do método continua o mesmo.

(Observação: No exemplo, a classe Funcionario poderia ter uma invariável que substituiria sua pré-condição. Assim, seria garantido que _diaDoPagamento sempre estaria na a faixa de valores desejada, independente do método que operasse sobre ele.)

 

Removabilidade de Contratos

Os contratos devem sempre poder ser removidos sem que o funcionamento do programa mude. Isso porque se o contrato fosse parte da lógica do programa, ele deveria estar sujeito a garantia de qualidade. Então teríamos de ter outros contratos para validar esses contratos, e assim por diante, o que é notadamente inviável e incorreto. Também porque devemos poder alterar a forma como eles são implementados ou removê-los por eficiência, sem que tenhamos que alterar outras partes do programa.

 

Herança, Polimorfismo e Contratos

Ao implementarmos herança em uma classe, trazemos os contratos da classe pai para a classe filha. A classe filha então pode fortalecer ou enfraquecer os contratos:

  • Fortalecer um contrato significa manter as cláusulas do contrato antigo e ainda adicionar novas cláusulas ao mesmo;
  • Enfraquecer um contrato significa não cumprir as cláusulas do contrato antigo sob certas condições;

Em termos de implementação, veremos que:

  • Podemos usar AND para fortalecer um contrato; e
  • Podemos usar OR para enfraquecer um contrato;

Exemplo (C++):

      Contrato original:

   // Pressao deve ser menor que 120
   assert( pressao <= 120 );

      Contrato fortalecido:

   // Pressao deve ser menor que 120 e
   // Temperatura deve ser menor que 400
   assert( ( pressao <= 120 )
     && ( temperatura <= 400 ) );

      Contrato enfraquecido:

   // Pressao deve ser menor que 120 ou
   // Temperatura deve ser menor que 400

   assert( ( pressao <= 120 )
     || ( temperatura <= 400 ) );

 

Fica evidenciado, então, como podemos fortalecer ou enfraquecer contratos. Mas quando devemos fortalecer ou enfraquecer um contrato ?

Para responder a esta questão, vamos refletir sobre seu impacto sobre o comportamento da classe filha.

Suponha que uma classe A possua um método X. Sua classe filha, B, precisa reimplementar X para que X se comporte conforme suas necessidades (polimorfismo). Se o método X de B fortalece as pré-condições de X de A, o risco de uma chamada correta para X de B não ser correta para X de A aumenta. Em outras palavras, como o X de B adiciona cláusulas ao contrato que existe em X de A, pode ser que uma destas cláusulas não seja cumprida ao chamar X de A. Ou mais resumido, o que é válido para B pode não ser para A.

Ainda, se o método X de B enfraquece uma pós-condição do método X de A, um resultado esperado para X de A pode não ser esperado para X de B, levando ao risco de haver problemas no entendimento do valor resultante de X de B.

Logo, concluímos que fortalecer uma pré-condição de um método em uma classe filha pode acarretar em um aumento do risco para o mesmo método da classe pai. E se enfraquecermos uma pós-condição de um método na classe filha, aumentamos o risco do método da classe filha.

Com isso, respondemos nossa pergunta e formamos duas regras:

  • Pré-condições não devem ser fortalecidas;
  • Pós-condições não devem ser enfraquecidas;

E quanto às invariáveis ?

Como uma instância de uma classe filha pode ser considerada uma instância de suas classes pai, podemos dizer que as regras (invariáveis) aplicadas para a classe filha devem servir também para as classes pai. Logo, só é permitido fortalecer as invariáveis das classes pai, o que nos leva a uma terceira regra:

  • Invariáveis não podem ser enfraquecidas;

Essas regras levam a um melhor entendimento dos contratos e das suas relações. Com elas podemos aplicar os contratos com mais segurança e garantir que a aplicação de herança e polimorfismo não resultará numa violação de algum contrato estabelecido.

 

Confiar no código escrito ?

O uso de Assert como garantia de contrato implica que o programa termine caso se encontrado uma violação no contrato (uma falha). Isso é bom, pois obriga-nos a consertar o software e não ignorar a falha, para que seja corrigida imediatamente e que a versão de liberação não contenha a mesma. Como vimos, na versão de liberação a falha não é mais tratada, já que confiamos nos testes efetuados na versão de depuração.

Como podemos então garantir ou ao menos melhorar as chances de não haver uma violação de contrato, ou mesmo outras falhas no software ?

Podemos introduzir algumas ações:

A aplicação destas ações maximiza (e muito) a chance de nunca ocorrer uma violação de contrato no software e ele funcionar conforme o previsto, mesmo que os contratos não cubram todas as falhas. Sim, o estabelecimento contrato ajuda na identificação de falhas. Mas não, ele sozinho não cobre todas as possíveis falhas.

Isso que acabei de citar leva a uma implicação:

A ausência de evidência de um erro não é a evidência da ausência de um erro.

Em outras palavras, só porque você não achou um erro não que dizer que ele não exista.

 

Conclusão

Vimos contratos mais a fundo e estabelecemos algumas regras importantes para a sua aplicação. Observamos que o uso de contratos auxilia a escrever código com menos falhas, mas que sozinho não pode oferecer a garantia de que falhas não ocorrerão.

 

Em um próximo post estarei mostrando exemplos de DbC na prática, em linguagens que suportam e que não o suportam nativamente. Até breve.

 

domingo, 10 de dezembro de 2006

Aplicativo: Songbird

Anote aí esse nome: Songbird. Daqui um tempo ele provavelmente estará concorrendo com iTunes, Windows Media Player, WinAmp e companhia na preferência do melhor "digital jukebox". Seu nome soa com algo parecido, e não lhe causa estranheza ? Sim, o nome é parecido com o Thunderbird, primo-irmão do Firefox. E não é à toa. O Songbird é construído na mesma base destes dois aplicativos, usando a Mozilla Cross-Plataform (que possui a XUL - eXtensible User-interface Language) , o que faz com que haja versões pra Windows, GNU/Linux e Mac OS. Isso quer dizer, entre outras coisas, que este player opensource também suporta skins, é todo integrado a web, oferece suporte a línguas de diversos países e, o principal, oferece suporte à extensões (plug-ins) - o ponto forte do seu primo Firefox.

O Songbird aindá está chocando, se apresentando no momento em que escrevo na versão 0.2.1, mas ele nascerá (versão 1.0) em meados de 2007. Apesar disso, ele já tem diversas funções que deixam os concorrentes com inveja.

Com certeza, você ainda vai ouvir falar muito bem dele. Talvez, escutando em audio nele mesmo... ;)

 

 

 

 

 

 

 

Confira mais no site do Songbird.

Design by Contract (DbC)

João deseja um guarda-roupa sob-medida para o seu quarto, mas teme que um marceneiro inábil não consiga fazê-lo como ele anseia. Então ele resolve deixar tudo anotado no papel para que não haja equívocos e o marceneiro entregue o guarda-roupa como planejado. Assim, ele diminui o risco de problemas e não precisa confiar tanto nas habilidades de improviso do marceneiro.

João então anota tudo e faz uma espécie de contrato onde ele - o cliente - descreve tudo que deseja obter. O marceneiro - seu fornecedor - irá acordar em entregar o pedido sob o preço e tempo determinados.

Assim, foi estabelecido um contrato, uma forma de tentar assegurar que ambas as partes se beneficiem.

Geralmente, vemos nos contratos as seguintes características:

  • Cada parte se beneficiará de alguma forma;
  • Para ter tais benefícios, terão de cumprir certas obrigações;
  • Esses benefícios e obrigações estarão documentados num Documento de Contrato;

O Documento de Contrato protegerá ambos os lados:

  • Ele protege o cliente especificando o quanto deve ser feito: o cliente espera um determinado resultado.
  • Ele protege o fornecedor especificando um mínimo aceitável: o fornecedor está ciente do escopo requisitado.

Aquilo que não está no contrato obviamente não pode ser cobrado ou imposto. Na verdade, nem sempre. Podem haver problemas neste ponto. Sempre há coisas que estão implícitas e que são esperadas por ambos os lados. Por exemplo, João obviamente espera que a porta de seu guarda-roupa feche sem problemas e que a tranca idem, mas isso não foi colocado em seu contrato pois acredita estar implícito na fabricação de qualquer guarda-roupa. Seu marceneiro, por sua vez, espera que o cheque não volte... :)

Como visto, há certas questões que são sempre esperadas de estar contidas num contrato. Seja por práticas comuns do mercado, regras, leis ou quaisquer outras que sejam largamente conhecidas. A questão é que estas regras implícitas são esperadas em todos os contratos e portanto não necessitam ser repetidas em todos eles.

Contratos e escrita de código

A primeira vista pode parecer um pouco estranho, mas toda esta bagagem de contrato é aplicável a escrita de código. Cliente, fornecedor, contrato, benefícios, obrigações e regras implícitas estão permeados em nosso código fonte de uma forma ou outra e é facilmente possível identificá-los, formalizá-los e verificá-los. O melhor de tudo é que esta formalização da escrita de código em espécies de contratos pode nos auxiliar a garantir um ótimo nível de qualidade do código. Ela ajuda a assegurar que o código fará aquilo que desejamos, que poderemos verificá-lo e atestar que cumpre suas obrigações.

Aplicando contratos à escrita de código

Ao escrever código, espressamos a lógica do software em linhas e agrupamos estas linhas em rotinas para ficarem mais fáceis de gerenciar, memorizar (diminuir a carga mental), evitar duplicações, etc. Estas rotinas muitas vezes fazem referências à outras que por sua vez à outras mais, indo de um alto nível de abstração para um baixo nível de abstração, em relação aos detalhes de implementação.

Quando uma rotina chama outra, ela espera um determinado comportamento e resultado desta - e falando em termos de contrato, um determinado benefício. Por sua vez ela tem uma obrigação com a outra rotina, que seria a de passar informações corretas para a mesma.

Então uma rotina A que chama outra, B, espera um benefício ao custo de uma obrigação. Podemos dizer que A, a rotina chamadora, seria o cliente e B, a rotina chamada, seria o fornecedor. Agora, podemos tentar estabelecer o contrato entre as partes criando suas cláusulas.

Como fazemos isso no código ?

Através de afirmações do tipo "assegure que", os asserts. Praticamente toda a linguagem de programação tem uma biblioteca que possui um método capaz de verificar se uma condição está sendo cumprida e caso ela não esteja, interromper a execução do programa informando o ocorrido.

Em C++, por exemplo, há uma biblioteca padrão chamada assert.h onde está definida a função assert. Caso a condição passada para a função assert não seja verdadeira, a execução do programa é interrompida e é informado o local (arquivo) e a linha onde a condição foi testada. Geralmente outras linguagens também possuem uma função assert em uma de suas bibliotecas padrão, ou é facilmente conseguida com terceiros ou mesmo implementada.

O exemplo que irei mostrar estará implementado em C++, mas acredito poder ser facilmente aplicado à qualquer linguagem de programação.

Considere a seguinte rotina:

 

 

A rotina cliente (que irá chamá-la) deve ter por obrigação passar um objeto válido (no caso, um objeto não-nulo). Ela se beneficiará de, passado um objeto válido, ter ele adicionado à uma lista para posterior uso.

A rotina fornecedora (a exemplificada) deve ter por obrigação adicionar um objeto à lista. Ela se beneficiará de não precisar fazer nada quando um objeto for nulo.

Fica então estabelecido um contrato entre as rotinas.

Geralmente, focaremos atenção na rotina fornecedora. Isto porque por ela sabemos também se a rotina cliente cumpriu com suas obrigações.

No caso exemplificado a rotina cliente tem uma pré-condição (obrigação) "objeto deve ser válido" e uma pós-condição (benefício/resultado) "ter o objeto adicionado à lista".

Veja a implementação:

Corpo da rotina

 

Repare que a rotina com contrato tem a seguinte estrutura:

<pré-condições>  // Obrigações da rotina chamadora

<implementação>

<pós-condições> // Garantia dos benefícios para a rotina chamadora

 

Nas pré-condições temos os requerimentos que qualquer rotina que for chamá-la deve satisfazer para estar correta. E nas pós-condições as propriedades que devem ser garantidas para quando houver o retorno (da execução) de volta à rotina chamadora.

Invariáveis

Acabamos de ver como aplicamos um contrato à uma rotina. Pudemos identificar facilmente cliente, fornecedor, obrigações e benefícios de cada uma. No entanto, na introdução sob os contratos, informei que há determinadas regras que estão implícitas e que, apesar de não declaradas no contrato são esperadas de estarem contidas. A estas regras implícitas e imutáveis, damos o nome de invariáveis. Uma invariável, como o próprio nome diz é uma regra que não varia no decorrer do contexto (do programa, ou da classe, enfim). Ela teria de estar presente em todos os contratos, de forma a assegurar que todos eles a tenham cumprido.

Por isso, o desejável é que uma invariável seja declarada somente uma vez e que seja implicitamente presente em todos os contratos (rotinas).

No entanto, somente algumas poucas linguagens atualmente suportam a notação de contratos, como Eiffel e a linguagem D. Essas possuem palavras reservadas (como invariant) que possibilitam a implementação correta de DbC.

Em outras, por sua vez, é necessário um certo trabalho para simular alguns desses conceitos.

Contratos ou tratamento de entradas inválidas

Uma diferença clara que se deve ter em mente ao decidir quando usar um contrato - usando uma cláusula de verificação (assert) - ou quando tratar uma entrada inválida - usando uma expressão condicional (if) - é: determinado valor é esperado ou é um caso especial ?

Por exemplo, suponha que uma rotina espere que um determinado objeto que será usado não seja nulo. Como é esperado que ele não seja nulo, não é aceitável que ele seja. Logo, deve-se usar uma cláusula de verificação para garantir que a rotina receberá o objeto no estado esperado.

Se ao invés disso a rotina fosse projetada para tolerar objetos nulos e tomasse determinada ação de acordo com esta condição, então deveria-se usar uma expressão condicional no lugar.

A decisão vai de encontro com o comportamento desejado em seu código e como ele irá se comportar caso haja condições que vão contra aquilo que é esperado. 

Não se deve misturar as duas coisas. A presença de verificações redundantes aumenta as chances de inconsistência. Se o contrato é preciso e explícito não há necessidade de checagens redundantes.

Conclusão

O uso de contratos no desenvolvimento de software pode levar a:

  • um melhor entendimento das relações entre as rotinas de um programa e do comportamento das rotinas em si;
  • assegurar um mínimo de confiabilidade no código;
  • diminuição de bugs por análise das obrigações e verificações de cada rotina;
  • facilidade de verificação de bugs (os asserts acusam falhas nas condições esperadas);
  • significante aumento da estabilidade (com a previsão e reflexão dos comportamentos do programa, o código é escrito para ser menos suscetível a imprevisibilidades);

Em próximos posts estarei exemplificando a notação de contratos em linguagens que a suportam e que não a suportam. Até breve.

 

Best-Pratices - Let's begin

Aos poucos, irei escrevendo mais sobre algumas metodologias, processos e best-pratices do desenvolvimento de software. Como um assunto puxa outro e muitas coisas estão interligadas, tentarei ir devagar e colocando as coisas em pequenas porções, para serem absorvidas e digeridas facilmente. Até breve.

quarta-feira, 6 de dezembro de 2006

Utilitário: Programmer's Notepad

Um notepad opensource com uma grande gama de recursos e suporte a diversas linguagens. E melhor, leve e prático.

Screenshot do PNotepad

 

 

 

 

 

 

 

 

Site do PNotepad

segunda-feira, 27 de novembro de 2006

Últimas fusões e aquisições

  • LG e Philips se fundem em LG Philips
  • Alcatel e Lucente se fundem em Alcatel Lucent
  • Corel compra WinZip
  • RedHat compra JBoss
  • Buscapé compra BondFaro
  • Nokia e Siemens fundem operações de infra-estrutura para telecomunicações
  • Americanas e Submarino se fundem em B2W
  • HP compra Mercury
  • AMD compra ATI
  • EMC compra RSA Security
  • Adobe compra Macromedia
  • Schneider Electric compra APC
  • Telefônica compra parte da TVA
  • CADE confirma fusão de Sky e DirecTV

e outras...

Como sempre, prevalecendo a "Lei da Selva".

domingo, 26 de novembro de 2006

They write the right stuff

Excelente artigo sobre como software é escrito em agências como NASA, Micron Technology e Clear Lake, quando a vida de pessoas está em risco e falhas podem acarretar perdas irreparáveis.

Para ler e se inspirar.

Artigo (Revista Fast Company, Dezembro de 1996)

Ps: Interessante que o artigo tem quase dez anos e tudo é tão atual.

segunda-feira, 20 de novembro de 2006

Utilitário: Folder Size for Windows

Este utilitário opensource mostra uma coluna a mais no Windows Explorer informando o tamanho das pastas - o que é muito útil quando temos que fazer aquela faxina no micro a fim de liberar um espaço. ;)

screenshot

 

 

 

 

 

 

 

 

 

Site do Folder Size

 

Dicas Úteis:

  • Para ver a coluna Folder Size, clique sobre a barra de colunas do Windows Explorer com o botão direito e marque "Folder Size".
  • Para o Folder Size poder escanear o tamanho das pastas, é necessário que elas tenham a permissão SYSTEM, que já vem por padrão quando o Windows é instalado. (Ou seja, só se preocupe com isso se você tiver tirado esta permissão das suas pastas, o que provavelmente fará com que a coluna Folder Size não esteja exibindo o tamanho).

terça-feira, 14 de novembro de 2006

Guerra silenciosa, sem vítimas

Com o lançamento quase simultâneo do Mozilla Firefox e do Internet Explorer 7, esquentou a discussão sobre a preferência dos browsers em todo o mundo. O fato foi ainda evidenciado pelo envio de um bolo (bolo mesmo, como o de aniversário) pela equipe de desenvolvimento do IE7 para a equipe do FF, no qual o motivo foi o retorno do interesse da equipe em desenvolver o browser (que estava claramente estagnado por falta de estímulo, como um browser que ameaçasse seu posto...).

Quase que indiscutivelmente, hoje o Firefox apresenta uma gama maior de funcionalidades, pelo uso de seus extensões (plug-ins) e tem ganhado cada vez mais adeptos, mesmo por usuários não-técnicos.

Um grande debate que ainda ocorre é a falta de padrão do IE7 em relação ao DOM (Document Object Model). Ou melhor, não exatamente a falta, mas é que a Microsoft segue seu próprio padrão ao invés do da W3C (Word Wide Web Consortium, uma espécie de comitê que estabelece padrões para a Internet). Isso faz com que as páginas da internet carreguem diferentemente nos dois browsers, o que enlouquece a vida dos webdesigners.

Os "cabeças" do desenvolvimento dos dois browsers concederam entrevista ao Slashdot. Fique por dentro do que vem por aí:

Entrevista com Dean Hachamovitch, líder do IE Team

Entrevista com Chris Beard, líder do FF Team

domingo, 12 de novembro de 2006

De volta, com o WLW

Espero estar podendo voltar a postar regularmente, agora sem a complicação editor de conteúdo do Blogger...

Este é o primeiro post que faço usando o Windows Live Writer e ele me parece muito bom. Leve, simples e eficaz. Resolve as dores de cabeça das transições "quebradas" entre HTML e texto que o editor do Blogger apresenta e dá mais opções - como inserir tags e mapas - além de estar integrado a serviços de sites populares.

Enfim, de volta a ativa...

quarta-feira, 9 de agosto de 2006

Dica C++: Destrutor virtual abstrato

Em uma classe base, métodos virtuais são aqueles que podem ter sua implementação sobrescrita por uma classe filha.
Isto é uma verdade para métodos, mas não para destrutores. A implementação de um destrutor virtual em uma classe filha não sobrescreve a implementação da classe base. Ela estende.
Primeiro é executado o código do destrutor da classe filha, depois o código do destrutor da classe base.

Exemplo:


class Base
{
public:
virtual ~Base()
{
cout<< "\nDestrutor da classe base." << endl;
};

virtual void Metodo()
{
cout << "\nMétodo da classe base." << endl;
};
};

class Derivada : public Base
{
public:
virtual ~Derivada()
{
cout<< "\nDestrutor da classe derivada." << endl;
};

virtual void Metodo()
{
cout << "\nMétodo da classe derivada." << endl;
};
};

// ...

int main()
{
Base *obj = new Derivada;

obj->Metodo();

delete obj;
}


No exemplo acima, se um destrutor virtual não fosse declarado, o destrutor da classe filha não seria executado.

O destrutor virtual da classe filha extender o código do destrutor da classe base,claro, tem sua razão de ser. Se não fosse esse mecanismo, por exemplo, para uma classe base que tem objetos que são desalocados no momento de sua destruição, a classe filha teria que fazer todo o trabalho novamente...

E quando desejarmos criar uma classe puramente abstrata, ou seja, uma interface ?
O destrutor dela provavelmente teria de ser virtual, mas sendo abstrata não poderia ter código. Bom, e como não vai ter código se há a necessidade de executar o código de destruição, pelo destrutor ser virtual ???

Simples, declare um destrutor virtual abstrato com implementação.
Mas se é abstrato como terá implementação ?¿?
Bom, infelizmente C++ não foi bem planejado nesta parte... :(

No arquivo .h, declare normalmente:


class BasePura
{
public:
virtual ~BasePura() = 0; // destrutor virtual abstrato

virtual void Metodo() = 0;
};


No arquivo .cpp (implementação), implemente o destrutor:


BasePura::~BasePura()
{
// implementacao normalmente vazia
}


Esta é uma das partes mal planejadas do C++, que devem ser corrigidas futuramente
pelos novos padrões da linguagem, definidas pelo ISO C++ Standards Commitee.

terça-feira, 16 de maio de 2006

Separação em camadas - parte 1 (MVC)

Introdução

A idéia geral dos modelos de separação do software em camadas é a de dar você a possibilidade de variar as camadas independentemente e isso gerar pouco ou nenhum impacto nas demais.

Para conseguir isso, cada camada é projetada de forma que nenhuma saiba muito sobre o funcionamento interno da outra. A maioria só conhece o protocolo pelo qual vai se comunicar com a camada associada.

Não conhecendo detalhes sobre a implementação das camadas associadas, uma camada comunica-se sem necessitar entender o funcionamento das demais. Por isso o nível de abstração do modelo aumenta e você pode facilmente variar a implementação de cada parte (aplicando reutilização do modelo através da combinação de herança e polimorfismo).


MVC

O Model-View-Controller é um padrão criado inicialmente para ser utilizado no sistema de interface gráfica do SmalltalkTM (Goldberg e Robson, 1983), possuindo as seguintes metas:

  • Criar um conjunto de componentes que suporte um processo de desenvolvimento de software de alta interatividade.
  • Prover um conjunto de componentes que torne possível para programadores criar facilmente aplicações gráficas portáveis.


Antes do MVC, os projetos de interface com o usuário tendiam a agrupar o comportamento da interface e a lógica do problema no mesmo contexto, digamos numa mesma classe. O MVC separa cada parte numa classe diferente, a fim de aumentar a flexibilidade e a reutilização.

O MVC propõe a separação do modelo de interação com a interface com o usuário em três partes: uma Visão (View), um Modelo (Model) e uma Controladora (Controller). A Visão representa a interface com o usuário, a representação interativa do software. O Modelo representa o domínio da aplicação, a lógica sob a qual a Visão opera, transforma e obtém informações. A Controladora é um mediador entre o Modelo e a Visão, que tem como objetivo controlar a interação entre ambos, esconder os detalhes e diminuir a dependência entre as mesmas.

O funcionamento do modelo se dá da seguinte maneira:

1. O usuário executa alguma ação de entrada na interface, ou melhor, na Visão.

2. A Visão notifica a Controladora para mudar o Modelo de acordo com a ação executada.

3. A Controladora notifica o Modelo sobre a ação.

4. O Modelo recebe a notificação da Controladora, possivelmente alterando seu estado, e notifica indiretamente a todos que necessitam saber que seu estado se alterou.


A notificação indireta é feita registrando-se no Modelo todos os objetos que necessitam saber sobre sua atualização. No entanto, o Modelo não sabe nada a respeito dos objetos que ele notifica, a não ser que eles possuem um método comum para atualização (possivelmente se chamando Atualizar ou outro nome conveniente). Quando o Modelo precisa notificar outros objetos sobre sua mudança ele apenas chama este método, que envia uma mensagem para todos os objetos registrados (broadcast).

Assim sendo, os objetos que são notificados de atualizações no Modelo implementam um método comum (Atualizar), e por terem o mesmo comportamento, implementam (herdar de) uma mesma interface.

Quais podem ser os objetos notificados ? Eles podem ser as Controladoras ou mesmo as Visões. (Em breve, possivelmente em outro post, estarei discutindo problemas e melhorias relacionadas, além de outros modelos).

Nota sobre uso de Padrões:

O comportamento acima descrito sobre a notificação de objetos foi catalogado como um Padrão de Projeto (Design Pattern) sobre o nome Observador (Observer). A idéia é que as classes que são notificadas sobre as atualizações são as “observadoras” e as que enviam a notificação são as “observadas”.



Caso você também tenha notado, o papel da classe Controladora do modelo MVC pode ser descrita também como um Padrão, catalogado sob o nome de Mediador (Mediator). No padrão Mediador, um objeto encapsula como um conjunto de objetos interage. Ele promove o acoplamento fraco para evitar que os objetos se refiram explicitamente uns aos outros, permitindo que eles variem independentemente.

Sendo assim, vamos analisar um exemplo simples, para retratar o modelo MVC:

Um usuário deseja conceder desconto em uma venda que ele está realizando. Quando o novo desconto é informado, o total da venda é recalculado e atualizado na tela, refletindo a mudança.
Para isso, teremos uma janela de venda (GUI_Venda) e um classe de venda (Venda).
Venda é uma classe (filha de) Observada
e GUI_Venda é (filha de) uma classe Observadora.

Vamos nos ater ao problema proposto:
Ao se conceder um desconto à venda, ela recalcula seu total. É desejado entretanto que ao recalcular seu total, a interface com o usuário exiba o novo total recalculado.

Tomando-se as devidas abstrações, o modelo seria parecido com este:


Ao recalcular seu total (método RecalcularTotal), venda notifica seus observadores (veja diagrama estrutural do padrão Observador), fazendo com que eles se atualizem com o total da venda. Isso acontece sem que Venda saiba quem está se atualizando. Ela sabe apenas que há observadores que serão atualizados.

Essa é a idéia por trás do padrão Observador, e é a base do MVC.

A atualização via notificação indireta é uma poderosa ferramenta para nos auxiliar no desenvolvimento de diferentes interfaces para o mesmo modelo. Isso porque a informação permanece sempre sincronizada com o modelo. Ao alterarmos o modelo, essa alteração se reflete em todas as interfaces.

Em um próximo post estarei discutindo mais sobre o modelo MVC, alguns padrões relacionados e alguns outros modelos, como o Model-View-Presenter (MVP).

Até breve.

domingo, 30 de abril de 2006

Baixando "na unha" vídeos do Google Video

A cada dia vídeos e mais vídeos são adicionados no Google Video e é cada vez mais freqüente o surgimento de players FLV para exibí-los. Apesar do site fazer streaming do vídeo, impedindo de baixá-lo da forma usual, há algumas formas de baixá-lo diretamente, utilizando-se de alguns pequenos passos.
Se você quiser ser poupado do trabalho, recomendo baixar o Google Video Downloader, ou outro programa que já faz o trabalho por você (Obs: O Google Video Downloader requer o Microsoft.NET Framework 1.1 - com 23.1 MB ).

Se quiser tentar a outra forma, aqui vai:

1- Abra o vídeo no Google Video da forma usual
2- Quando a página for carregada e o vídeo começar a ser carregado, abra o código fonte da página (no Firefox, clique com o botão direito na página e vá em "Código-fonte". No Internet Explorer, o procedimento é similar)
3- Localize o texto googleplayer.swf?videoURL
4- Um pouco a frente, está o texto http
5- Copie desde http até true (que está um pouco a frente de autoPlay)
6- Cole o texto num editor de texto qualquer
7- Substitua todos os seguintes trechos (ex: usando Editar -> Substituir no Notepad e clicando em Substiuir Tudo)
  • substitua %3A por :
  • substitua %2F por /
  • substitua %3F por ?
  • substitua %3D por =
  • substitua \u003d por =
  • substitua %26 por &

8- Copie e cole o texto, ou melhor, a nova URL no seu browser. Um arquivo .FLV vai aparecer pra ser baixado. Pronto.

Para o vídeo ser visualizado, use algum
"FLV player" disponível (há diversas opções gratuitas).
Prepare a pipoca e assista seus vídeos prediletos sem ter que fazer streaming. ;)


Explicação mais... "técnica":
O passo 7 se baseia na substituição dos códigos Unicode daURL em sua forma normal. Cada código possui o símbolo
% mais o código hexadecimal ASCII do símbolo, que pode ser obtido no AsciiTable.com. (Traduzindo, se você encontrar mais % no texto, vá no site AsciiTable.com e veja o símbolo referente ao texto. Por exemplo, %24 equivalerá à $, pois 24 é o número hexadecimal referente ao símbolo $).

domingo, 26 de março de 2006

Aprendendo com os erros alheios

A Engenharia de Software, diferente de outos setores da engenharia, faz pouquíssimo uso de casos de sucesso e fracasso do passado. Isso é realmente lamentável. Em arquitetura, por exemplo, poderíamos estudar os desenhos de Oscar Niemeyer, Louis Sullivan, Frank Lloyd Wright, I. M. Pei, dentre outros, e talvez até visitar alguns de seus prédios.

Uma parte de qualquer ciência amadurecida é um conjunto de problemas resolvidos, comumente reconhecidos como exemplos de bons trabalhos no setor e que servem como exemplos para trabalhos futuros (Thomas Kuhn, 1996).

Infelizmente a ES está apenas começando a amadurecer neste sentido...

Se você, como eu, gosta de analisar e refletir sobre problemas e soluções do passado, algumas providas por casos de fracasso em projetos de software, aqui vão alguns links interessantes:

Casos de Fracasso:

The Mythical Man-Month (análise sobre o projeto do OS/360, da IBM)
Daikatana (game assinado por John Romero e
Hiro Miyamoto - Ion Storms Inc e Eidos Interactive)
Duke Nukem Forever (game da 3D Realms)

veja mais aqui.

Nota: Infelizmente, a maioria dos casos de sucesso que encontrei não trazia detalhes ou maiores discussões sobre a experiência vivida. Optei então por não apresentar seus links aqui.

sexta-feira, 24 de março de 2006

Paralelo sobre TDD

Há 150 anos atrás, os médicos acreditavam que espíritos do mal causavam doenças. Então a anestesia dava aos médicos mais tempo para realizar cirurgias. Esses médicos tipicamente lavavam suas mãos somente após operar - para limpar o sangue. Os pacientes frequentemente pegavam infecções pós-operatórias. "A operação era um sucesso mas o paciente morria."

Por volta de 1850, o Dr. Ignaz Philippe Semmelweiss experimentou lavar as mãos antes de ajudar as mulheres a dar a luz, ao invés de somente depois. Ele relatou uma baixa taxa de mortalidade, mas os médicos ainda tinham que aprender melhores sistemas para relatar e valorar experimentos e estatísticas, e Semmelweiss não usava um método científico para anunciar os resultados.

Então Louis Pasteur e Robert Koch espalharam uma teoria que animais pequeninos comiam nossos corpos por dentro. Esse modelo físico explicou os experimentos, voltados para antecipar a pesquisa com microscópios. Médicos como Scot Joseph Lister adicionaram soluções anti-sépticas aos seus tratamentos. Contudo, muitos médicos ainda se recusaram a lavar ou esterilizar seus instrumentos – mesmo assim esses procedimentos obviamente não puderam prejudicar os pacientes. Doutores em liderança e ensino de regras, como Pierre Pachet, o professor de Fisiologia em Toulouse, por volta de 1872, declamaria que, “A teoria de germes de Louis Pasteur é uma ficção ridícula.”

Hoje nós reconhecemos corpos saldáveis contendo milhões de criaturinhas vivendo em harmonia de cooperação e alvoroço. Os médicos diminuíram o risco de transtornar esse balanço começando a esfregar uma longa lista de coisas, antes mesmo de operações de menor importância, usando sabão e adotando alguns procedimentos que são agora obrigatórios por lei.

A indústria de programação vive momentos interessantes. Enquanto muitos engenheiros ainda acreditam que falta de planejamento de projeto causa bugs, eles não tem uma razão concebível para não escrever código de teste antes de testar o código. O que certamente poderia não machucar o paciente. Evidências nesse campo irão eventualmente encerrar o debate.

(Texto traduzido de Test First User Interfaces: Developing Agile GUI Code, capítulo 16 página 195, fornecido do grupo de discussão Test-First User Interfaces (TFUI) em http://groups.yahoo.com/group/testfirstuserinterfaces/ do qual atualmente faço parte).

Há cada vez mais relatos de empresas que implantaram métodologias Ágeis, Programação Extrema, ou metodologias com Desenvolvimento Dirigido a Testes . A grande maioria relata um significativo salto qualitativo no processo de desenvolvimento e, principalmente, no produto final.
Emergindo de práticas simples e sem burocracia, elas estão cada vez mais sendo usadas por equipes de projeto preocupadas com a confiabilidade e com a diminuição dos custos de manutenção do software.

Em próximos posts deverei discutir alguns aspectos relacionados ao desenvolvimento de software, alguns deles possivelmente relacionados a metodologias que trazem grandes melhorias qualitativas e aumentam o retorno do investimento empregado em seu desenvolvimento.

Até o próximo ! ;-)

sábado, 18 de março de 2006

"Hello World" (my first post)

So, after some time of thinking about it, I've decided to start a blog.
What will I talk about here? Who knows - only time will tell.

I still don't know if all my posts would be in English, 'cause my native language is Brazillian Portuguese. Anyway, I will try to make some of them available in both languages.

Stay arround. I'll try to post new stuff fairly regularly.