Termo (ou "padrão"?) Para "Faça algo se ainda não estiver pronto" [fechado]

54

Parece bastante básico, eu sei, mas recentemente um colega me disse que um método chamado startHttpServeré muito complicado de entender porque só inicia o servidor se ele ainda não estiver em execução. Percebo que tenho problemas quando respondo: "Sério? Faço isso há décadas - é um padrão comum na programação". Mais frequentemente do que gostaria de admitir, ele volta com algumas evidências documentadas que mostram que toda a comunidade de programação está por trás de seu ponto de vista e acabo me sentindo tímido.

Pergunta : Existe um padrão de design documentado por trás do conceito de um método que não é operacional se a ação necessária já estiver em vigor? Ou, se não for um padrão, também tem um nome? E se não, existe alguma razão para pensar que é muito complicado considerar escrever um método dessa maneira?

John Calcote
fonte
6
soa como cache - e geralmente é considerado realmente complicado (veja também Como explico $ {something} para $ {someone}? ) #
2800
8
Você tem três respostas diferentes, mas a melhor resposta seria: aplique todas: renomeie a função original, divida seu código em duas funções (onde uma das duas agora recebe o nome startHttpServer) e sim, o termo "idempotente" se aplica aqui bem.
Doc Brown
8
Que tipo de contra-evidência seu colega fornece? Você pode fornecer um link para ele?
Robert Harvey
5
Estou curioso para ver de onde seu colega obtém suas citações. Pelo menos em torno do Stack Overflow e da Engenharia de software, essa função teria um nome muito ruim, mas não teria nenhum comportamento incomum. Lembre-se de que não é porque alguém em algum lugar colocou algo em um blog que representa toda a visão da comunidade de programação. Até grandes nomes como Martin Fowler dizem coisas muito estranhas de tempos em tempos. Somos todos apenas humanos.
T. Sar - Restabelece Monica
4
Eu acho que uma maneira melhor de pensar em "Faça algo se ainda não tiver sido feito" seria "Coloque o sistema nesse estado". Obviamente, se o sistema já estiver nesse estado, o método não faria nada - qual seria o comportamento esperado.
T. Sar - Restabelece Monica

Respostas:

127

Como NickWilliams já disse : o conceito que o OP descreve é ​​chamado de idempotente (substantivo Idempotência ). É de fato uma prática comum, especialmente em APIs de alto nível.

MAS: Renomeie a função.

Em vez de startHttpServerchamá-lo makeSureHttpServerIsRunningou ensureHttpServerIsRunning.

Quando uma função é chamada startHttpServer, os leitores esperam que ela inicie um servidor HTTP; quando chamado dez vezes seguidas, terei dez servidores em execução. Sua função não faz isso na maioria das vezes. Além disso, o nome com "start" sugere que, se eu quiser apenas um servidor em execução, precisarei acompanhar se a função já foi chamada ou não.

Quando uma função é chamada makeSureHttpServerIsRunning, presumo que ela fará as coisas necessárias para garantir que um servidor HTTP esteja em execução, provavelmente verificando se já está em execução e iniciando-o de outra forma. Suponho também que a função verifique se o servidor está realmente em execução (iniciar um servidor pode envolver algum tempo em que ainda não está em execução).

gnasher729
fonte
83
Este. Pessoalmente, uso "Garantir". O ponto é que isso deve se tornar um esquema de nomenclatura que é entendido em sua equipe.
Euphoric
3
ou comece a lançar uma exceção se já tiver iniciado. como sqlconnection.open
Ewan
9
Eu sempre uso a nomeação "garantir" para a função "do if if já".
Kaz
3
Outro nome que costumo ver é getOrCreateSomething, que apenas cria na primeira chamada e depois retorna algo
#
5
@aroth Tem certeza de que não esperaria encontrar uma porta disponível e devolvê-la? Ou apenas ser mal escrito? ;)
jpmc26 28/08
33

Renomeie para EnsureServerRunning.

