terça-feira, 14 de outubro de 2008

Poster C#

Segue no link abaixo um pdf que contém todos os keybindings do C# do Visual Studio 2008.

Muito bom! Vale a pena dar uma lida!

http://www.microsoft.com/downloads/details.aspx?familyid=E5F902A8-5BB5-4CC6-907E-472809749973&displaylang=en

sábado, 4 de outubro de 2008

Princípio da Segregação de Interface

O princípio da segregação de interfaces ajuda a resolver o problema das interfaces “gordas”, que são classes cuja interface não possui coesão com o modelo tornado as interfaces poluídas e disponibilizando métodos desnecessariamente ao cliente.

Os clientes não devem ser forçados a depender de métodos que eles não utilizam, ou seja, quando um cliente 1 depende de uma classe que contém métodos que o mesmo não utiliza, porém o cliente 2 os utiliza, o cliente 1 será afetado pelas mudanças que o cliente 2 necessite fazer na classe. Para evitar este tipo de acoplamento é que devemos separar as interfaces.

Como exemplo, considere as interfaces de um terminal de auto atendimento bancário. A interface do usuário deve ser bem flexível, possibilitando que a saída seja feita de diversas formas como na tela do equipamento ou falada por um sintetizador de voz. Para esta parte, a flexibilidade pode ser obtida através de um classe abstrata que possui todos os métodos necessários para que os dados sejam apresentados pela interface.

Consideremos também que cada transação efetuada no terminal é derivada de uma classe chamada “Transação”, logo possuímos as classes “Deposito”, “Saque” e “Transferencia”. Cada classe deve utilizar os métodos da interface com o usuário. 

A princípio possuímos um modelo como demonstra a ilustração 1.

Ilustração 1 – Hierarquia da Transação

Ao analisar melhor o modelo da ilustração 1 observamos que cada transação está sendo forçada a depender de métodos não interessantes às mesmas. Nesta situação, se alguma transação for modificada, todas as outras transações serão afetadas além das classes que dependem da interface IU. O pior acontecerá ao tentar reutilizar estas classes, pois é necessário levar junto muita coisa que não interessa. Tudo isto exala odores como rigidez, fragilidade e viscosidade.

Esses problemas observados podem ser evitados segregando a interface IU em partes individuais como “IUDeposito”, “IUSaque” e “IUTransferencia” como demonstra a ilustração 2.


Ilustração 2 – Interface IU segregada

Portanto os clientes devem depender apenas dos métodos que eles irão utilizar. Este objetivo pode ser alcançado quebrando a interface da classe poluída em interfaces específicas. Com isso é quebrado a dependência dos clientes de métodos que eles não utilizam criando uma independência mútua entre os clientes.

Princípio da inversão de dependências

Os métodos de desenvolvimento de software mais tradicionais tendem a criar estruturas onde módulos de alto nível dependem de módulos de baixo nível e onde política de negócios depende de detalhes.

Este tipo de dependência é prejudicial pois geralmente módulos de alto nível contém o modelo de negócios da aplicação. Se módulos como estes dependerem de módulos de baixo nível, mudanças efetuadas nos módulos de baixo nível podem afetar diretamente os módulos de alto nível forçando estes a mudar.

Os módulos de alto nível devem ser isolados de módulos com menor importância no modelo de negócios. Sem o isolamento, torna-se impossível o reuso dos modelos de negócios implementados nos módulos de alto nível.

Segundo Booch, “todas as arquiteturas orientadas a objetos bem estruturadas possuem camadas claramente definidas, sendo que cada camada disponibiliza um pacote coerente de serviços através de uma interface definida.”. Podemos utilizar como exemplo a estrutura representada na ilustração 1:

Ilustração 1 – Exemplo de um modelo em camadas

Apesar de aparentar correto, o modelo demonstra que a camada de política é sensível a mudanças em todas as camadas inferiores uma vez que dependência é transitiva, ou seja, a camada de política depende da camada de controle e também da camada de utilidades.

Para aplicar o principio da inversão de dependências, devemos ter como base as seguintes teorias:
• Módulos de alto nível não devem depender em módulos de baixo nível. Ambos devem depender de abstrações.
• Abstrações não devem depender de detalhes. Detalhes devem depender de abstrações. 

A ilustração 2 demonstra um modelo mais apropriado. Cada camada superior declara uma interface abstrata para o serviço que ela necessita. Cada classe de alto nível utilizam as classes de baixo nível através de uma interface abstrata.


Ilustração 2 – Camadas invertidas

Na ilustração 2 pode-se observar que as camadas superiores não dependem das camadas inferiores. Ao invés disso, as camadas inferiores dependem das interfaces de serviços abstratos declarados nas camadas superiores. Com a aplicação do principio podemos observar que não a dependência transitiva entre as camadas foi quebrada, resolvendo os problemas citados anteriormente. 

