Recentemente, desenvolvi minha própria API e, com esse interesse investido em design de API, fiquei muito interessado em saber como posso melhorar meu design de API.
Um aspecto que surgiu algumas vezes é (não pelos usuários da minha API, mas na minha observação de discussão sobre o tópico): deve-se saber apenas olhando o código que chama a API do que está fazendo .
Por exemplo, veja esta discussão no GitHub para o repositório de discurso , é algo como:
foo.update_pinned(true, true);
Apenas olhando o código (sem saber os nomes dos parâmetros, a documentação etc.), não se pode adivinhar o que vai fazer - o que significa o segundo argumento? A melhoria sugerida é ter algo como:
foo.pin()
foo.unpin()
foo.pin_globally()
E isso esclarece as coisas (o segundo argumento foi sobre se deve ou não ser globalmente, suponho), e concordo que, neste caso, o posterior certamente seria uma melhoria.
No entanto, acredito que possa haver casos em que métodos para definir um estado diferente, mas logicamente relacionado, seriam melhor expostos como uma chamada de método, em vez de separados, mesmo que você não saiba o que está fazendo apenas observando o código . (Portanto, você teria que procurar nos nomes dos parâmetros e na documentação para descobrir - o que pessoalmente eu sempre faria, independentemente de não estar familiarizado com uma API).
Por exemplo, eu exponho um método SetVisibility(bool, string, bool)
em um FalconPeer e reconheço apenas olhando a linha:
falconPeer.SetVisibility(true, "aerw3", true);
Você não teria ideia do que está fazendo. Ele está configurando três valores diferentes que controlam a "visibilidade" do falconPeer
no sentido lógico: aceitar solicitações de associação, apenas com senha e responder a solicitações de descoberta. Dividir isso em três chamadas de método pode levar um usuário da API a definir um aspecto da "visibilidade", esquecendo-se de definir os outros que eu os forço a pensar, expondo apenas o método único para definir todos os aspectos da "visibilidade" . Além disso, quando o usuário deseja alterar um aspecto, quase sempre desejará alterar outro aspecto e agora pode fazê-lo em uma chamada.
fonte
update
método:foo.update(pinned=true, globally=true)
. Ou:foo.update_pinned(true, globally=true)
. Portanto, a resposta para sua pergunta também deve levar em consideração os recursos do idioma, porque uma boa API para o idioma X pode não ser boa para o idioma Y e vice-versa.setSize(10, 20)
não é tão legível quantosetSize(width=10, height=20)
ourandom(distribution='gaussian', mean=0.5, deviation=1)
. Em idiomas com parâmetros nomeados impostos, os booleanos podem transmitir exatamente a mesma quantidade de informações que o uso de enumerações / constantes nomeadas, para que possam ser boas nas APIs.Respostas:
Seu desejo de não dividi-lo em três chamadas de método é completamente compreensível, mas você tem outras opções além dos parâmetros booleanos.
Você pode usar enumerações:
Ou até mesmo uma enumeração de sinalizadores (se o seu idioma suportar):
Ou você pode usar um objeto de parâmetro :
O padrão Parameter Object fornece algumas outras vantagens que você pode achar úteis. Isso facilita a transmissão e a serialização de um conjunto de parâmetros, e você pode facilmente fornecer valores "padrão" de parâmetros não especificados.
Ou você pode apenas usar parâmetros booleanos. A Microsoft parece fazer isso o tempo todo com a API do .NET Framework, então você pode simplesmente dar de ombros e dizer "se é bom o suficiente para eles, é bom o suficiente para mim".
fonte
Obviamente, sempre há exceções à regra, mas como você explicou bem por si mesmo, há certas vantagens em ter uma API legível. Os argumentos booleanos são particularmente incômodos, pois 1) eles não revelam uma intenção e 2) implicam que você chama uma função, na qual você realmente deve ter dois, porque coisas diferentes vão acontecer dependendo do sinalizador booleano.
A principal questão é: quanto esforço você deseja investir para tornar sua API mais legível? Quanto mais voltado para o exterior, mais esforço pode ser facilmente justificado. Se é uma API que é usada apenas por outra unidade, não é grande coisa. Se você está falando sobre uma API REST na qual planeja deixar o mundo inteiro perder, também pode investir mais algum esforço para torná-la mais compreensível.
Como no seu exemplo, existe uma solução simples: aparentemente, no seu caso, a visibilidade não é apenas uma coisa verdadeira ou falsa. Em vez disso, você tem um conjunto de coisas que considera "visibilidade". Uma solução pode ser a introdução de algo como uma
Visibility
classe, que cubra todos esses tipos diferentes de visibilidade. Se você aplicar ainda o padrão Builder para criá-los, poderá acabar com um código como o seguinte:fonte