Conformidade de tipo

terça-feira, 22 de setembro de 2009

Eu já falei sobre isso em algumas aulas mas sempre é válido ressaltar: herança é um acoplamento forte.

Acoplamentos fortes são indesejáveis, pois criam dependências pelas quais o impacto de uma mudança se propaga.
Uma herança entre duas classes traz várias implicações. Uma delas é dizer que a subclasse se comporta como a superclasse (Princípio de Substituição de Liskov*).

Assim, um pequeno polimorfismo de classe pode causar grandes transtornos se este relacionamento não for bem observado. Vou falar um pouco de covariância e contravariância.

Imaginem uma Superclasse e uma Subclasse, ambas com um método m declarado, sendo redefinido na subclasse. Este método tem um retorno e uma lista de parâmetros.

A covariância é a determinação de que o retorno da subclasse deve ser do mesmo tipo do retorno da superclasse ou de um tipo mais específico (subtipo) que este.

Então suponha a classe Object e a classe String herdando de Object.

O método m na Superclasse pode retornar Object. O método m redefinido na Subclasse pode retornar Object ou String (ou qualquer outro subtipo de Object).

Isto porque um código como

Object objeto = instancia.m();

deveria funcionar com

Superclasse instancia = new Superclasse();
ou
Superclasse instancia = new Subclasse();

Reparem que o código cliente (que usa as classes) assume que os retornos serão pelo menos do tipo declarado na Superclasse (ou mais específico) e, mais ainda, a variação deste retorno deve ser igual ou menor. Por exemplo, fosse o retorno um inteiro entre 0 e 10, a subclasse poderia retornar quaisquer números entre 0 e 10 (não necessariamente todos) mas nunca um número fora destes limites, pois o código cliente não pode estar preparado para lidar com situações que não estão previstas para a execução daquele método através de uma referência para a Superclasse.

Notem que para os objetos retornados, o ideal é que o espaço-estado* também seja igual ou menor.

Assim, conforme o tipo vai ficando mais específico (descendo na hierarquia), o retorno também vai ficando mais específico, configurando-se a covariância.

A contravariância é o mesmo conceito aplicado aos parâmetros. Ela é "contra" pois conforme se desce na hierarquia (ficando mais específico), os parâmetros vão aumentando os limites.

Se passo para o método m na superclasse um valor entre 0 e 5 e ele funciona, na subclasse, ele deve funcionar, no mínimo, com valores entre 0 e 5. Nenhum problema se ele funcionar também com 314.1592. O problema é se ele só funcionar com valores entre 0 e 3 ou não funcionar com 4 ou qualquer outra restrição. Devemos lembrar que a declaração
Superclasse instancia = new Subclasse();
é sempre válida e quem determina se uma classe funciona como deveria é o código cliente (novamente Princípio de Substituição de Liskov).

Vocês se preocupam com o funcionamento correto das heranças nas aplicações que fazem?

Abraço a todos!

*Prometo que no futuro falo um pouco do Princípio de Substituição de Liskov e de espaço-estado.

Bookmark and Share

Você comenta seu código?

segunda-feira, 7 de setembro de 2009

Para quem não sabe, sou monitor honorário da disciplina "Computação II" no Departamento de Ciência da Computação da UFRJ (http://www.dcc.ufrj.br/~comp2/professor.html).
Como monitor honorário eu ajudo os outros monitores nas tarefas que me agradam. ;-) Isso significa dar aulas de apoio e tirar dúvidas, principalmente.
Na última semana um aluno perguntou sobre a importância de comentar o código dos exercícios a serem corrigidos. Adaptei a resposta para a pergunta "É importante comentar?".
É MUITO importante que seu código seja compreensível, não só por você mesmo (agora ou no futuro), por pessoas que trabalham com você ou trabalharão num projeto em que você desenvolveu um dia.
Como regra, eu adoto a política de tentar fazer um código tão claro que dispense comentários. Só quando isto não é possível por uma idiossincrasia da vida que eu comento e tento ser o mais explicativo possível.

“There are two ways of constructing a software design; one way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.” Sir Charles Antony Richard Hoare

Reparem, por exemplo, neste trecho fictício:
if ((e1.comparaMaior(e2) || e1.vazio()) && (e2 != null)) {
    fazerAlgo();
}

em contraste com:
if (elemento1AtendeRestricaoComparandoElemento2(elemento1, elemento2)) {
    fazerAlgo();
}

boolean elemento1AtendeRestricaoComparandoElemento2(Elemento elemento1, Elemento elemento2) {
    return (elemento1.comparaMaior(elemento2) || elemento1.vazio()) && (elemento2 != null);
}


Notem que fica claro o que está sendo testado no if e fica clara também a intenção daquela comparação enorme graças ao nome método implementado em seguida. Não é necessário o comentário. Assim, evita-se, entre outras coisas, que o código seja atualizado, e o comentário esquecido, ficando desatualizado e informando uma condição diferente da que acontece, causando muitos transtornos.
Além disso, se a intenção é entender a finalidade do código, muitas vezes entender a comparação é desncessário.

Usar nomes de variáveis que signifiquem alguma coisa também é outra boa prática. Todas as boas IDEs completam os nomes quando digitamos os primeiros caracteres e pressionamos ctrl+space.
Notem que não poupei letras no nome do método. O importante é deixar claro e organizado.

Vale notar também que este código é agora mais testável, já que é possível testar apenas a comparação. ;-)

Uma situação válida para a utilização de comentários é quando entendemos algum código um pouco complicado, que demanda algum raciocínio de como o código funciona para entendermos seu objetivo. Neste caso, vale comentar o objetivo se não for possível (por restrições quaisquer) refatorá-lo para torná-lo adequado ao entendimento.

O Javadoc também é bem interessante, mas nele deve constar apenas "o que" o método faz (objetivo) e não "como" o método faz (lógica). Assim, a documentação se torna suscinta, simples e direta, atendendo aos objetivos de quem a lê. Quem quiser entender como irá ler o código e o deve encontrar devidamente organizado.

E vocês? O que comentam? Como comentam?

Abraço a todos!

Bookmark and Share

Um blog!

Então galera...
Muita gente tem me perguntado porque eu não tenho um blog sobre coisas relacionadas a Java ou orientação a objetos ou desenvolvimento de software em geral. A resposta é simples. Porque eu não tenho tempo pra ficar postando, atualizando. E ainda acho que isso é verdade.
De qualquer forma, vou fazer uma tentativa.
A princípio, este blog não é sobre Java. É sobre desenvolvimento de software em geral. Sendo de um assunto mais amplo, pode ser que eu tenha mais o que dizer. ;-) Também aumentam as chances de um assunto interessante aparecer por acaso e me animar para postar.
Enfim, pretendo abordar assuntos relacionados a qualidade de software em geral, de processos e metodologias a questões de design, boas práticas, padrões, etc.
É claro que como trabalho com Java, vez ou outra haverá aqui dicas de aplicações, componentes, frameworks e trechos de código interessantes. E os exemplos, em geral, provavelmente serão em Java quando envolverem código.

Vamos ver se esta empreitada segue em frente ou desisto depois de meia dúzia de posts.

Abraço a todos.

Bookmark and Share

 
addthis_config = { data_ga_tracker: pageTracker }