Completamente inequívoco e é claro que garante que esteja em execução (se não estiver) sem implicar uma reinicialização, se estiver.

(Alternativa StartServerIfNotRunning:?)

Stilez
fonte
11
A maioria dos chamadores apenas se preocupa com o fato de o servidor estar em execução após a chamada. Como isso é alcançado, eles não se importam.
precisa saber é o seguinte
29

Não é realmente um padrão de design, mas eu chamaria seu método de idempotente . Geralmente, o termo é usado para se referir a chamadas remotas, mas a descrição parece corresponder ao que você está fazendo.

Métodos Idempotentes. Os métodos também podem ter a propriedade "idempotência", pois (além de problemas de erro ou expiração), os efeitos colaterais de N> 0 pedidos idênticos são os mesmos que para um único pedido. ( De W3.org )

O efeito colateral do servidor aqui é que o servidor http é iniciado assim que o método é chamado. Não vejo nada de errado em um método fazer isso.

Se você precisar de um padrão de design, acho que você pode expor seu httpServer como um singleton que é iniciado quando inicializado.

Nick Williams
fonte
5
A função não é idempotente se a chamada uma vez iniciar o servidor http e a segunda vez não. Isso é literalmente o oposto da idempotência. Seria idempotente se toda chamada iniciasse um novo servidor http.
Polygnome
36
@ Polygnome A Wikipedia define idempotência como f (f (x)) = f (x), que geralmente corresponde à descrição de Nick. Aqui, a entrada e saída da função seria o estado implícito do servidor; portanto, o estado "servidor está em execução" seria um ponto fixo para essa função. Talvez eu esteja entendendo errado o artigo da Wikipedia aqui, você poderia vincular a outras referências?
amon
16
@ Polygnome: esta resposta está correta, idempotência refere-se a funções para as quais não importa se são chamadas uma ou mais de uma vez, o resultado permanece sempre o mesmo. Aqui, o resultado é um serviço http em execução, independentemente do número de vezes que a função é chamada.
Doc Brown
14
A confusão vem do fato de que a idempotência em sua essência é um conceito matemático aplicado às funções. A definição é agradável e simples para eles e também funciona bem para linguagens funcionais puras. Assim que você introduz efeitos colaterais, isso se torna complicado - você deve considerar o ambiente em que executa a função como um argumento (implícito) e saída da função. Se esse estado não for modificado por outras chamadas para a função, ele é idempotente, caso contrário, não é. Nesse caso, o estado relevante é "o servidor http está em execução" - portanto, a função é idempotente.
Voo 27/08
10
@ Polygnome Desculpe, você está errado. Idempotente significa que a solicitação tem o mesmo efeito, seja executada uma vez ou mais de uma vez. Iniciar N servidores http se N duplicatas da solicitação forem recebidas definitivamente não é idempotente.
Kaz
7

Como aquele que implementa essa ferramenta , startHttpServervocê deve tentar torná-la a mais simples, suave e perfeita de usar ...

A lógica da função

Tecnicamente, dividindo startHttpServera lógica em 2 funções e chamando-as separadamente , tudo o que você faz é mover startHttpServer a idempotência para o código chamando as duas funções ... Além disso, a menos que você envolva a lógica em uma terceira função (que é o que faz startHttpServerem primeiro lugar), isso obriga a escrever código não-SECADO, duplicando-o exponencialmente em qualquer lugar que você precise chamar startHttpServer. Em suma, startHttpServer tem que se chamar de isHttpServerRunningfunção.

Então, meu ponto é:

  • Implemente a isHttpServerRunningfunção porque isso pode ser necessário independentemente, de qualquer maneira ...
  • Implemente startHttpServero uso isHttpServerRunningpara definir sua próxima ação de acordo ...

Ainda assim, você pode startHttpServerretornar qualquer valor que o usuário dessa função possa precisar, por exemplo:

  • 0 => falha na inicialização do servidor
  • 1 => servidor iniciando com sucesso
  • 2 => servidor já foi iniciado

A nomeação da função

