É errado usar um parâmetro booleano para determinar valores?


39

De acordo com É errado usar um parâmetro booleano para determinar o comportamento? , Eu sei a importância de evitar o uso de parâmetros booleanos para determinar um comportamento, por exemplo:

versão original

public void setState(boolean flag){
    if(flag){
        a();
    }else{
        b();
    }
    c();
}

nova versão:

public void setStateTrue(){
    a();
    c();
}

public void setStateFalse(){
    b();
    c();
}

Mas e o caso em que o parâmetro booleano é usado para determinar valores em vez de comportamentos? por exemplo:

public void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

Estou tentando eliminar o sinalizador isHintOn e criar 2 funções separadas:

public void setHintOn(){
    this.layer1.visible=true;
    this.layer2.visible=false;
    this.layer3.visible=true;
}

public void setHintOff(){
    this.layer1.visible=false;
    this.layer2.visible=true;
    this.layer3.visible=false;
}

mas a versão modificada parece menos sustentável porque:

  1. tem mais códigos que a versão original

  2. não pode mostrar claramente que a visibilidade da camada2 é oposta à opção de dica

  3. quando uma nova camada (por exemplo: layer4) é adicionada, preciso adicionar

    this.layer4.visible=false;
    

    e

    this.layer4.visible=true;  
    

    em setHintOn () e setHintOff () separadamente

Portanto, minha pergunta é: se o parâmetro booleano for usado apenas para determinar valores, mas não comportamentos (por exemplo: nenhum if-else nesse parâmetro), ainda é recomendável eliminar esse parâmetro booleano?


26
Nunca está errado se o código resultante for mais legível e sustentável ;-) Eu recomendaria o uso do método único em vez dos dois métodos separados.
helb

32
Você apresenta um argumento convincente de que uma única implementação de um método que define esses booleanos resultará em uma manutenção mais fácil da classe e no entendimento de sua implementação. Muito bem; essas são considerações legítimas. Mas a interface pública da classe não precisa ser deformada para acomodá-los. Se métodos separados vai fazer a interface pública mais fácil de entender e trabalhar com, definir o seu setHint(boolean isHintOn)como um privado método, e adicionar pública setHintOne setHintOffmétodos que respectivamente chamam setHint(true)e setHint(false).
Mark Amery

9
Eu ficaria muito infeliz com esses nomes de métodos: eles realmente não oferecem nenhum benefício setHint(true|false). Potahto de batata. Pelo menos use algo como setHinte unsetHint.
Konrad Rudolph


4
@kevincline Se a condição for um nome, você escreve isno início. isValidetc. Então, por que mudar isso por duas palavras? Além disso, "mais natural" está nos olhos de quem vê. Se você quer pronunciar-lo como uma frase Inglês, em seguida, para mim seria mais natural ter "se a dica é sobre" com um "a" dobrado.
Sr. Lister

Respostas:


95

O design da API deve se concentrar no que é mais utilizável para um cliente da API, do lado da chamada .

Por exemplo, se essa nova API exigir que o chamador escreva regularmente códigos como este

if(flag)
    foo.setStateTrue();
else
    foo.setStateFalse();

deve ser óbvio que evitar o parâmetro é pior do que ter uma API que permita ao chamador escrever

 foo.setState(flag);

A versão anterior apenas produz um problema que deve ser resolvido no lado de chamada (e provavelmente mais de uma vez). Isso não aumenta a legibilidade nem a capacidade de manutenção.

O lado da implementação , no entanto, não deve ditar a aparência da API pública. Se uma função como setHintum parâmetro precisa de menos código na implementação, mas uma API em termos setHintOn/ setHintOffparece mais fácil de usar para um cliente, é possível implementá-la desta maneira:

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

Portanto, embora a API pública não tenha parâmetro booleano, não há lógica duplicada aqui; portanto, apenas um lugar para alterar quando um novo requisito (como no exemplo da pergunta) chegar.

Isso também funciona ao contrário: se o setStatemétodo acima precisar alternar entre dois trechos de código diferentes, esses trechos de código poderão ser refatorados para dois métodos particulares diferentes. Portanto, no IMHO, não faz sentido procurar um critério para decidir entre "um parâmetro / um método" e "zero parâmetros / dois métodos" olhando para os internos. Veja, no entanto, a maneira como você gostaria de ver a API no papel de consumidor dela.

