Descrevi a um colega por que um construtor que chama um método pode ser um antipadrão.
exemplo (no meu enferrujado C ++)
class C {
public :
C(int foo);
void setFoo(int foo);
private:
int foo;
}
C::C(int foo) {
setFoo(foo);
}
void C::setFoo(int foo) {
this->foo = foo
}
Gostaria de motivar melhor esse fato por meio de sua contribuição adicional. Se você tiver exemplos, referências de livros, páginas de blog ou nomes de princípios, eles serão muito bem-vindos.
Edit: Eu estou falando em geral, mas estamos codificando em python.
this
para qualquer um dos métodos que você chama do construtor.Respostas:
Você não especificou um idioma.
No C ++, um construtor deve tomar cuidado ao chamar uma função virtual, pois a função real que está chamando é a implementação da classe. Se for um método virtual puro sem uma implementação, isso será uma violação de acesso.
Um construtor pode chamar funções não virtuais.
Se sua linguagem é Java, em que as funções geralmente são virtuais por padrão, faz sentido que você precise ser extremamente cuidadoso.
O C # parece lidar com a situação da maneira que você esperaria: você pode chamar métodos virtuais em construtores e chama a versão mais final. Portanto, em C # não é um anti-padrão.
Um motivo comum para chamar métodos de construtores é que você possui vários construtores que desejam chamar um método "init" comum.
Observe que os destruidores terão o mesmo problema com os métodos virtuais; portanto, você não pode ter um método de "limpeza" virtual que fica fora do seu destruidor e espere que ele seja chamado pelo destruidor de classe base.
Java e C # não têm destruidores, eles têm finalizadores. Eu não sei o comportamento com Java.
C # parece manipular a limpeza corretamente nesse sentido.
(Observe que, embora Java e C # possuam coleta de lixo, isso gerencia apenas a alocação de memória. Há outra limpeza que seu destruidor precisa fazer para não liberar memória).
fonte
OK, agora que a confusão sobre métodos de classe contra métodos de instância seja esclarecido, posso dar uma resposta :-)
O problema não é chamar métodos de instância em geral de um construtor; é com a chamada de métodos virtuais (direta ou indiretamente). E a principal razão é que, enquanto dentro do construtor, o objeto ainda não está totalmente construído . E, especialmente, suas partes de subclasse não são construídas enquanto o construtor da classe base está em execução. Portanto, seu estado interno é inconsistente de uma maneira dependente do idioma, e isso pode causar diferentes erros sutis em diferentes idiomas.
C ++ e C # já foram discutidos por outros. Em Java, o método virtual do tipo mais derivado será chamado, porém esse tipo ainda não foi inicializado. Portanto, se esse método estiver usando quaisquer campos do tipo derivado, esses campos ainda não poderão ser inicializados corretamente naquele momento. Esse problema é discutido em detalhes no Effecive Java 2nd Edition , Item 17: Design e documento para herança ou então a proíbe .
Observe que este é um caso especial do problema geral de publicar referências de objetos prematuramente . Os métodos de instância têm um
this
parâmetro implícito , mas a passagemthis
explícita para um método pode causar problemas semelhantes. Especialmente em programas simultâneos em que, se a referência do objeto for publicada prematuramente em outro encadeamento, esse encadeamento já poderá chamar métodos antes que o construtor no primeiro encadeamento seja concluído.fonte
Eu não consideraria as chamadas de método aqui um antipadrão em si, mais um cheiro de código. Se uma classe fornece um
reset
método, que retorna um objeto ao seu estado original, a chamadareset()
no construtor é DRY. (Não estou fazendo nenhuma declaração sobre métodos de redefinição).Aqui está um artigo que pode ajudar a satisfazer seu pedido de autoridade: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/
Não se trata realmente de chamar métodos, mas de construtores que fazem muito. IMHO, chamar métodos em um construtor é um cheiro que pode indicar que um construtor é muito pesado.
Isso está relacionado a como é fácil testar seu código. Os motivos incluem:
O teste de unidade envolve muita criação e destruição - portanto, a construção deve ser rápida.
Dependendo do que esses métodos fazem, pode ser difícil testar unidades de código discretas sem depender de alguma pré-condição (potencialmente não testável) configurada no construtor (por exemplo, obter informações de uma rede).
fonte
Filosoficamente, o objetivo do construtor é transformar um pedaço bruto de memória em uma instância. Enquanto o construtor está sendo executado, o objeto ainda não existe, portanto, chamar seus métodos é uma má idéia. Você pode não saber o que eles fazem internamente, afinal, e eles podem considerar com razão que o objeto existe pelo menos (duh!) Quando são chamados.
Tecnicamente, pode não haver nada de errado nisso, em C ++ e especialmente em Python, cabe a você tomar cuidado.
Praticamente, você deve limitar as chamadas apenas aos métodos que inicializam os membros da classe.
fonte
Não é uma questão de uso geral. É um problema no C ++, especificamente ao usar métodos virtuais e de herança, porque a construção do objeto ocorre ao contrário e o (s) ponteiro (s) da vtable são redefinidos com cada camada de construtor na hierarquia de herança, portanto, se você estiver chamando um método virtual, talvez não acabamos obtendo o que realmente corresponde à classe que você está tentando criar, o que anula todo o propósito de usar métodos virtuais.
Em idiomas com suporte a OOP, que definem o ponteiro vtable corretamente desde o início, esse problema não existe.
fonte
Há dois problemas ao chamar um método:
Não há nada errado em chamar uma função auxiliar, desde que ela não caia nos dois casos anteriores.
fonte
Eu não compro isso. Em um sistema orientado a objetos, chamar um método é praticamente a única coisa que você pode fazer. De fato, essa é mais ou menos a definição de "orientado a objetos". Portanto, se um construtor não pode chamar nenhum método, o que ele pode fazer?
fonte
Na teoria OOP, isso não deve importar, mas na prática, cada linguagem de programação OOP lida com os construtores de maneira diferente . Não uso métodos estáticos com muita frequência.
Em C ++ e Delphi, se eu precisasse fornecer valores iniciais para algumas propriedades ("membros do campo") e o código for muito extenso, adiciono alguns métodos secundários como extensão dos construtores.
E não chame outros métodos que fazem coisas mais complexas.
Quanto aos métodos "getters" e "setters" das propriedades, geralmente uso variáveis privadas / protegidas para armazenar seu estado, além dos métodos "getters" e "setters".
No construtor, atribuo valores "padrão" aos campos de estado das propriedades, SEM chamar os "acessadores".
fonte