É uma prática ruim de codificação criar algo se não existir?

17

Então, eu tenho um serviço da web que tem algo como um getAccountonde ele retornaria um identificador para a conta, se o obtivesse, caso contrário, lance uma exceção. O cliente sempre desejará criar uma conta se uma exceção for lançada com as mesmas informações com as quais a obtenção é feita.

Estou criando uma biblioteca de conveniência para os clientes que manipularão todas as chamadas de serviço da Web para que não precisem saber como fazer as chamadas.

O que me pergunto é nesta biblioteca se eu deveria criar um getAccount(accountName)que obterá a conta, se existir, e se não o criar e retornar as informações, isso é uma coisa ruim a se fazer? Devo deixar que o cliente lide com as exceções ou simplesmente nomeie algo como getOrCreateAccount? Isso importa?

É uma prática ruim criar algo em uma operação get?

Mike
fonte
9
No mínimo, eu o nomearia getOrCreateAccountou similar.
Telastyn 22/05/12
4
Parece válido no contexto de inicialização lenta. No entanto, isso depende se você precisar usar esse padrão em sua biblioteca.
C #
1
Eu gosto do verbo acquire, gosto acquireAccount. Ele não tem um significado existente em nenhum dos principais protocolos que encontrei e possui um anel imperativo adequado para isso. "Faça o que for necessário para adquirir um desses para mim. Solicite, construa, falsifique, roube, não me importo, apenas pegue um ou morra tentando."
Dan Ross
2
"O cliente sempre desejará criar uma conta" - isso parece altamente incomum. Se não conseguir obter a conta porque o usuário digitou incorretamente seu nome de usuário, certamente não quero criar uma conta com o nome digitado incorretamente.
gnasher729
De acordo com a especificação javabean, oracle.com/technetwork/articles/javaee/spec-136004.html getSomething() é para getters e setSomething()é para setters. Imo qualquer coisa que faz algo mais intelectual deve ser chamado outra coisa, ou seja fetchSomething, obtainSomething, computeSomething, ou doSomethingElseetc.
ccpizza

Respostas:

31

Sim, isso importa. Na minha opinião, geralmente é uma prática ruim criar algo em um procedimento que não esteja documentado como possuindo os poderes de criação. Nomeie o procedimento getOrCreate...ou tenha um create...procedimento separado e, se você realmente quiser, faça as getOrCreate...primeiras tentativas get...e, se isso falhar, ligue create...e depois ligue get....

O usuário da biblioteca provavelmente não esperará que o get...procedimento seja criado se a operação get falhar. Se eles descobrirem repentinamente que seus testes pedem para get...criar uma tonelada de dados, provavelmente ficarão bastante surpresos. E como eles limpam isso? E se eles escreverem código pensando que receberão um erro se get...falharem e quiserem lidar com isso à sua maneira?

FrustratedWithFormsDesigner
fonte
Obrigado, o create realmente retorna o ID que o get retornaria também, então eu não precisaria fazer get... create... get...apenas os dois primeiros. Vou falar com o cliente sobre se eles exigirão a capacidade de simplesmente chamar um getsem querer criar #
22212 Mike
6
@ Mike: Eu ainda acho que deveria ter createno nome, apenas para ser 100% claro sobre o que está acontecendo.
FrustratedWithFormsDesigner
Sim, eu concordo, eu estava pensando em fazer algo ao longo das linhas de getOrCreate porque meu pensamento original era apenas para fazer chegar, mas eu percebi que não está claro imediatamente que ele também está criando algo no fundo
Mike
1
Isso é muito tarde, mas getOrCreatetem precedência em uma estrutura da web popular: docs.djangoproject.com/en/1.10/ref/models/querysets/…
tex
18

Não, não é 'má prática'. Desde que você e os outros desenvolvedores concordem que é assim que você deseja que funcione, tudo bem. Afinal, seria retornar uma conta, que é o que você deseja. O fato de a conta ser criada "sob o capô" é irrelevante para o chamador.

