Temos muitos lugares no código fonte do nosso aplicativo, onde uma classe tem muitos métodos com os mesmos nomes e parâmetros diferentes. Esses métodos sempre têm todos os parâmetros de um método 'anterior' mais um.
É o resultado de longa evolução (código legado) e esse pensamento (acredito):
" Existe um método M que faz a coisa A. Eu preciso fazer A + B. OK, eu sei ... vou adicionar um novo parâmetro a M, criar um novo método para isso, mover o código de M para o novo método com mais um parâmetro, faça o A + B ali e chame o novo método de M com um valor padrão do novo parâmetro " .
Aqui está um exemplo (em linguagem semelhante a Java):
class DocumentHome {
(...)
public Document createDocument(String name) {
// just calls another method with default value of its parameter
return createDocument(name, -1);
}
public Document createDocument(String name, int minPagesCount) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false);
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
// just calls another method with default value of its parameter
return createDocument(name, minPagesCount, false, "");
}
public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
// here the real work gets done
(...)
}
(...)
}
Eu sinto que isso está errado. Não apenas não podemos continuar adicionando novos parâmetros como esse para sempre, mas o código é difícil de estender / alterar por causa de todas as dependências entre os métodos.
Aqui estão algumas maneiras de como fazer isso melhor:
Introduzir um objeto de parâmetro:
class DocumentCreationParams { String name; int minPagesCount; boolean firstPageBlank; String title; (...) } class DokumentHome { public Document createDocument(DocumentCreationParams p) { // here the real work gets done (...) } }
Defina os parâmetros para o
DocumentHome
objeto antes de chamarmoscreateDocument()
@In DocumentHome dh = null; (...) dh.setName(...); dh.setMinPagesCount(...); dh.setFirstPageBlank(...); Document newDocument = dh.createDocument();
Separe o trabalho em diferentes métodos e chame-os conforme necessário:
@In DocumentHome dh = null; Document newDocument = dh.createDocument(); dh.changeName(newDocument, "name"); dh.addFirstBlankPage(newDocument); dh.changeMinPagesCount(new Document, 10);
Minhas perguntas:
- O problema descrito é realmente um problema?
- O que você acha das soluções sugeridas? Qual você prefere (com base na sua experiência)?
- Você consegue pensar em outra solução?
Respostas:
Talvez tente o padrão do construtor ? (nota: resultado bastante aleatório do Google :)
Não posso descrever detalhadamente por que prefiro o construtor sobre as opções que você fornece, mas você identificou um grande problema com muito código. Se você acha que precisa de mais de dois parâmetros para um método, provavelmente seu código está estruturado incorretamente (e alguns argumentariam um!).
O problema com um objeto params é (a menos que o objeto que você crie seja de alguma forma real), você apenas eleva o problema para um nível superior e acaba com um cluster de parâmetros não relacionados formando o 'objeto'.
Suas outras tentativas me parecem alguém buscando o padrão do construtor, mas não chegando lá :)
fonte
DocumentoFactory
). Tendobuilder
em lugares diferentes, é difícil controlar as mudanças futuras na construção do documento (como adicionar um novo campo obrigatório ao documento, por exemplo) e adicionar código extra nos testes para satisfazer apenas as necessidades do construtor de documentos nas classes que estão usando o construtor.Usar um objeto de parâmetro é uma boa maneira de evitar sobrecarga (excessiva) de métodos:
Eu não iria muito longe com isso.
Ter uma sobrecarga aqui e não é uma coisa ruim. É suportado pela linguagem de programação, portanto, use-o para sua vantagem.
Eu desconhecia o padrão do construtor, mas o usei "por acidente" em algumas ocasiões. O mesmo se aplica aqui: não exagere. O código no seu exemplo se beneficiaria com isso, mas gastar muito tempo implementando-o para cada método que possui um único método de sobrecarga não é muito eficiente.
Apenas meus 2 centavos.
fonte
Sinceramente, não vejo um grande problema com o código. Em C # e C ++, você pode usar parâmetros opcionais, isso seria uma alternativa, mas até onde eu sei, Java não suporta esse tipo de parâmetros.
Em C #, você pode tornar todas as sobrecargas privadas e um método com parâmetros opcionais é público para chamar as coisas.
Para responder à sua pergunta, parte 2, eu pegaria o objeto de parâmetro ou mesmo um dicionário / HashMap.
Igual a:
Como aviso, sou primeiro um programador em C # e JavaScript e depois um programador em Java.
fonte
Eu acho que este é um bom candidato para o padrão do construtor. O padrão do construtor é útil quando você deseja criar objetos do mesmo tipo, mas com representações diferentes.
No seu caso, eu teria um construtor com os seguintes métodos:
Em seguida, você pode usar o construtor da seguinte maneira:
Por outro lado, não me importo com poucas sobrecargas simples - o que diabos, o .NET framework as usa em todo o lugar com os auxiliares de HTML. No entanto, eu reavaliaria o que estou fazendo se tiver que passar mais de dois parâmetros para cada método.
fonte
Acho que a
builder
solução pode funcionar na maior parte dos cenários, mas em casos mais complexos, o construtor também será complexo de configurar , porque é fácil cometer alguns erros na ordem dos métodos, quais métodos precisam ser expostos ou não , etc. Muitos de nós preferem uma solução mais simples.Se você acabou de criar um construtor simples para criar um documento e espalhar esse código em diferentes partes (classes) do aplicativo, será difícil:
Mas isso não responde à pergunta do OP.
A alternativa à sobrecarga
Algumas alternativas:
createDocument
, como:createLicenseDriveDocument
,createDocumentWithOptionalFields
, etc. Claro, isso pode levar você a nomes de métodos gigantes, de modo que este não é uma solução para todos os casos.Document
, como:Document.createLicenseDriveDocument()
.createDocument(InterfaceDocument interfaceDocument)
e criar diferentes implementações paraInterfaceDocument
. Por exemplo:createDocument(new DocumentMinPagesCount("name"))
. Obviamente, você não precisa de uma única implementação para cada caso, porque é possível criar mais de um construtor em cada implementação, agrupando alguns campos que fazem sentido nessa implementação. Esse padrão é chamado de construtores telescópicos .Ou apenas fique com a solução de sobrecarga . Mesmo sendo, às vezes, uma solução feia, não há muitas desvantagens em usá-la. Nesse caso, prefiro usar métodos de sobrecarga em uma classe separada, como uma
DocumentoFactory
que possa ser injetada como dependência de classes que precisam criar documentos. Posso organizar e validar os campos sem a complexidade de criar um bom construtor e manter o código em um único local também.fonte