Em caso de dúvida, tente usar o "TDD), que forçará você a pensar na API pública e em como usá-la.


DocBrown, você diria que a linha divisória apropriada é se a configuração de cada estado tem efeitos colaterais complexos e possivelmente não reversíveis? Por exemplo, se você estiver simplesmente alternando uma bandeira que faz o que diz na lata e não houver uma máquina de estado subjacente, você parametrizará os diferentes estados. Considerando que, por exemplo, você não parametrizaria um método como SetLoanFacility(bool enabled), porque, tendo concedido um empréstimo, pode não ser fácil retirá-lo novamente, e as duas opções podem envolver uma lógica completamente diferente - e você deseja mover para separar Create / Remova métodos.
Steve

15
@ Steve: você ainda está tentando projetar a API a partir dos requisitos que você vê no lado da implementação. Ser sincero: isso é totalmente irrelevante. Use a variante da API pública mais fácil de usar do lado da chamada. Internamente, você sempre pode permitir que dois métodos públicos chamam um particular com um parâmetro Ou vice-versa, você pode permitir que um método com um parâmetro alterne entre dois métodos particulares com lógica diferente.
Doc Brown

@ Steve: veja minha edição.
Doc Brown

Eu entendo todos os seus pontos de vista - na verdade, estou pensando nisso do lado do interlocutor (daí a minha referência ao "que está escrito na lata") e tentando formular a regra apropriada em que um interlocutor geralmente espera usar cada abordagem. Parece-me que a regra é se o chamador espera que chamadas repetidas sejam idempotentes e que as transições de estado sejam irrestritas e sem efeitos colaterais complexos. A ativação e desativação de um interruptor da luz da sala seria parametrizada, a ativação e desativação de uma usina regional seria multi-método.
Steve

1
@ Steve Portanto, se o usuário precisar afirmar uma configuração específica, essa Toggle()não é a função correta a ser fornecida. Esse é o ponto inteiro; se o chamador se preocupa apenas com "alterá-lo" e não "como ele termina", então Toggle()é a opção que evita verificações e tomadas de decisão adicionais. Eu não chamaria isso de caso COMUM, nem recomendaria disponibilizá-lo sem um bom motivo, mas se o usuário precisar de uma alternância, eu as alternaria.
Kamil Drakari

40

Martin Fowler cita Kent Beck ao recomendar setOn() setOff()métodos separados , mas também diz que isso não deve ser considerado inviolável:

Se você puxar dados [sic] de uma fonte boolean, como um controle UI ou fonte de dados, eu prefiro ter setSwitch(aValue)que

if (aValue)
  setOn();
else
  setOff();

Este é um exemplo de que uma API deve ser gravada para facilitar o chamador, portanto, se soubermos de onde o chamador vem, devemos projetar a API com essas informações em mente. Isso também argumenta que, às vezes, podemos fornecer os dois estilos se recebermos chamadas dos dois lados.

Outra recomendação é usar um valor enumerado ou bandeiras digite para dar truee falsemelhor, nomes específicos ao contexto. No seu exemplo, showHinte hideHintpoderia ser melhor.


16
Na minha experiência, setSwitch (valor) quase sempre resulta em menos código geral do que setOn / setOff precisamente por causa do código if / else citado em sua resposta. Costumo amaldiçoar os desenvolvedores que me fornecem uma API de setOn / setOff em vez de setSwitch (value).
17 de 26

1
Analisando a partir de uma lente semelhante: se você precisar codificar qual será o valor, isso também é fácil. No entanto, se você precisar configurá-lo para, digamos, entrada do usuário, se você puder apenas passar o valor diretamente, isso salvará uma etapa.
precisa saber é o seguinte

@ 17of26 como um contra-exemplo concreto (para mostrar que "depende" e não um é preferível ao outro), no AppKit da Apple, há um -[NSView setNeedsDisplay:]método pelo qual você passa YESse uma visão deve ser redesenhada e NOse não deveria. Você quase nunca precisa dizer para não fazê-lo, então o UIKit simplesmente não possui -[UIView setNeedsDisplay]parâmetro. Não possui o -setDoesNotNeedDisplaymétodo correspondente .
Graham Lee

