Evitando construtores com muitos argumentos

10

Então, eu tenho uma fábrica que cria objetos de diferentes classes. As classes possíveis são todas derivadas de um ancestral abstrato. A fábrica possui um arquivo de configuração (sintaxe JSON) e decide qual classe criar, dependendo da configuração do usuário.

Para conseguir isso, a fábrica usa boost :: property_tree para análise JSON. Ele percorre a árvore e decide qual objeto concreto criar.

No entanto, os objetos do produto têm muitos campos (atributos). Dependendo da classe concreta, o objeto possui cerca de 5 a 10 atributos, no futuro talvez até mais.

Portanto, não tenho certeza de como deve ser o construtor dos objetos. Eu posso pensar em duas soluções:

1) O construtor do produto espera que cada atributo seja um parâmetro; portanto, o construtor terá mais de 10 parâmetros. Isso será feio e resultará em linhas de código longas e ilegíveis. No entanto, a vantagem é que a fábrica pode analisar o JSON e chamar o construtor com os parâmetros corretos. A classe do produto não precisa saber que foi criada devido à configuração JSON. Não é necessário saber se há JSON ou configuração envolvida.

2) O construtor do produto espera apenas um argumento, o objeto property_tree. Em seguida, ele pode analisar as informações necessárias. Se houver informações ausentes ou fora dos limites, cada classe de produto poderá reagir adequadamente. A fábrica não precisa saber quais argumentos são necessários para os vários produtos. A fábrica também não precisa saber como reagir em caso de configuração incorreta. E a interface do construtor é unificada e pequena. Mas, como desvantagem, o produto precisa extrair as informações necessárias do JSON, portanto, sabe como é construído.

Eu tendem a preferir a solução 2). No entanto, não tenho certeza se esse é um bom padrão de fábrica. De alguma forma, parece errado informar o produto que ele foi criado com a configuração JSON. Por outro lado, novos produtos podem ser introduzidos de maneira muito simples.

Alguma opinião sobre isso?

lugge86
fonte
1
Eu segui o seu link. Há um exemplo na resposta mais bem avaliada de catraca. Mas que problema essa abordagem de "construtor" resolve? Existe a linha de código DataClass data = builder.createResult () ;. Mas o método createResults () - ainda precisa obter os 10 parâmetros no objeto DataClass. Mas como? Parece que você tem apenas mais uma camada de abstração, mas o construtor do DataClass não fica menor.
lugge86
Dê uma olhada no construtor e no protótipo.
Silviu Burcea
Silviu Burcea, eu fiz. No entanto, ao usar o construtor, como o construtor obtém os parâmetros no produto? Em algum lugar tem que haver uma interface gorda. O construtor é apenas mais uma camada, mas de alguma forma os parâmetros precisam encontrar seu caminho na classe do produto.
precisa
1
Se sua classe é muito grande, mudar os argumentos dos construtores não tornará muito grande .
Telastyn

Respostas:

10

Eu não faria a opção 2, porque você sempre convolveu a construção do seu objeto com a análise de árvore de propriedades boost. Se você se sente confortável com uma classe que precisa de muitos parâmetros, deve se sentir confortável com um construtor que precisa de muitos parâmetros, como a vida!

Se sua principal preocupação é a legibilidade do código, você pode usar o padrão do construtor, é basicamente o código c ++ / java por falta de argumentos nomeados. Você acaba com coisas assim:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

Então agora o MyObject terá um construtor privado, chamado em Builder :: build. O bom é que esse será o único lugar em que você precisará chamar um construtor com 10 parâmetros. A fábrica da árvore de propriedades boost usará o construtor e, subseqüentemente, se você desejar construir um MyObject diretamente ou de uma fonte diferente, passará pelo construtor. E o construtor basicamente permite que você nomeie claramente cada parâmetro à medida que o passa, para que fique mais legível. Obviamente, isso adiciona alguns clichês, então você terá que decidir se vale a pena em comparação com apenas chamar o construtor confuso ou agrupar alguns de seus parâmetros existentes em estruturas etc. Apenas jogando outra opção na mesa.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example

Nir Friedman
fonte
5

NÃO use a segunda abordagem.

Definitivamente, não é a solução e levaria apenas a instanciar classes na lógica de negócios, em vez da parte do aplicativo em que as fábricas estão.

Ou:

  • tente agrupar certos parâmetros que parecem representar coisas semelhantes em objetos
  • dividir a classe atual em várias classes menores (ter uma classe de serviço com 10 parâmetros parece que a classe faz muitas coisas)
  • deixe como está, se sua classe não for realmente um serviço, mas um objeto de valor

