Eu ainda me considero um programador aprendiz, por isso estou sempre procurando aprender uma maneira "melhor" para a programação típica. Hoje, meu colega de trabalho argumentou que meu estilo de codificação faz algum trabalho desnecessário e quero ouvir opiniões de outras pessoas. Normalmente, quando eu desenvolvo uma classe na linguagem OOP (geralmente C ++ ou Python), separava a inicialização em duas partes diferentes:
class MyClass1 {
public:
Myclass1(type1 arg1, type2 arg2, type3 arg3);
initMyClass1();
private:
type1 param1;
type2 param2;
type3 param3;
type4 anotherParam1;
};
// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
: param1(arg1)
, param2(arg2)
, param3(arg3)
{}
// Any other procedure is done in a separate initialization function
MyClass1::initMyClass1() {
// Validate input arguments before calculations
if (checkInputs()) {
// Do some calculations here to figure out the value of anotherParam1
anotherParam1 = someCalculation();
} else {
printf("Something went wrong!\n");
ASSERT(FALSE)
}
}
(ou equivalente em python)
class MyClass1:
def __init__(self, arg1, arg2, arg3):
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
#optional
self.anotherParam1 = None
def initMyClass1():
if checkInputs():
anotherParam1 = someCalculation()
else:
raise "Something went wrong!"
Qual a sua opinião sobre essa abordagem? Devo me abster de dividir o processo de inicialização? A questão não se limita apenas a C ++ e Python, e respostas para outras linguagens também são apreciadas.
design
object-oriented
coding-style
Caladbolgll
fonte
fonte
Respostas:
Embora às vezes seja problemático, há muitas vantagens em inicializar tudo no construtor:
initMyClass1()
porque não está lá . "Os componentes mais baratos, rápidos e confiáveis são aqueles que não existem".fonte
Pense na abstração que você está fornecendo aos usuários.
Por que dividir algo que poderia ser feito de uma vez em duas?
A inicialização extra é apenas algo a mais para que os programadores que usam sua API se lembrem e fornece mais para dar errado se não fizerem o que é certo, mas qual é o valor para esse encargo extra?
Você deseja fornecer abstrações simples, fáceis de usar e difíceis de dar errado. Programar já é bastante difícil, sem coisas gratuitas para lembrar / bastidores para pular. Você deseja que os usuários da API (mesmo que você esteja usando sua própria API) caiam no poço do sucesso .
fonte
Inicialize tudo, exceto a área de big data. As ferramentas de análise estática sinalizarão campos não inicializados no construtor. No entanto, a maneira mais produtiva / segura é ter todas as variáveis de membro com construtores padrão e inicializar explicitamente apenas aquelas que requerem inicialização não padrão.
fonte
Há casos em que o objeto tem muita inicialização que pode ser dividida em duas categorias:
Atributos imutáveis ou que não precisam ser redefinidos.
Atributos que podem precisar reverter para valores originais (ou valores com modelos) com base em alguma condição após a conclusão do trabalho, como uma redefinição suave. por exemplo, conexões em um conjunto de conexões.
Aqui, a segunda parte da inicialização mantida em uma função separada, como InitialiseObject (), pode ser chamada no ctor.
A mesma função pode ser chamada posteriormente se uma redefinição suave for necessária, sem a necessidade de descartar e recriar o objeto.
fonte
Como outros já disseram, geralmente é uma boa ideia inicializar no construtor.
Existem, no entanto, razões para isso não se aplicar ou não em casos específicos.
Manipulação de erros
Em muitos idiomas, a única maneira de sinalizar um erro em um construtor é gerar uma exceção.
Se a sua inicialização tiver uma chance razoável de gerar um erro, por exemplo, envolve E / S ou seus parâmetros podem ser entrada do usuário, o único mecanismo aberto a você é gerar uma exceção. Em alguns casos, pode não ser o que você deseja e pode fazer mais sentido separar o código propenso a erros em uma função de inicialização separada.
Provavelmente, o exemplo mais comum disso é em C ++ se o padrão do projeto / organização for desativar as exceções.
Máquina de estado
Este é o caso em que você está modelando um objeto que possui transições de estado explícitas. Por exemplo, um arquivo ou um soquete que pode ser aberto e fechado.
Nesse caso, é comum que a construção do objeto (e exclusão) lide apenas com atributos orientados à memória (nome do arquivo, porta, etc.). Haverá funções para gerenciar especificamente as transições de estado, por exemplo, abrir, fechar, que são efetivamente funções de inicialização e desmontagem.
As vantagens estão no tratamento de erros, como acima, mas também pode haver um caso para separar a construção da inicialização (por exemplo, você constrói um vetor de arquivos e os abre de forma assíncrona).
A desvantagem, como já foi dito, é que agora você coloca o ônus da administração do estado no usuário de suas classes. Se você pudesse gerenciar apenas com a construção, poderia, por exemplo, usar o RAII para fazer isso automaticamente.
fonte