2
@GrahamLee, acho que o argumento de Fowler é bastante sutil e realmente se baseia no julgamento. Eu não usaria uma bool isPremiumflag no exemplo dele, mas usaria um enum ( BookingType bookingType) para parametrizar o mesmo método, a menos que a lógica para cada reserva fosse bem diferente. A "lógica emaranhada" a que Fowler se refere é frequentemente desejável se alguém quiser ver qual é a diferença entre os dois modos. E se eles forem radicalmente diferentes, eu exporia o método parametrizado externamente e implementaria os métodos separados internamente.
Steve

3

Eu acho que você está misturando duas coisas no seu post, a API e a implementação. Nos dois casos, não acho que exista uma regra forte que você possa usar o tempo todo, mas considere essas duas coisas de forma independente (o máximo possível).

Vamos começar com a API, ambos:

public void setHint(boolean isHintOn)

e:

public void setHintOn()
public void setHintOff()

são alternativas válidas, dependendo do que seu objeto deve oferecer e como seus clientes vão usar a API. Como o Doc apontou, se os usuários já tiverem uma variável booleana (de um controle de interface do usuário, de uma ação do usuário, de uma API externa, etc.), a primeira opção fará mais sentido; caso contrário, você estará forçando uma instrução if extra no código do cliente . No entanto, se, por exemplo, você estiver alterando a dica para true ao iniciar um processo e para false no final, a primeira opção fornecerá algo como isto:

setHint(true)
// Do your process
…
setHint(false)

enquanto a segunda opção fornece:

setHintOn()
// Do your process
…
setHintOff()

qual IMO é muito mais legível, então eu vou com a segunda opção neste caso. Obviamente, nada impede você de oferecer as duas opções (ou mais, você pode usar uma enumeração, como disse Graham, se isso faz mais sentido, por exemplo).

O ponto é que você deve escolher sua API com base no que o objeto deve fazer e como os clientes o usarão, não com base em como você o implementará.

Então você deve escolher como implementar sua API pública. Digamos que escolhemos os métodos setHintOne setHintOffcomo nossa API pública e eles compartilham essa lógica comum, como no seu exemplo. Você poderia abstrair facilmente essa lógica por meio de um método privado (código copiado do Doc):

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

Por outro lado, digamos que escolhemos setHint(boolean isHintOn)uma API, mas vamos reverter o seu exemplo, por qualquer motivo que tenha definido a dica como Ativada, é completamente diferente de desativada. Nesse caso, podemos implementá-lo da seguinte maneira:

public void setHint(boolean isHintOn){
    if(isHintOn){
        // Set it On
    } else {
        // Set it Off
    }    
}

Ou até:

public void setHint(boolean isHintOn){    
    if(isHintOn){
        setHintOn()
    } else {
        setHintOff()
   }    
}

private void setHintOn(){
   // Set it On
}

private void setHintOff(){
   // Set it Off 
}

O ponto é que, nos dois casos, escolhemos nossa API pública e depois adaptamos nossa implementação para se ajustar à API escolhida (e às restrições que temos), e não o contrário.

A propósito, acho que o mesmo se aplica à postagem que você vinculou sobre o uso de um parâmetro booleano para determinar o comportamento, ou seja, você deve decidir com base em seu caso de uso específico e não em uma regra rígida (embora, nesse caso específico, geralmente seja a coisa certa a fazer) é quebrá-lo em várias funções).


3
Como observação lateral, se for algo parecido com o pseudo-bloco (uma declaração no início, outra no final), você provavelmente deve usar begine endou sinônimos, apenas para esclarecer explicitamente o que eles fazem e implicar que um o começo tem que ter um final e vice-versa.
precisa saber é o seguinte

Eu concordo com Nic e gostaria de acrescentar: Se você deseja garantir que os começos e os fins sejam sempre acoplados, você também deve fornecer idiomas específicos para o idioma: RAII / escopos em C ++, usingblocos em C #, withgerenciadores de contexto de instruções em Python, passando o corpo de como lambda ou objecto pode ser chamado (por exemplo, a sintaxe de bloco rubi), etc
Daniel Pryden

Eu concordo com os dois pontos, eu estava apenas tentando dar um exemplo simples para ilustrar o outro caso (embora seja um mau exemplo, como os dois apontaram :)).
jesm00

2

Primeiras coisas primeiro: o código não é automaticamente menos sustentável, apenas porque é um pouco mais longo. Clareza é o que importa.

Agora, se você realmente está apenas lidando com dados, o que você tem é um configurador para uma propriedade booleana. Nesse caso, você pode simplesmente armazenar esse valor diretamente e derivar as visibilidades da camada, ou seja,