GrandmasterB
fonte
10
A única exceção é se for uma API disponível publicamente. A comunidade maior já concordou que o GET é indempotente e nulipotente; em outras palavras, a mesma ação é executada a cada vez e é um método seguro que não altera o estado do servidor. Isso está no Wiki do REST . Além disso, se a API existir apenas no seu pequeno mundo, faça o que for aceito pelo seu grupo.
Jmort253
9
Eu discordo, mesmo para uma API pública, tudo bem - desde que faça sentido dentro do contexto do que a API deve fazer. Não há sentido em criar várias entradas na API quando uma única faria o truque.
GrandmasterB
Para o registro, é uma biblioteca totalmente interna e eu tenho a capacidade de simplesmente dizer a equipe de usá-lo como ele funciona
Mike
1
@ jmort253: Um serviço é nulipotente se não tiver efeitos colaterais visíveis para o cliente . O servidor pode fazer o que bem entender.
kevin Cline
1
@ Kevincline, acho que estava pensando nisso em termos de, digamos, cobrança no meu cartão de crédito. Se o servidor cobrar meu cartão com base em uma solicitação GET, mas ainda assim me der um resultado como "recusado" toda vez que eu o executar, isso parecerá contrário ao espírito do GET. Com isso dito, entendo seu ponto de vista. O servidor pode contar o número de solicitações GET e me bloquear após três consultas, limitando-me a taxa, mas sem alterar os detalhes da minha conta. Eu acho que é uma distinção importante quando se diz que o estado do servidor não é modificado. Talvez eu devesse dizer "estado cliente no servidor" em vez
jmort253
10

Se getAccount()sempre puder retornar uma conta, do ponto de vista do chamador, a conta existe e sempre existiu. Não é necessário getAccount()"criar" nada. A conta não precisa ser armazenada em nenhum lugar até que seja diferente da conta padrão.

Kevin Cline
fonte
+1 Geralmente, GetOrCreateé a semântica errada, mas obter um objeto que possa "logicamente" existir, existindo ou não phyiscally, é bom. Como exemplo, uma matriz esparsa de itens mutáveis ​​pode não ter nenhum armazenamento alocado para o elemento 1.841.533, mas ainda permitir que esse elemento seja "recuperado" criando um novo objeto, armazenando-o e retornando uma referência.
Supercat
4

Faz mais sentido criar 3 métodos:

getAccount -> O que apenas obtém a conta.

createAccount -> Cria uma conta.

getAccountAndCreateIfNeeded -> Escolha sua própria nomeação;)

Por que a separação: você tem um método simples para obter e criar. Esse é um método claramente testável para ambos. Para getAccount, não é uma exceção não encontrar a conta. Então, basta retornar false ou algo assim, é esperado.

Em seguida, você pode usar esse valor de retorno em sua função agrupada: getAccountAndCreateIfNeeded, que agora também é testável, deve retornar sempre uma conta. O que você pedir.

Todos esses três métodos são claros, é claro o que eles fazem e o que retornam. Você pode fazer acordos agora com sua equipe, mas esse tipo de exceção é terrível a longo prazo. Apenas deixe bem claro e você não terá problemas.

Luc Franken
fonte
Além disso, no Código Limpo: "Se o seu método não puder fazer o que o nome implica, lance uma exceção." Eu teria um bloco Try / Catch dentro de 'GetOrCreateIfneeded' que gera um GET com falha e, em seguida, tem o 'createAccount' dentro do Throw para qualquer tipo de exceção que volte para o get com falha.
Graham
Eu poderia sugerir um quarto método:, getAccountIfExistsque obteria uma conta ou indicaria que ela não existe sem a criação de uma nova. O getAccountpróprio método deve pressupor a existência da conta e, se não houver, lançar uma exceção.
Supercat
2

Isso depende das circunstâncias.

Por exemplo, você pode usá-lo para fazer carregamento / instanciação lenta, adiando o carregamento de dados ou a criação de uma instância até que seja realmente necessário. Isso normalmente é sensato, pois economiza recursos dos quais você talvez não precise (se a classe / dados nunca for necessário, nunca será carregado).

No entanto, nesse caso em particular, eu diria que ter um método chamado getAccount que criaria uma nova conta se ela não existisse não seria uma boa prática. Se o usuário forneceu algumas credenciais para identificar uma conta específica e essa conta não pôde ser encontrada, isso significa que o usuário ainda não é um cliente e deve ter uma conta criada para ele ou significa que as credenciais foram digitadas incorretamente e o usuário precisa ser solicitado a verificar se inseriu o que pretendia inserir?

Se você possui um método getAccount que cria uma nova conta, caso não consiga identificar uma, não há escolha. Se você dividir a criação e a saída da conta em métodos separados, terá muito mais flexibilidade para decidir o que fazer caso uma tentativa de obter uma conta falhe.

GordonM
fonte