quarta-feira, 6 de abril de 2011

Herança vs Composição

Gostaria de compartilhar o artigo publicado no site iMasters pelo colega Lucio Camilo, fazendo um comparativo interessante entre Herança e Composição.

Na verdade, esse artigo não é bem um comparativo, e sim uma "desqualificação" do uso da herança. Na minha opinião, não há solução ideal para todos os problemas, nem modelo ideal de arquitetura. Há sim uma solução específica para um problema específico.

Citando o velho exemplo do objeto Veículo, que pode ser especializado como Carro, Bicicleta, Carroça, Caminhão, é um caso claro onde a Herança se aplica muito bem. Mas volto a dizer, não sou defensor de arquitetura A, B ou C, defendo sim a avaliação de cada problema, e a aplicação da solução adequada para cada um, seja com Herança, Composição, Agregação, ou seja lá o que for.

Vamos ao artigo publicado:
Há algum tempo, herança era considerada a ferramenta básica de extensão e reuso de funcionalidade. Qualquer linguagem que não possuísse o conceito de herança “não servia para nada”. Atualmente, todos os artigos sobre padrões de projeto desaconselham a utilização de herança.

E agora?

Um princípio básico de padrões de projeto é “dar prioridade à composição”, preferir sempre “tem-um” ao invés de “é-um”. Quanto à herança, deve ser utilizada com muita prudência e em pouquíssimas situações.
Uma das poucas certezas que temos no desenvolvimento de aplicações é que existirão alterações. Portanto, a utilização de herança para fins de reutilização não dá tão certo quando se tratam de manutenções nos códigos já existentes.
Supondo que utilizamos uma classe pai para encapsular o comportamento de algum objeto. Dessa forma, todas as nossas classes filhas herdarão esses comportamentos. E o problema ocorre quando alguma das classes filhas não precisam de algum dos comportamentos que estão encapsulados. Passamos a ter que mudar o código na classe filha para que o método que foi herdado funcione da maneira específica para esse objeto. Ou, pior ainda, quando esse objeto não necessitar desse comportamento, então teríamos que sobrescrever o método para que ele não faça nada.
Alguns dos problemas dessa implementação é que o encapsulamento entre classes e subclasses é fraco, e o acoplamento é forte. Assim, toda vez que uma superclasse for alterada, todas as subclasses podem ser afetadas. Perceba que estamos violando o princípio básico de OO (Alta Coesão, Baixo Acoplamento).
Ainda com herança, a estrutura está presa ao código, e não pode sofrer alterações facilmente em tempo de execução, fazendo diminuir a capacidade de polimorfismo.
Quando utilizamos composição, instanciamos a classe que desejamos dentro de nosso código. Dessa forma, estamos estendendo as responsabilidades pela delegação de trabalho a outros objetos. Em vez de codificar um comportamento estaticamente, definimos pequenos comportamentos padrão e usamos composição para definir comportamentos mais complexos. Ainda na utilização de composição, podemos mudar a associação entre classes em tempo de execução e permitir que um objeto assuma mais de um comportamento.
Ao utilizar a composição, teremos muito mais flexibilidade, além de ser mais comum em muitos padrões de projetos. Porém, na herança, temos uma possibilidade de desenvolver mais rápido, diminuindo o tempo de desenvolvimento, levando em conta que perdemos muito mais tempo mantendo e alterando o código original do que com o desenvolvimento inicial. Portanto, nosso esforço deve ser sempre voltado para a reutilização e para a manutenção.

Aproveito para indicar um artigo interessante que conta com códigos mostrando o motivo de nunca se utilizar herança: veja aqui.
Fonte: iMasters

segunda-feira, 4 de abril de 2011

Algumas verdades sobre programação

Este artigo é a tradução de artigo escrito por David Veksler, no qual ele conta o que sua experiência como programador lhe ensinou.

O texto original pode ser encontrado aqui.

E vamos ao que interessa:
  • Um programador gasta cerca de 10% a 20% do seu tempo escrevendo código. Normalmente escreve entre 10 e 12 linhas por dia, que estarão presentes no produto final independentemente do seu nível de perícia ou experiência. Bons programadores gastam cerca de 90% do seu tempo pensando, pesquisando e experimentando maneiras de encontrar a solução ótima. Os programadores ruins gastam quase 90% do tempo debugando e fazendo alterações muitas vezes aleatórias na tentativa de “fazer funcionar”.
  • Um bom programador é dez vezes mais produtivo do que um programador comum. Um excelente programador é entre 20 e 100 vezes mais produtivo do que um convencional. Não é um exagero. Estudos desde os anos 60 têm mostrado isso consistentemente. Um mau programador não é só improdutivo - além de não concluir o trabalho com êxito, gera dores de cabeça e trabalho extra para outras pessoas consertarem.
  • Excelentes programadores gastam pouco do seu tempo escrevendo (código que de fato estará no resultado final). Os programadores que gastam muito do seu tempo escrevendo provavelmente não estão encontrando e utilizando soluções existentes para problemas antigos. Bons programadores são ótimos em reconhecer e em reutilizar padrões comuns e não têm medo de refatorar seu código constantemente, a fim de atingir a solução ótima. Programadores ruins escrevem código que falha em integridade conceitual, não-redundância, hierarquia e padrões, tornando complicada a refatoração, fazendo com que seja mais fácil jogar fora todo o trabalho e recomeçar.
  • Software, como qualquer coisa, obedece às leis da entropia. Contínuas mudanças levam ao desgaste do software e de sua integridade conceitual planejada originalmente. A entropia é inevitável, no entanto, programadores que falham ao estabelecer a integridade conceitual criam sistemas que se desgastam tão rapidamente, que muitas vezes se tornam inúteis e caóticos demais mesmo antes de serem concluídos. Possivelmente, o motivo mais comum para falha em projetos é o rompimento da integridade conceitual devido à entropia descontrolada (o segundo mais comum é a entrega de um produto diferente do que o cliente esperava). A entropia desacelera exponencialmente o desenvolvimento e é o principal motivo para deadlines desesperadoras.
  • Um estudo realizado em 2004 revelou que 51% dos projetos falham ou irão falhar em alguma funcionalidade importante e que 15% simplesmente vão falhar como um todo, o que é um grande avanço desde 1994, quando 31% dos projetos falhavam criticamente.
  • Embora muitos softwares sejam desenvolvidos em equipe, não se trata de uma atividade democrática. Geralmente somente uma pessoa é responsável pelo “design” do sistema e o resto do time o completa com detalhes.
  • Programar é um trabalho pesado. É uma atividade mental intensa. Bons programadores pensam sobre seu trabalho 24/7. Eles escrevem seu código mais importante no chuveiro, sonhando etc., porque o trabalho mais importante é feito longe do teclado. Projetos não são concluídos mais rapidamente gastando mais tempo no escritório ou adicionando pessoas novas ao projeto.
“Um excelente operário pode ser duas ou até três vezes mais produtivo que um operário comum, já um bom programador pode fazer com que seu trabalho seja mais do que 10 mil vezes mais produtivo do que um programador comum” - Bill Gates
Fonte: http://dotmac.rationalmind.net (Traduzido por iMasters)