bool isBackgroundVisible() {
    return isHintVisible;
}    

bool isContentVisible() {
    return !isHintVisible;
}

(Tomei a liberdade de dar nomes reais às camadas - se você não tiver isso no código original, eu começaria com isso)

Isso ainda deixa você com a questão de ter um setHintVisibility(bool)método. Pessoalmente, eu recomendaria substituí-lo por um método showHint()e hideHint()- ambos serão realmente simples e você não precisará alterá-los quando adicionar camadas. Não é um corte claro certo / errado, no entanto.

Agora, se a chamada da função realmente alterar a visibilidade dessas camadas, você realmente terá um comportamento. Nesse caso, eu recomendaria definitivamente métodos separados.


Então, é o seguinte: você recomenda dividir em duas funções porque não precisará alterá-las quando adicionar camadas? Se uma camada4 for adicionada, talvez seja necessário dizer "this.layer4.visibility = isHintOn" também, então não tenho certeza se concordo. Se é que isso é um demérito, como agora, quando uma camada é adicionada, precisamos editar duas funções, não apenas uma.
Erdrik Ironrose

Não, eu (fraco) recomendo para maior clareza ( showHintvs setHintVisibility). Mencionei a nova camada apenas porque o OP estava preocupado com isso. Além disso, nós só precisa adicionar um novo método: isLayer4Visible. showHinte hideHintapenas defina o isHintVisibleatributo como true / false e isso não muda.
usar o seguinte comando

1
@doubleYou, você diz que um código mais longo não é automaticamente menos sustentável. Eu diria que o comprimento do código é uma das principais variáveis ​​de manutenção e clareza, superada apenas pela complexidade estrutural. Qualquer código que se torne mais longo e mais complexo estruturalmente deve merecê-lo; caso contrário, problemas simples recebem um tratamento mais complexo do código do que merecem, e a base de código adquire emaranhados de linhas desnecessárias (o problema "muitos níveis de indireção indireta").
Steve

@ Steve Concordo plenamente que você pode fazer engenharia de coisas demais, ou seja, tornar o código mais longo sem torná-lo mais claro - oh, você sempre pode tornar o código mais curto ao custo da clareza, para que não haja relação 1: 1 aqui.
Doubleyou

@ Steve Pense “golf code” - tomar tal código e reescrevê-lo em mais linhas que normalmente torná-lo mais claro. O “código golf” é extremo, mas ainda existem muitos programadores que acham que colocar tudo em uma expressão inteligente é “elegante” e talvez até mais rápido, porque os compiladores não estão otimizando o suficiente.
BlackJack 11/01

1

Parâmetros booleanos são bons no segundo exemplo. Como você já descobriu, os parâmetros booleanos não são, por si só, problemáticos. É mudar o comportamento com base em um sinalizador, o que é problemático.

O primeiro exemplo é problemático, porque a nomeação indica um método setter, mas as implementações parecem ser algo diferente. Então você tem o antipadrão de mudança de comportamento e um método enganosamente nomeado. Mas se o método é realmente um levantador regular (sem mudança de comportamento), não há problema com setState(boolean). Ter dois métodos setStateTrue()e setStateFalse()complicar desnecessariamente as coisas sem nenhum benefício.


1

Outra maneira de resolver esse problema é introduzir um objeto para representar cada dica e fazer com que o objeto seja responsável por determinar os valores booleanos associados a essa dica. Dessa forma, você pode adicionar novas permutações em vez de simplesmente ter dois estados booleanos.

Por exemplo, em Java, você pode fazer:

public enum HintState {
    SHOW_HINT(true, false, true),
    HIDE_HINT(false, true, false);

    private HintState(boolean layer1Visible, boolean layer2Visible, boolean layer3Visible) {
         // constructor body and accessors omitted for clarity
    }
}

E então seu código de chamada ficaria assim:

setHint(HintState.SHOW_HINT);

E seu código de implementação ficaria assim:

public void setHint(HintState hint) {
    this.layer1Visible = hint.isLayer1Visible();
    this.layer2Visible = hint.isLayer2Visible();
    this.layer3Visible = hint.isLayer3Visible();
}

Isso mantém o código de implementação e o código do chamador concisos, em troca da definição de um novo tipo de dados que mapeia claramente as intenções nomeadas e fortemente tipadas para os conjuntos de estados correspondentes. Eu acho que é melhor por toda parte.