Primeiro de tudo, qual é o objetivo principal do usuário? Para iniciar o servidor HTTP , certo?

Fundamentalmente, não há problema pretendendo iniciar algo que já foi iniciado, também conhecido como AKA 1*1=1. Então, pelo menos para mim, chamá-lo " ensureHttpServerIsRunning" não parece ser criticamente necessário, eu me importaria mais com quanto tempo, natural e memorável é o nome da função.

Agora, se você quiser saber como trabalhar em detalhes a função sob o capô, existe a documentação ou a fonte de código para isso, quero dizer, como para qualquer outra função da biblioteca / framework / API / etc ...

Você aprende a função uma vez enquanto a escreve várias vezes ...

Enfim, eu continuaria com o startHttpServerque é mais curto, mais simples e mais explícito do que ensureHttpServerIsRunning.

ClemC
fonte
11
Especialmente porque o método deve ter alguns argumentos de configuração, como diretório raiz, configurações de segurança, possivelmente um número de porta, estou 100% confortável com o "start" aqui.
user949300
@ user949300: O chamador de "garantirHttpServerIsRunning" não se importa com configuração, diretório raiz etc. Isso é assunto da pessoa que o implementa.
gnasher729
2
@ gnasher729 Isso pressupõe que o servidor http seja um Singleton. Singletons são maus. E possivelmente inadequado aqui. Pode-se facilmente ter vários servidores em várias portas. Se realmente houver apenas um servidor http, todo esse método será IMO, design incorreto. É melhor iniciar o servidor apenas uma vez na inicialização do programa.
user949300
2

Suponho que seu colega quis dizer que startHttpServerestá fazendo muito:

  • Verificando se o servidor já está em execução,
  • Iniciando o servidor, se necessário.

Essas são duas partes não relacionadas do código. Por exemplo, existe uma situação semelhante quando um aplicativo de desktop deve garantir que ele ainda não esteja sendo executado quando iniciado; haverá uma parte do código que manipula instâncias de aplicativos (por exemplo, usando um mutex) e o código que iniciará o loop de mensagens do aplicativo.

Isso significa que você precisa ter não um, mas pelo menos dois métodos :

  • isHttpServerRunning: boolean
  • startHttpServer

O ponto de entrada do aplicativo chamará o primeiro método e, em seguida, o segundo se o valor de retorno for false. Agora, todo método está fazendo uma coisa e é fácil de entender.


¹ Se a lógica necessária para saber se o servidor já está em execução é muito complexa, pode ser necessária uma separação adicional em vários métodos.

Arseni Mourzenko
fonte
21
Agora, outra coisa que chama as duas funções está fazendo duas coisas, além disso, expor as funções separadamente pode introduzir condições de corrida. Sem almoço grátis.
Whatsisname
11
Se isso é demais para o colega, boa sorte.
gnasher729
@ gnasher729: esta resposta não contradiz a sua - pelo contrário, pode ser uma boa idéia combinar a renomeação do método com a divisão em duas. E enquanto não vemos o código real, não sabemos quão complexo é o código.
Doc Brown
10
Esta resposta parece opor-se à abstração e SECA. E se startHttpServerfor chamado mais de um lugar no código? Várias linhas semelhantes devem ser copiadas e coladas em todos os lugares? Isso deve ser feito com todas as funções? Em breve, o programa será de tamanho infinito.
precisa saber é o seguinte
3
Eu acho que o primeiro efeito colateral dessa abordagem é que o início do startHttpServermétodo será aproximadamente if (isHttpServerRunning()){ return; }. Você está afirmando uma regra de negócios que "não é válido iniciar o servidor http, se já estiver em execução", mas depois é responsabilidade de outra pessoa impor essa regra. Ad-hoc e repetidamente em todos os locais para onde eles possam ligar startHttpServer.
Aroth 28/08/19
2

Como você não especifica um idioma, em JavaScript, muitas bibliotecas têm uma função "única", por exemplo, Sublinhado . Portanto, se isso lhe for familiar, chame-o de "uma vez" e renomeie seu método.