A menos que o objeto que você esteja criando seja realmente uma classe responsável pela retenção de dados, tente refatorar o código e dividir a classe grande em outras menores.

Andy
fonte
Bem, a lógica do bsiness está usando uma fábrica que retorna os produtos, assim, a lógica do negócio não vê o material JSON / ptree. Mas entendo o seu ponto de vista, ter o código do Analisador no construcotr parece errado.
lugge86
A classe está representando um widget em uma interface gráfica para um sistema embarcado, assim, 5+ atributos parece OK para mim: x_coord, Y_coord, backgroundcolor, framesize, framecolor, texto ...
lugge86
1
@ lugge86 Mesmo que você esteja usando uma fábrica para analisar o JSON e, assim, evitando chamar newou construir objetos dentro da lógica de negócios, não é um design muito bom. Verifique o item Não procure coisas por Miško Hevery , que explica mais detalhadamente por que a abordagem de fábrica sugerida é ruim do ponto de vista de teste e leitura. Além disso, sua classe parece ser um objeto de dados e, para aqueles, geralmente é bom ter mais parâmetros do que a classe de serviço regular. Eu não ficaria muito incomodado.
21415 Andy
Estou me sentindo bem com minha abordagem de fábrica, mas seguirei o seu link e pensarei nisso. No entanto, a fábrica não está em questão neste tópico. A questão ainda é como configurar os produtos ...
lugge86
"ter uma classe de serviço com 10 parâmetros parece que a classe faz muitas coisas", não no aprendizado de máquina. Qualquer algoritmo ML teria toneladas de parâmetros ajustáveis. Gostaria de saber qual é a maneira correta de lidar com isso ao codificar ML.
Siyuan Ren
0

A opção 2 está quase certa.

Uma opção melhorada 2

Crie uma classe "de frente", cuja tarefa é pegar esse objeto de estrutura JSON e selecionar os bits e chamar o (s) construtor (es) da fábrica. É preciso o que a fábrica faz e o entrega ao cliente.

  • A fábrica não tem absolutamente nenhuma idéia de que tal coisa JSON exista.
  • O cliente não precisa saber de quais bits específicos a fábrica precisa.

Basicamente, o "front end" está dizendo aos 2 Bobs: "Eu lido com os clientes redigidos para que os engenheiros não precisem! Eu tenho habilidades com as pessoas!" Pobre Tom. Se ele dissesse "eu separo o cliente da construção. Esse resultado é uma fábrica altamente coesa"; ele pode ter mantido seu emprego.

Muitos argumentos?

Não é para a comunicação front-end do cliente.

Front-end - fábrica? Se não forem 10 parâmetros, o melhor que você pode fazer é adiar a descompactação, se não a coisa original do JSON, então algum DTO. Isso é melhor do que passar o JSON para a fábrica? A mesma diferença que eu digo.

Eu consideraria fortemente passar parâmetros individuais. Atenha-se ao objetivo de uma fábrica limpa e coesa. Evite as preocupações da resposta do @DavidPacker.

Atenuando "muitos argumentos"

  • Construtores de fábrica ou classe

    • usando apenas argumentos para construção específica de classe / objeto.
    • parâmetros padrão
    • parâmetros opcionais
    • argumentos nomeados
  • Agrupamento de argumentos de front-end

    • Examina, avalia, valida, define, etc. valores de argumento guiados pelas assinaturas do construtor acima.
radarbob
fonte
"A fábrica não tem absolutamente nenhuma idéia de que tal coisa JSON exista" - bem, então para que serve a fábrica ?? Ele oculta os detalhes da criação do produto, tanto do produto quanto do consumidor. Por que outra classe deveria ajudar? Eu estou bem com a fábrica falando JSON. Pode-se implementar outra fábrica para analisar XML e implementar "Abstract Factory" no futuro ...
lugge86
Sr. Factory: "Que objeto você quer? ... O que é isso? Apenas me diga qual objeto de classe construir." O arquivo de configuração JSON é uma fonte de dados, como o tio Bob diz "é um detalhe de implementação". Poderia ser de outra fonte e / ou de outra forma. Geralmente, queremos separar os detalhes específicos da fonte de dados. Se a fonte ou o formulário mudar, a fábrica não mudará. Dado um analisador de origem +, e uma fábrica como módulos dissociados torna ambos reutilizáveis.
Radarbob 25/12/15