0

Portanto, minha pergunta é: se o parâmetro booleano for usado apenas para determinar valores, mas não comportamentos (por exemplo: nenhum if-else nesse parâmetro), ainda é recomendável eliminar esse parâmetro booleano?

Quando eu tenho dúvidas sobre essas coisas. Eu gosto de imaginar como seria o rastreamento de pilha.

Por muitos anos, trabalhei em um projeto PHP que usava a mesma função que um setter e um getter . Se você passou nulo, ele retornaria o valor, caso contrário, defina-o. Foi horrível trabalhar com .

Aqui está um exemplo de rastreamento de pilha:

function visible() : line 440
function parent() : line 398
function mode() : line 384
function run() : line 5

Você não tinha idéia do estado interno e isso dificultava a depuração. Existem vários outros efeitos colaterais negativos, mas tente ver se há valor em um nome detalhado da função e clareza quando as funções executam uma única ação.

Agora imagine trabalhar com o rastreamento de pilha para uma função que tenha um comportamento A ou B com base em um valor booleano.

function bar() : line 92
function setVisible() : line 120
function foo() : line 492
function setVisible() : line 120
function run() : line 5

Isso é confuso se você me perguntar. As mesmas setVisiblelinhas produzem dois caminhos de rastreamento diferentes.

Então, de volta à sua pergunta. Tente imaginar como será o rastreamento da pilha, como ele se comunica com uma pessoa e o que está acontecendo e pergunte a si mesmo se você está ajudando uma futura pessoa a depurar o código.

Aqui estão algumas dicas:

  • um nome de função claro que implica intenção sem a necessidade de conhecer os valores do argumento.
  • uma função executa uma única ação
  • o nome implica mutação ou comportamento imutável
  • resolver desafios de depuração relativos à capacidade de depuração do idioma e das ferramentas.

Às vezes, o código parece excessivo sob um microscópio, mas quando você retorna à visão geral, uma abordagem minimalista o faz desaparecer. Se você precisar se destacar para poder mantê-lo. A adição de muitas funções pequenas pode parecer excessivamente detalhada, mas melhora a capacidade de manutenção quando usada amplamente em um contexto maior.


1
O que me confunde sobre o setVisiblerastreamento de pilha é que a chamada setVisible(true)parece resultar em uma chamada para setVisible(false)(ou vice-versa, dependendo de como você conseguiu que esse rastreamento ocorresse).
David K

0

Em quase todos os casos em que você está passando um booleanparâmetro para um método como um sinalizador para alterar o comportamento de algo, considere uma explicação mais explícita e segura de fazer isso.

Se você não fizer nada além de usar um Enum que represente o estado, melhorou a compreensão do seu código.

Este exemplo usa a Nodeclasse de JavaFX:

public enum Visiblity
{
    SHOW, HIDE

    public boolean toggleVisibility(@Nonnull final Node node) {
        node.setVisible(!node.isVisible());
    }
}

é sempre melhor do que, como encontrado em muitos JavaFXobjetos:

public void setVisiblity(final boolean flag);

mas penso .setVisible()e .setHidden()sou a melhor solução para a situação em que a bandeira é umaboolean , pois é a mais explícita e menos detalhada.

no caso de algo com várias seleções, é ainda mais importante fazê-lo dessa maneira. EnumSetexiste apenas por esse motivo.

Ed Mann tem um ótimo post sobre esse assunto. Eu estava prestes a parafrasear exatamente o que ele diz, para não duplicar o esforço, postarei apenas um link para o seu blog como um adendo a esta resposta.


0

Ao decidir entre uma abordagem para alguma interface que passa um parâmetro (booleano) versus métodos sobrecarregados sem esse parâmetro, observe os clientes consumidores.

Se todos os usos passassem valores constantes (por exemplo, verdadeiro, falso), isso indica as sobrecargas.

Se todos os usos passassem um valor variável, isso argumentaria com o método com abordagem de parâmetros.

Se nenhum desses extremos se aplicar, isso significa que há uma mistura de usos do cliente; portanto, você deve optar por dar suporte a ambos os formulários ou criar uma categoria de clientes para se adaptar ao outro (o que para eles é um estilo menos natural).


