Digamos que, por alguma razão, todos os objetos sejam criados dessa maneira $ obj = CLASS :: getInstance (). Em seguida, injetamos dependências usando setters e executamos a inicialização usando $ obj-> initInstance (); Existem problemas ou situações reais que não podem ser resolvidos se não usarmos construtores?
Ps a razão para criar um objeto desta maneira, é que podemos substituir a classe dentro de getInstance () de acordo com algumas regras.
Estou trabalhando em PHP, se isso importa
object-oriented-design
class-design
Axel Foley
fonte
fonte
Respostas:
Eu diria que isso dificulta bastante o seu espaço de design.
Os construtores são um ótimo local para inicializar e validar os parâmetros passados. Se você não puder mais usá-los para isso, a inicialização, o tratamento de estado (ou simplesmente negar o construtor de objetos "quebrados") se tornará muito mais difícil e parcialmente impossível.
Por exemplo, se todo
Foo
objeto precisar de umFrobnicator
, poderá verificar em seu construtor se o valorFrobnicator
é não nulo. Se você retirar o construtor, fica mais difícil verificar. Você verifica em todos os pontos em que seria usado? Em uminit()
método (externalizando efetivamente o método construtor)? Nunca verifique e espere o melhor?Embora você provavelmente ainda possa implementar tudo (afinal, você ainda está completo), algumas coisas serão muito mais difíceis de fazer.
Pessoalmente, sugiro investigar a injeção de dependência / inversão de controle . Essas técnicas também permitem alternar classes de implementação concretas, mas não impedem a gravação / uso de construtores.
fonte
HttpClient
, ele verifica se esse parâmetro é não nulo). E se essas restrições não forem atendidas, isso poderá gerar uma exceção. Isso não é realmente possível com a abordagem de construir e definir valores.init()
, o que é inteiramente possível - embora isso apenas imponha mais encargos de manutenção.2 vantagens para os construtores:
Os construtores permitem que as etapas de construção de um objeto sejam executadas atomicamente.
Eu poderia evitar um construtor e usar setters para tudo, mas e as propriedades obrigatórias, como Joachim Sauer sugeriu? Com um construtor, um objeto possui sua própria lógica de construção para garantir que não haja instâncias inválidas dessa classe .
Se a criação de uma instância
Foo
exigir 3 propriedades a serem configuradas, o construtor poderá fazer referência a todas as 3, validá-las e lançar uma exceção se forem inválidas.Encapsulamento
Ao confiar apenas em levantadores, o ônus é do consumidor de um objeto para construí-lo adequadamente. Pode haver diferentes combinações de propriedades válidas.
Por exemplo, toda
Foo
instância precisa de uma instância deBar
como propriedadebar
ou deBarFinder
como uma propriedadebarFinder
. Pode usar qualquer um. Você pode criar um construtor para cada conjunto de parâmetros válido e aplicar as convenções dessa maneira.A lógica e a semântica dos objetos vivem dentro do próprio objeto. É um bom encapsulamento.
fonte
Sim, você pode viver sem construtores.
Claro, você pode acabar com um monte de código duplicado da placa da caldeira. E se o seu aplicativo for de qualquer escala, você provavelmente gastará muito tempo tentando localizar a origem de um problema quando esse código padrão não for usado de forma consistente no aplicativo.
Mas não, você não precisa "estritamente" seus próprios construtores. Obviamente, você também não 'precisa' estritamente de classes e objetos.
Agora, se seu objetivo é usar algum tipo de padrão de fábrica para criação de objetos, isso não é mutuamente exclusivo para o uso de construtores ao inicializar seus objetos.
fonte
A vantagem de usar construtores é que eles facilitam a garantia de que você nunca terá um objeto inválido.
Um construtor oferece a oportunidade de definir todas as variáveis de membro do objeto para um estado válido. Quando você garante que nenhum método mutador pode alterar o objeto para um estado inválido, você nunca terá um objeto inválido, o que o salvará de muitos bugs.
Mas quando um novo objeto é criado em um estado inválido e você deve chamar alguns setters para colocá-lo em um estado válido no qual ele pode ser usado, você corre o risco de um consumidor da classe esquecer de chamá-los ou chamá-los incorretamente e você acaba com um objeto inválido.
Uma solução alternativa poderia ser criar objetos apenas através de um método de fábrica que verifica a validade de todos os objetos criados antes de devolvê-los ao chamador.
fonte
Eu acho que você está tornando isso mais difícil do que precisa ser. Podemos injetar dependências muito bem através do construtor - e se você tiver muitas delas, use uma estrutura semelhante a dicionário para poder especificar quais você deseja usar:
E dentro do construtor, você pode garantir consistência da seguinte forma:
$args
pode então ser usado para definir membros privados conforme necessário.Quando feito inteiramente dentro do construtor, nunca haverá um estado intermediário onde
$obj
exista, mas não seja inicializado, como ocorreria no sistema descrito na pergunta. É melhor evitar esses estados intermediários, porque você não pode garantir que o objeto sempre será usado corretamente.fonte
Na verdade, eu estava pensando em coisas semelhantes.
A pergunta que fiz foi "O que o construtor faz e é possível fazê-lo de maneira diferente?" Cheguei a essas conclusões:
Isso garante que algumas propriedades sejam inicializadas. Aceitando-os como parâmetros e definindo-os. Mas isso pode ser facilmente aplicado pelo compilador. Simplesmente anotando os campos ou propriedades como "obrigatório", o compilador verifica durante a criação da instância se tudo está definido corretamente. A chamada para criar a instância provavelmente seria a mesma, simplesmente não haveria nenhum método construtor.
Garante que as propriedades são válidas. Isso pode ser facilmente alcançado pela condição de afirmação. Novamente, você anotaria as propriedades com as condições corretas. Alguns idiomas já fazem isso.
Alguma lógica de construção mais complexa. Os padrões modernos não recomendam fazer isso no construtor, mas propõem o uso de métodos ou classes especializados de fábrica. Portanto, o uso de construtores neste caso é mínimo.
Então, para responder à sua pergunta: Sim, acredito que é possível. Mas isso exigiria algumas grandes mudanças no design da linguagem.
E acabei de notar que minha resposta é bonita OT.
fonte
Sim, você pode fazer quase tudo sem usar construtores, mas isso é claramente um desperdício de benefícios das linguagens de programação orientada a objetos.
Nas linguagens modernas (falarei aqui sobre C # em que programo), você pode limitar partes do código que podem ser executadas apenas em um construtor. Graças ao qual você pode evitar erros desajeitados. Uma dessas coisas é o modificador somente leitura :
Como recomendado anteriormente por Joachim Sauer, em vez de usar o
Factory
design patter, leia sobreDependency Injection
. Eu recomendaria ler Injeção de Dependência no .NET por Mark Seemann .fonte
Instanciar um objeto com um tipo, dependendo dos requisitos, é absolutamente possível. Pode ser o próprio objeto, usando variáveis globais do sistema para retornar um tipo específico.
No entanto, ter uma classe no código que pode ser "Todos" é o conceito de tipo dinâmico . Pessoalmente, acredito que essa abordagem crie inconsistência no seu código, torne os testes complexos * e "o futuro se torna incerto" com relação ao fluxo do trabalho proposto.
* Estou me referindo ao fato de que os testes devem considerar primeiro o tipo, depois o resultado que você deseja alcançar. Então você está criando um grande teste aninhado.
fonte
Para equilibrar algumas das outras respostas, reivindicando:
e
Tais declarações às vezes implicam a suposição de que:
Mas isso não é bem verdade. A maioria dos idiomas não possui uma regra contra um construtor que passa
this
(self
ou o que o idioma chama) para código externo. Esse construtor cumpre completamente a regra mencionada acima e ainda corre o risco de expor objetos semi-construídos a códigos externos. É um ponto menor, mas facilmente esquecido.fonte
Isso é algo anedótico, mas eu costumo reservar construtores para um estado essencial para que o objeto seja completo e utilizável. Exceto qualquer injeção de setter, uma vez executado o construtor, meu objeto deve ser capaz de executar as tarefas necessárias.
Qualquer coisa que possa ser adiada, deixo de fora do construtor (preparando valores de saída, etc.). Com essa abordagem, sinto que não estou usando construtores para nada, mas a injeção de dependência faz sentido.
Isso também tem o benefício adicional de conectar seu processo de design mental a não fazer nada prematuramente. Você não inicializará ou executará uma lógica que talvez nunca acabe sendo usada porque, na melhor das hipóteses, tudo o que você está fazendo é uma configuração básica para o trabalho futuro.
fonte