Eu mesmo, vindo mais do Java, os termos "armazenamento em cache" ou "avaliação lenta" vêm à mente. "Idempotente" é tecnicamente correto e uma boa escolha, esp. se você tiver um fundo mais funcional.

user949300
fonte
Pode não ser "uma vez", se o servidor puder ser parado.
gnasher729
@ gnasher729. Eu poderia imaginar um restartHttpServer()método raramente usado . Mas apenas parando - qual é o caso de uso aí? Você gosta de falhas esporádicas de conexão? :-)
user949300
Não é necessário um caso de uso para interromper o servidor HTTP, pois ele pode ter sido interrompido por algo externo ao próprio programa - o servidor HTTP pode ter sido falhado e falhado, um administrador pode ter parado manualmente por algum motivo, etc.
Dave Sherohman
Dave, um acidente é "excepcional" e deve ser tratado como tal. Além disso, como uma falha pode ocorrer a qualquer momento , todas as linhas do seu código não precisam ser ensureRunning()? :-) Quanto ao administrador pará-lo, ter esse outro código reiniciando constantemente enquanto o administrador está tentando modificar ou consertar algo seria incrivelmente irritante e errado. Deixe o administrador reiniciá-lo, não o código.
user949300
-2

Eu preferiria startHttpServerIfNotIsRunning.

Dessa forma, a condição já está claramente mencionada no nome do método. Ensureou makeSureparece um pouco vago para mim, pois não é uma expressão técnica. Parece que não sabemos exatamente o que vai acontecer.

Herr Derb
fonte
Estou assumindo que você não é um falante nativo? Porque garantir tem a definição exata do que estamos buscando. Ainda assim, é justo que a documentação e as APIs não exijam que os níveis de falantes nativos de inglês sejam compreensíveis.
Voo
11
Obrigado eu exatamente não o que Ensuresignifica. Ainda não gosto desta palavra para uma expressão técnica. Ensureé algo humano. Um sistema não pode garantir nada, apenas fará o que deve fazer.
precisa saber é o seguinte
2
@ Voo - Eu sou um falante nativo e não tenho certeza do que garantir significa.
Jonathan fundido
Como todos os que postam aqui têm acesso à Internet, por que não procurariam a definição de 'Garantir' se não tinham certeza do que isso significava? Um pouco de trivialidade ... muitas pessoas trocam o uso de 'Assegurar' e 'Assegurar' sem perceber que o uso de uma dessas palavras pode inadvertidamente torná-las 'legalmente' responsáveis, enquanto a outra não.
Dunk
@jcast: Sério?
gnasher729
-3

O que seu colega deveria ter lhe dito é que você não tem negócios escrevendo esse método. Já foi escrito muitas vezes e melhor do que você provavelmente escreverá. Por exemplo: http://docs.ansible.com/ansible/latest/systemd_module.html https://docs.saltstack.com/en/latest/ref/states/all/salt.states.service.html

De uma perspectiva arquitetônica, ter um pouco de código arbitrário gerenciando um servidor Web é coisa de pesadelo. A menos que gerenciar serviços seja exclusivamente o que seu código faz. Mas acho que você não escreveu monit (ou kubernetes ou ...).

Andrew
fonte
2
A linguagem é Java e o método foi desenvolvido para iniciar um servidor incorporado pardo em um aplicativo independente de Jersey. Não posso culpar a precisão do seu comentário, já que não lhe dei muito contexto, mas você assumiu muito ao fazer sua declaração. É perfeitamente bom ter um método que chame jetty ou grizzly para iniciar o servidor.
John Calcote
11
Existem milhões de aplicativos de negócios que incluem servidor HTTP auto-hospedado para fornecer uma API. E todos eles precisarão de um código muito simples que realmente configure a biblioteca que está usando e forneça informações como qual interface ligar, qual porta usar, configurações de segurança etc. Ter uma startServerfunção ou similar é algo incomum . Isso não significa que você escreverá os detalhes de baixo nível.
Voo 28/08