Princípios da Substituição de Liskov

O princípio de substituição de Liskov, criado por Bárbara Liskov, determina que funções que utilizam referências a classes base devem estar aptas a utilizar objetos de classes derivadas sem nem ao menos conhecê-los.

A importância deste princípio é obvia quando são consideradas as conseqüências de violá-lo. Por exemplo, seja “F” uma função que recebe como parâmetro uma classe base “B”. Presumindo que ao passar um objeto “D”, de uma classe derivada de “B”, cause um comportamento errado em “F”. Logo, “D” viola o princípio de substituição de Liskov e o mesmo é considerado frágil quando utilizado por “F”. Para testar esta função, deverá ser criado um teste particular para “F” ao receber “D” como parâmetro. Este teste viola o princípio aberto/fechado porque “F” não é fechado a extensões de “B”.

Como exemplo de violação do princípio, considere a aplicação que utiliza um retângulo. A princípio essa aplicação funciona corretamente, porém o cliente decide que a aplicação deve também trabalhar com um quadrado. Um quadrado não é nada mais que um retângulo com lados iguais, logo a classe quadrado deve derivar de retângulo. Costuma-se interpretar esse tipo de relacionamento de herança entre duas classes como “é um” ou “é um tipo de”, neste caso, quadrado é um tipo de retângulo como mostra a ilustração 1.

Ilustração 1 – Quadrado herda de Retângulo

Este tipo de análise de relacionamento é visto algumas vezes como uma das técnicas fundamentais da análise da orientação a objetos. Porém este tipo de análise pode induzir a erros significantes que apenas serão notados na implementação.

Uma falha que pode ser apontada no exemplo é o fato de que um quadrado não necessita ter as propriedades Base e Altura, uma vez que todos os lados do quadrado são iguais. Estas propriedades herdadas não são apropriadas quando o objeto em questão é um quadrado, porém é possível contornar a situação sobrescrevendo os métodos “get” e “set” da Base e da Altura para que quando alguém atribuir um valor à base, o método atribui o mesmo valor à altura e vice-versa.

Agora considere uma função onde é passado como parâmetro um retângulo:

Função fx(Retângulo r)
{
    r.base = 5;
    r.altura = 4;
    se (r.Area() != 20)
        //Exibe Erro: Área incorreta;
}

A função utiliza os atributos base e altura de um suposto retângulo. A função funciona corretamente quando lhe é passado um retângulo mas dá errada se um quadrado for passado. A função “fx” é frágil se levarmos em consideração a hierarquia quadrado/retângulo. A função citada viola o principio de substituição de Liskov já que é possível existir uma função que receba um retângulo como parâmetro e não funcione corretamente se for passado um quadrado, por este motivo o quadrado não é substituível por um retângulo, ou seja, sua classe base.


quarta-feira, 1 de outubro de 2008

Princípio Aberto / Fechado (Open/Closed Principle - OCP)

Ao desenvolver um sistema, deve-se ter em mente que o sistema sofrerá várias mudanças no decorrer do seu ciclo de vida. Para ter um design suscetível a novas funcionalidades, podemos aplicar o princípio aberto/fechado definido por Bertrand Meyer que diz que as entidades do sistema (classes, módulos, funções, etc.) devem ser abertas a extensões, mas fechadas a modificações.

Aberto a extensões, pois o módulo deve estar preparado para suportar novos comportamentos e atender novos requisitos. Fechado a modificações, pois mesmo estendendo o módulo com novos comportamentos, o mesmo deve permanecer intacto, sem mudanças no código fonte. 

Este princípio atende diretamente o odor de rigidez de um software devido ao fato de que o mesmo torna-se maleável para que sejam implementadas novas funcionalidades apenas adicionando novas linhas de código sem ter que modificar as linhas de código já testadas.             

Para aplicar este princípio no design de um sistema, é necessário o uso de abstrações. As abstrações são classes-base que representam possíveis comportamentos cujas classes derivadas deverão possuir. 

Se um módulo é capaz de manipular uma abstração, o mesmo pode então ser fechado a modificações, desde que ele dependa apenas da abstração, e aberto a extensões criadas a partir de novas derivações da abstração. 

O exemplo a seguir ilustra o princípio aberto/fechado a partir de uma aplicação simples que tem por objetivo apenas desenhar formas geométricas na tela do usuário.   


Ilustração 1 – Sistema rígido  

A ilustração 1 mostra o design do sistema citado sem se importar com o princípio aberto/fechado. Observe que é um design rígido uma vez que o módulo de desenhar as formas está dependendo diretamente do tipo da forma que deve ser desenhada. Se futuramente tiver que ser adicionado outra forma, será necessário alterar o módulo para o mesmo atender esse novo requisito.   