E se você está projetando um sistema integrado e você são em última análise, tanto o produtor de código e o "cliente consumir" do código? Como uma pessoa que está no lugar de um cliente consumidor deve formular sua preferência por uma abordagem em detrimento de outra?
Steve

@ Steve, como cliente consumidor, você sabe se está passando ou não uma constante ou variável. Se passar constantes, prefira sobrecargas sem o parâmetro.
Erik Eidt

Mas estou interessado em articular por que esse deveria ser o caso. Por que não empregar enumerações para um número limitado de constantes, pois essa é uma sintaxe leve na maioria dos idiomas projetados precisamente para esse tipo de objetivo?
Steve Steve

@ Steve, se soubermos em tempo de design / tempo de compilação que os clientes usariam um valor constante (verdadeiro / falso) em todos os casos, isso sugere que realmente existem dois métodos específicos diferentes, em vez de um método generalizado (que aceita um parâmetro). Eu argumentaria contra a introdução da generalidade de um método parametrizado quando ele não é usado - é um argumento YAGNI.
Erik Eidt

0

Há duas considerações a serem projetadas:

  • API: qual interface você apresenta ao usuário,
  • Implementação: clareza, manutenção, etc ...

Eles não devem ser confundidos.

É perfeitamente bom:

  • tem vários métodos no delegado da API para uma única implementação,
  • tenha um único método no despacho da API para várias implementações, dependendo da condição.

Como tal, qualquer argumento que tente equilibrar os custos / benefícios de um design de API pelos custos / benefícios de um design de implementação é duvidoso e deve ser examinado cuidadosamente.


No lado da API

Como programador, geralmente irei favorecer APIs programáveis. O código é muito mais claro quando posso encaminhar um valor do que quando preciso de um if/switch instrução no valor para decidir qual função chamar.

O último pode ser necessário se cada função esperar argumentos diferentes.

No seu caso, portanto, um único método setState(type value) parece melhor.

No entanto , não há nada pior do que sem nome true, false, 2, etc ... esses valores mágicas não têm significado por conta própria. Evite a obsessão primitiva e adote uma digitação forte.

Assim, a partir de um POV API, eu quero: setState(State state).


No lado da implementação

Eu recomendo fazer o que for mais fácil.

Se o método for simples, é melhor mantê-lo junto. Se o fluxo de controle for complicado, é melhor separá-lo em vários métodos, cada um lidando com um sub-caso ou uma etapa do pipeline.


Por fim, considere agrupar .

No seu exemplo (com espaço em branco adicionado para facilitar a leitura):

this.layer1.visible = isHintOn;
this.layer2.visible = ! isHintOn;
this.layer3.visible = isHintOn;

Porque é layer2 contrariando a tendência? É um recurso ou é um bug?

Seria possível ter 2 listas [layer1, layer3]e [layer2], com uma explicação explícita nome indicando o motivo pelo qual eles estão agrupados e, em seguida, iterar sobre essas listas.

Por exemplo:

for (auto layer : this.mainLayers) { // layer2
    layer.visible = ! isHintOn;
}
for (auto layer : this.hintLayers) { // layer1 and layer3
    layer.visible = isHintOn;
}

O código fala por si, é claro por que existem dois grupos e seu comportamento é diferente.


0

Separadamente do setOn() + setOff()vsset(flag) pergunta , eu consideraria cuidadosamente se um tipo booleano é melhor aqui. Tem certeza de que nunca haverá uma terceira opção?

Pode valer a pena considerar um enum em vez de um booleano. Além de permitir a extensibilidade, isso também torna mais difícil obter o booleano da maneira errada, por exemplo:

setHint(false)

vs

setHint(Visibility::HIDE)

Com o enum, será muito mais fácil estender quando alguém decide que deseja uma opção 'se necessário':

enum class Visibility {
  SHOW,
  HIDE,
  IF_NEEDED // New
}

vs

setHint(false)
setHint(true)
setHintAutomaticMode(true) // New

0

De acordo com ..., eu sei a importância de evitar o uso de parâmetros booleanos para determinar um comportamento

Eu sugeriria reavaliar esse conhecimento.

Antes de tudo, não vejo a conclusão que você está propondo na pergunta SE que você vinculou. Eles estão falando principalmente sobre o encaminhamento de um parâmetro em várias etapas de chamadas de método, onde é avaliado muito longe na cadeia.

No seu exemplo, você está avaliando o parâmetro diretamente no seu método. A esse respeito, não difere de nenhum outro tipo de parâmetro.

