[quote=pcalcado][quote=saoj]
De novo peço que se minha solução é errada ou spagetti que vc ensine para a gente como deveria ter sido o procedimento correto para alterar o método e resolver o problema. É muito fácil e deselegante falar que o código do outro é spagetti e não ter a humildade (ou coragem) de propor uma solução alternativa e melhor…
[/quote]
Se você lesse o que te indicam e não ficasse praguejando contra a teoria saberia a resposta. Polimorfismo, sobrescrita, etc.
[code]
class A{
public void fazAlgo(Com b){
if(b.numero>12121){fazUmaCoisa(b);}
else{fazOutraCoisa(b)};
}
public void fazAlgo(ComUmaCoisaQuePareceQueEhMasNaoEh b2){
//comprotamento variante
}
}
class EhUmAMasFazAlgoDiferente extends A{
public void fazAlgo(Com b){
//comportamento variante
}
}
[/code][/quote]
Claro!!! Sempre falei isso aqui e isso apenas reforça o meu ponto:
Boas práticas de OO (polimorfismo e sobrescrita) é apenas MAIS UM MOTIVO (na verdade o principal) para vc não precisar de testes e para que vc possa alterar uma coisa sem quebrá-la… Se vc está estendendo a classe A como nesse seu exemplo, porque diabos vc vai precisar testar A ??? Não faz o menor sentido testar A quando vc estende A e isso é uma grande sacada/vantagem de OO como todo mundo sabe (ou deveria) saber…
Só que no meu exemplo, vc não quer sobrescrever o método para alterá-lo APENAS para uma situação em particular. Vc quer alterá-lo para todas as situações, ou seja, vc quer que qualquer bolsa que já estava usando aquele método possa lhe enviar notação científica sem abendar o seu código.
Por isso pela opção pelo IF ao invés de polimorfismo/sobrescrita. (Sim, até um simples IF é perigoso, principalmente quando vc está desatento e se esquece de testar um NullPointerException! Logo por isso que mais abaixo eu digo que prefiro polimorfimo/sobrescrita a esse IF em prol da segurança… Mas qualquer um com atenção e disciplina consegue fazer um IF direito…)
E confesso que muitas vezes, em prol da segurança, prefiro polimorfismo/sobrescrita para que apenas aquela BOLSA seja afetada por novo código, e não todas as outras juntas. Não é a melhor solução, visto que se outras bolsas amanhã resolverem te mandar notação científica (improvável mas possível, principalmente pelas novas que entrarem no sistema) o seu código vai abendar, mas é a mais segura…
E não acredito de maneira nenhuma que o código com um IF para tratar uma situação particular seja spagetti code ou POG. O problema é quando vc usa IF quando deveria usar polimorfismo ou herança, e aí sim, temos um problema grave como colocaram aqui: http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
Eu acho ótimo quando tenho segurança de alterar alguma coisa sem quebrar. Só não entendo pq isso é um motivo pra eu não fazer um teste pra garantir que ninguém mais quebre sendo que:
:arrow: Eu não sou o único que mexe no código.
:arrow: As pessoas que eu trabalho erram, assim como eu também erro.
Por que software é uma coisa viva, o que hoje é certo amanha não é, precisa ser melhorado, modificado ou complementado.
Quanto a ser normal uma coisa que funcionava passar a não funcionar, eu mesmo várias vezes já fiz mudanças em projetos que já participei, que eu jurava que estavam correto, que faziam a mesma coisa que faziam antes, só que melhor e mais completa e quando rodava os testes descobria que não estava tão correto assim.
Você pode argumentar que eu não deveria ter mexido no que tava pronto, mas o fato de ter testes me dava total liberdade de ousar e tentar melhorar o que já existia com um bom nível de segurança e eu fui lá e melhorei e complementei o que já existia ao invés de duplicar funcionalidade no seu sistema.
Ok, argumento batido. Se você realmente defende o ponto de vista que OOP elimina a necessidade real de testes deveria escrever um livro, artigo, paper ou o que for para contrastar sua opinião com a das pessoas citadas em outros lugares, que possuem suas opiniões favoráveis a testes (são grandes evangelistas, na verdade).
Como essa sentença foi repetida diversas vezes sem novos argumentos (aliás, como foi repetido!) vou colar o trecho que escrevi nesta thread em 26/06:
Exclua deste todo mundo Martin Fowler, Eric Evans, Page-Jones, Uncle Bob Dave Thomas e todos os já citados nesta thread. Sobra quem?
Creio que o problema possa ser o fato de que seu design simplesmente não é OO. Se você tem objetos colaborativos cada objeto tem sua responsabilidade e seu comportamento, que é a forma com que ele se modifica (se move no espaço-estado).
Se você tem um objeto que recebia uma coisa (Diagmos uma instância de Dinheiro) e passa a receber outra (uma instância de NotacaoCientifica) você apenas cria outro método sobrecarregando o inicial. É extremamente simples e sem ifs, e como você pode ver pelo texto do Fowler que o Mauricio citou ifs no lugar de polimorfismo (o que voce fez no exemplo) ja é motivador suficiente para refactoring.
É esse o caso, Sérgio. Vamos tentar esquecer implicâncias e pormenores, do contrário este debate é inútil do ponto de vista de produção de conteúdo mas continua influenciando quem o ler, e influenciando negativamente.
Eu tenho uma classe X que se comporta da maneira Y. Duas mudanças possíveis:
1)A classe X agora deve se comportar da maneira Z ao invés da maneira Y:
Procedimento: Substitua a implementação de Y por Z
A classe X deve se comportar da maneira Y se receber A e da maneira Z se receber B
Procedimento: Sobrecarregue o método criando um que lide com a nova situação, separe o comportamento comum em outro método que é invocado pelas duas sobrecargas
No seu caso específico me parece que o problema é que o parâmetro que muda o comportamento tem o mesmo tipo. Se você tem:
public void metodo(String qualquerCoisa){
E depende do valor do parametro para entender o que fazer é uma simples questão de dispatching e parsing. Se você possui uma arquitetura de Camadas bem construída a de Apresentação, seja um Servlet, MDB, Socket ou o que for, já converterá esta mensagem de String para o objeto em questão, se sua aplicação não usa Camadas (parece ser o caso) você precisa fazer este parsing em algum lugar. No caso em específico vai até haver um if, mas ele está no lugar errado. O que eu faria:
/*
@deprecated Use os metodos de sobrecarga
*/
public void metodo(String qualquerCoisa){
if(ehUmaNotacaoCientifica(qualquerCoisa){
NotacaoCientifica nc = converteParaNotacao(qualquerCoisa);
metodo(nc);
}else{
Dinheiro d = converteParaDinheiro(qualquerCoisa);
metodo(d);
}
}
Novamente: Esta conversão deveria estar na Camada de Apresentação, não aqui.
Quando vc estende HttpServlet vc para para testar HttpServlet ???
Quando Fiho estende Pai, não há qualquer motivo para testar Pai, a não ser que vc faça um esforço sobre-humano para fazer cagada. E isso acho que até esses nomes bonitos aí vão concordar… Mas se tiver testes melhor ainda, quanto mais segurança melhor. Se tiver um cara para fazer code review em cada alteração que vc fizer melhor ainda… Se tiver um departamente de QA que a cada novo release vai testar tudo na mão, funcionalidade por funcionalidade, melhor ainda! Só que no mundo real, não é isso que acontece… Vc precisa produzir mais com menos e é nessa hora que disciplina, organização, boas práticas, atenção, concentração vão ser mais importantes que qualquer outra coisa…
Eu não coloquei IF em lugar nenhum. Eu apenas falei que dentro do método que vc está alterando vai haver um IF para fazer um branch, mas isso foi o suficiente para algumas pessoas sem pensar o entender taxassem a coisa de gambiarra, code smell, POG, etc.
Tanto faz esse IF levar para um novo bloco de código ou para uma outra função que recebe um ByteBuffer e que, como o parametro é o mesmo, ela terá que ter outro nome, tipo parseExecutedQtyNC(ByteBuffer), o que é muito ruim para a API do seu sistema, a não ser que esse método seja feito private/protected.
Dessa vez seu exemplo foi ruim. Você não precisa testar a classe mãe se ela já foi testada. E você testa o que você desenvolveu. Bom senso é uma das qualidades mais importantes pra um bom programador. Exemplos como esse não ajudam na discussão.
Cara, na boa: você pdoe estar certo. Eu acho que não está, mas pode ser que esteja. Se você quer guardar para você tudo bem, mas se você quer discutir suas idéias, etc. é bom embasá-las em algo, por isso sugeri o artigo, paper, etc.
Ao adquirir software, por exemplo o framework de servlets, eu já suponho que meu fornecedor testou o sistema. Na verdade existe um grande teste chamado TCK especificamente para este caso.
Isso não evita a necessidade de testar o meu código. Ainda que o seu framework tivesse testes eu deveria testar o código que eu produzo.
A não ser que Pai seja de sua responsabilidade.
Pois é, Sérgio: não vai haver um if. Vão haver dois métodos, um recebe banana e o outro laranja. Como você infelizmente já tinha um método que recebe fruta você precisa que ele descubra de qual fruta falamos, mas isso é uma problema de projeto que deve ser consertado.
Dessa vez seu exemplo foi ruim. Você não precisa testar a classe mãe se ela já foi testada. E você testa o que você desenvolveu. Bom senso é uma das qualidades mais importantes pra um bom programador. Exemplos como esse não ajudam na discussão.
[]'s
Rodrigo Auler
[/quote]
Ajudam sim, pois é exatamente isso que vc faz no mundo real. É exatamente isso que vc faz quando vc cria novas funcionalidades para um framework web por exemplo, ou quando vc altera um sistema assim por exemplo:
// codigo figurativo...
public abstract class AbstractConnection implements Connection {
public void orderExecuted() {
// muito codigo aqui...
double qty = buf.getInt();
// muito codigo aqui...
}
}
Vamos alterar isso:
Primeiro preparamos a classe para sofrer um override.
// codigo figurativo...
public abstract class AbstractConnection implements Connection {
public void orderExecuted() {
// muito codigo aqui...
double qty = getExecutedQty();
// muito codigo aqui...
}
protected double getExecutedQty() {
return buf.getInt();
}
}
E agora vamos estender a classe:
public class ConnectionWithNotacaoCientifica extends AbstractConnection {
@Override
protected double getExecutedQty() {
// pega quantidade de outra maneira aquil...
}
}
Vc até pode c onseguir quebrar a classe AbstractConnection quando vc faz isso, mas vc tem que fazer um esforço sobre-humano para conseguir tamanha façanha…
Dessa vez seu exemplo foi ruim. Você não precisa testar a classe mãe se ela já foi testada. E você testa o que você desenvolveu. Bom senso é uma das qualidades mais importantes pra um bom programador. Exemplos como esse não ajudam na discussão.
[]'s
Rodrigo Auler
[/quote]
Ajudam sim, pois é exatamente isso que vc faz no mundo real. É exatamente isso que vc faz quando vc cria novas funcionalidades para um framework web por exemplo…
[/quote]
Discordo de você. Veja só (retirado da Wikipedia):
“The goal of unit testing is to isolate each part of the program and show that the individual parts are correct.”
Você disse que acha que não precisa testar uma classe “Pai” se as classes “Filhas” estão sendo testadas. Isto não é correto. Os testes devem ser feitos isoladamente de forma que, se houver algum problema nas classes filhas, você saiba que o problema é na classe Filha (ou na classe Pai no caso inverso). Não há dúvidas de que todos os testes devem ser feitos isoladamente, inclusive de eventuais superclasses.
Se vc não altera e apenas estende uma classe PAI, vc não tem muitos motivos para ter que testar a classe pai, do mesmo jeito que vc não tem muitos motivos para testar HttpServlet quando vc estender HttpServlet…
Tem um tópico meu aí mais atrás onde eu falo da diferença de estender e modificar alguma coisa…
Acho que essa discussão já deu o que tinha que dar… Já deu pra fornecer bastante combustível para que agora cada um queime seus neurônios por si só e tirem as conclusões que desejarem…
Se vc não altera e apenas estende uma classe PAI, vc não tem muitos motivos para ter que testar a classe pai, do mesmo jeito que vc não tem muitos motivos para testar HttpServlet quando vc estender HttpServlet…[/quote]
Você não precisa testar HttpServlet porque alguém já testou ela pra você quando desenvolveu a Servlet API!
Aliás, esse exemplo do HttpServlet é muito bom.
Imagina se o pessoal da Sun pensasse: “Não, não vamos testar HttpServlet porque todo mundo no mundo todo irá testá-la quando desenvolver seus Servlets.”. Se fosse desta forma estaríamos perdidos.
Esse exemplo deixa claríssimo a necessidade de testar a superclasse isoladamente (a Sun não poderia fazer um release de uma Servlet API cheia de bugs) e você também não pode deixar de testar a sua classe isoladamente (você não precisa testar o que já funciona da superclasse, você testa a subclasse isoladamente).
[quote=pcalcado]O cliente não quer saber se foi feito em java, C# ou Pascal, desde que funcione e dê lucro.
O usuário de um framework não quer saber se foi feito usando X, Y ou Z, desde que funcione e ele consiga fazer seu trabalho.
E nenhum dos dois da a mínima se você não tem testes logo não consegue adaptar seu framework às mudanças necessárias porque não tem uma vírgula de confiança na base de código não quebrar
Se um software não tem testes, automatizados ou não, é porque ele não vale o esforço (é isso que o Carlos disse, mas cv-style).
Você compraria um carro que não foi testado? Veja bem, ele funciona, 100 pessoas já dirigiram eles, tem uma empresa que fatura 1 bilhão de euros a cada nanossegundo que tem uma frota deles mas… você poria sua família dentro dele?
Você usaria um carro sem cinto de segurança? Eu passo boa parte do meu tempo no trânsito, nunca sofri nenhum acidente sério. Cinto de segurança não consegue me dar nem 90% de garantia sequer de sobrevivência num acidente, isso me faz mais confiante para largar minha segurança?[/quote]
Gostei! Bela analogia, muito melhor do que as anteriormente apresentadas pelos outros participantes do debate!
Concordo contigo, só quis dizer que acho que se testes unitários tivessem sido utilizados provavelmente ou possívelmente a quantidade poderia ser menor… só isso.
Quanto a não garantir, bom, isso é complicado dizer, eu acredito que se feito com muito cuidado, mas muito cuidado mesmo, onde vc segue uma documentação bem feita e monta os testes de acordo, existe a chance de sair sem nada a fazer, mas pode acontecer casos em que isso falhe, é normal, afinal foi feito por humanos (assim espero… hehehehehe).
Quanto a confiabilidade, na minha opinião, aumenta sim.
No caso dos bugs, eu considero que é uma ótima oportunidade (mais que boa. rsss).
É um mundo muito cor-de-rosa esse q vc não mexe num framework, ainda mais em algo q é aberto, se vc tem uma funcionalidade não prevista, o ideal seria poder mexer no core mesmo, mas sem testes isso pode ser suicídio.
E pq alguem mexeria no core, dá para listar montes de situações, novas funcionalidades não previstas, suporte a nova versão da VM com mais recursos (por exemplo qdo apareceram os generics) q pode fazer diferença.
Se amanhã ou depois, sei lá, o desenvolvedor morre, sem testes como saber todos os contratos q o código dele obedecia tão primorosamente e como faze-los continuar funcionando, mágica? Só por isso testes já seriam uma ótima ferramenta.
Mas realmente não é a coisa mais importante do mundo, que nem documentação e comentários no código, rs são todas ferramentas, tire os comentários, testes, documentação, javadoc, desenhos no quadro branco, etc… Todas ferramentas válidas. Ainda hj tem muita gente q não coloca nenhuma linha de comentário nem faz documentação, dar manutenção num código desses meses depois de feito deve ser lindo, sem testes melhor ainda [ironia]. Quem sabe um dia a falta de testes seja tão mal vista como a falta dessas outras ferramentas, rs
[quote=Edufa]… Mas realmente não é a coisa mais importante do mundo, que nem documentação e comentários no código, rs são todas ferramentas, tire os comentários, testes, documentação, javadoc, desenhos no quadro branco, etc… Todas ferramentas válidas. Ainda hj tem muita gente q não coloca nenhuma linha de comentário nem faz documentação, dar manutenção num código desses meses depois de feito deve ser lindo, sem testes melhor ainda [ironia]. Quem sabe um dia a falta de testes seja tão mal vista como a falta dessas outras ferramentas, rs
CV escrevi mais de 3 parágrafos, rs … hehehehe[/quote]
Sem querer desvirtuar do assunto, se o seu código precisa de comentários e muita documentação para ser entendido, é um sintoma de que você precisa refatorá-lo. Eu sou a favor de comentar apenas as coisas que sejam super-ultra importantes. No resto você deve se obrigar a escrever código legível e semântico. Se ainda assim alguma não ficar clara o suficiente, certamente os testes preencherão o espaço que falta.
A documentação do seu sistema não deve ser documentação sobre código. Ela não deve ser frágil, isto é, se o sistema mudar (por exemplo, deixar de ser Java e passar a usar RoR) ainda assim a documentação deverá servir ou a necessidade de modificá-la deverá ser mínima.