Ilustração 2 – Sistema conforme o princípio aberto/fechado

A ilustração 2 mostra o mesmo sistema, porém aplicando o principio aberto/fechado. Note que se for necessário estender o comportamento da função “DesenharTodasFormas”, da classe “DesenharFormas”, para atender um novo tipo de forma, será necessário apenas criar uma nova classe derivada da classe “Forma”. Desse modo a classe “DesenharFormas” não sofrerá nenhuma mudança, logo o comportamento do módulo pode ser estendido sem sofrer mudanças.             

No novo design modelado, podemos identificar algumas características. O sistema não é frágil, pois mesmo depois de adicionar um novo comportamento a ele, é desnecessária a revisão do código para achar partes que foram afetadas pela alteração.             

O sistema também não é rígido já que para adicionar uma nova forma para ser desenhada é bem simples, sendo necessário apenas criar uma nova derivação e implementar suas funções.             

Por último, o sistema não é imóvel. A classe “DesenharFormas” pode ser reusada por qualquer outra aplicação sem a necessidade de levar as classes “Quadrado” e “Circulo” com ela. Portanto o novo design não é caracterizado por nenhuma das características de um mau design mencionadas.             

Concluindo, o principio aberto/fechado é um dos principais princípios do design orientado a objetos porque com ele atingimos os melhores benefícios da orientação a objetos: flexibilidade, reusabilidade e manutenibilidade.

Nomeando Classes, Estruturas, Interfaces, Métodos e Membros.

- Uma regra geral válida para esses identificadores é utilizar nomes de substantivos. Se não for possível utilizar esta regra, tem algo de errado e que precisa ser reformulado.  

- Utilize nomes simples e fáceis de serem compreendidos em tipos que serão utilizados com mais freqüência.   

- Se for possível, termine o nome do identificador com o nome de sua classe pai. Ex. ArgumentOutOfRangeException, é um tipo de Exception.   

- Interfaces devem possuir a letra “I” como prefixo. Ex. IComponent.   

- Para uma classe que é uma implementação padrão de uma interface, diferencie o nome da classe apenas retirando o prefixo “I”. Ex. class Component : IComponent   

- Para enumerações (Enums) não utilize o sufixo “Enum”. Ex. enum ColorEnum {...}   

- Para métodos, utilize nomes de verbos ou frases com verbos.   

- Uma propriedade deve ser nomeada com um substantivo ou um adjetivo.   

- Não utilize “Get” para propriedades.   

- Para propriedades cujo retorno é booleano, utilize afirmações no nome. Ex. EstáAtivo ao invés de NãoEstáAtivo.     

Aqui termina esta parte de “Naming Guidelines”. Como já foi dito, o nome dado aos seus identificadores pode fazer muita diferença e ajuda a evitar muitas dores de cabeça, além de deixar o código mais limpo e legível.

Obrigado!
Denis Yomura

terça-feira, 30 de setembro de 2008

Regras de nomeação de Namespaces

Namespaces são principalmente utilizadas para organizar suas implementações em uma hierarquia lógica e fácil de ser utilizada. Abaixo algumas diretrizes para dar nome às Namespaces:   

- Como os nomes dos identificadores, o nome da Namespace deve ser facilmente interpretado, ou seja, deve ser possível saber suas funcionalidades através do seu nome.   

- Se precisar de algum padrão, considere o seguinte:               {Empresa}.({Produto}|{Tecnologia}) [.{Características}] [.{Subnamespace}] – Ex. Microsoft.VisualStudio.Design   

- É uma boa prática sempre colocar o nome da empresa como prefixo para evitar confusões caso haja uma Namespace com o mesmo nome de outra empresa, por exemplo.   

- No segundo nível da Namespace, escolha algo imutável, ou seja, que não irá mudar a cada versão lançada. Lembre-se que a Namespace será algo que ficará fixo no código, sendo algo muito trabalhoso para ficar mudando a cada release do seu produto.   

- Não utilize nomes “virtuais” em suas Namespaces, por exemplo, o nome dado para a equipe que você está trabalhando em um determinado projeto. Esses nomes tendem a mudar dependendo da estrutura organizacional da empresa.   

- Sempre utilize o PascalCasing e separe os componente das namespaces utilizando o ponto ”.”.   

- Se for necessário e fizer sentido, pluralize. Essa regra não deve ser utilizada no caso de siglas. Ex. System.Collections e System.IO.   

- Não utilize nomes genéricos demais como Element, Node, Log e Message. A probabilidade de haver nomes iguais é muito grande. Utilize então FormElement, XmlNode, EventLog e SoapMessage.