Em geral, não há absolutamente nada de errado em usar parâmetros booleanos; e obviamente qualquer parâmetro determinará um comportamento, ou por que você o teria em primeiro lugar?


0

Definindo a pergunta

Sua pergunta do título é "É errado [...]?" - mas o que você quer dizer com "errado"?

De acordo com um compilador C # ou Java, não está errado. Estou certo de que você está ciente disso e não é isso que você está perguntando. Receio que, além disso, só tenhamos opiniões diferentes dos nprogramadores n+1. Esta resposta apresenta o que o livro Clean Code tem a dizer sobre isso.

Responda

O Código Limpo apresenta argumentos fortes contra argumentos de função em geral:

Argumentos são difíceis. Eles tomam muito poder conceitual. [...] nossos leitores teriam que interpretá-lo cada vez que o vissem.

Um "leitor" aqui pode ser o consumidor da API. Também pode ser o próximo codificador, que ainda não sabe o que esse código faz - que pode ser você em um mês. Eles passarão por duas funções separadamente ou por uma função duas vezes , mantendo truee lembrando uma vez false.
Em resumo, use o mínimo de argumentos possível .

O caso específico de um argumento flag é posteriormente abordado diretamente:

Os argumentos da bandeira são feios. Passar um booleano para uma função é uma prática verdadeiramente terrível. Implica imediatamente a assinatura do método, proclamando em voz alta que essa função faz mais de uma coisa. Faz uma coisa se a bandeira for verdadeira e outra se a bandeira for falsa!

Para responder suas perguntas diretamente:
De acordo com o Clean Code , é recomendável eliminar esse parâmetro.


Informação adicional:

Seu exemplo é bastante simples, mas mesmo lá você pode ver a simplicidade propagando-se para o seu código: As funções no-parameter fazem apenas atribuições simples, enquanto a outra função precisa fazer aritmética booleana para atingir o mesmo objetivo. É aritmética booleana trivial neste exemplo simplificado, mas pode ser bastante complexo em uma situação real.


Eu já vi muitos argumentos aqui de que você deve torná-lo dependente do usuário da API, porque fazer isso em muitos lugares seria estúpido:

if (isAfterSunset) light.TurnOn();
else light.TurnOff();

Eu não concordo que algo não-ideal está acontecendo aqui. Talvez seja óbvio demais para ver, mas sua primeira frase está mencionando "a importância de evitar [sic] usar parâmetros booleanos para determinar um comportamento" e essa é a base para toda a pergunta. Não vejo uma razão para tornar mais fácil o que é ruim para o usuário da API.


Não sei se você faz testes - nesse caso, considere também o seguinte:

Os argumentos são ainda mais difíceis do ponto de vista de teste. Imagine a dificuldade de escrever todos os casos de teste para garantir que todas as várias combinações de argumentos funcionem corretamente. Se não houver argumentos, isso é trivial.


Você realmente escondeu o lede aqui: "... sua primeira frase está mencionando" a importância de evitar [sic] usar parâmetros booleanos para determinar um comportamento "e essa é a base para toda a pergunta. Não vejo um motivo para tornar mais fácil o que é ruim para o usuário da API ". Esse é um ponto interessante, mas você mina seu argumento no seu último parágrafo.
Curinga

Enterrou o lede? O núcleo desta resposta é "use o mínimo de argumentos possível", o que é explicado na primeira metade desta resposta. Tudo depois disso são apenas informações adicionais: refutar um argumento contraditório (por outro usuário, não OP) e algo que não se aplica a todos.
R. Schmitz

O último parágrafo está apenas tentando esclarecer que a questão do título não está bem definida o suficiente para ser respondida. O OP pergunta se está "errado", mas não diz de acordo com quem ou o quê. De acordo com o compilador? Parece um código válido, então não está errado. De acordo com o livro Código Limpo? Ele usa um argumento flag, então sim, está "errado". No entanto, eu escrevo "recomendado", porque é pragmático> dogmático. Você acha que eu preciso deixar isso mais claro?
R. Schmitz

então espere, sua defesa de sua resposta é que a pergunta do título não está clara para ser respondida? : D Ok ... Na verdade, eu pensei que o ponto que citei era uma nova perspectiva interessante.
Curinga

1
Vividamente claro agora; bom trabalho!
Curinga
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.