Se uma variável tiver getter e setter, ela deve ser pública?

23

Eu tenho uma classe com uma variável que é privada e a classe tem um getter e um setter para essa variável. Por que não tornar pública essa variável?

O único caso em que acho que você precisa usar getters e setters é se precisar fazer alguma operação além do set ou do get. Exemplo:

void my_class::set_variable(int x){
   /* Some operation like updating a log */
   this->variable = x;
}
Oni
fonte
10
Getters e setters estão lá para proteger. Algum dia você pode querer fazer this->variable = x + 5ou chamar uma UpdateStatisticsfunção no setter e, nesses casos classinstancea->variable = 5, causará problemas.
Coder
1
Outro exemplo é: set_inch, set_centimeter, get_inch, get_centimeter, com algumas ações assustadores.
rwong
Sim, getters e setters ajudam a controlar as variáveis ​​da instância da maneira que você deseja que elas sejam controladas.
developerdoug
7
@ Codificador: você sempre pode adicionar seu getter / setter quando necessário. Ou isso é algo que você não pode fazer facilmente em C ++ (minha experiência é principalmente em Delphi)?
Marjan Venema
Esta pergunta: programmers.stackexchange.com/questions/21802/… pode ser interessante.
Winston Ewert

Respostas:

21

Você já ouviu falar sobre uma propriedade?

Uma propriedade é um campo que possui acessadores "internos" (getters e setters). Java, por exemplo, não possui propriedades, mas é recomendável gravar os getters e setters em um campo privado. C # tem propriedades.

Então, por que precisamos de getters e setters? Basicamente, precisamos dela para proteger / proteger o campo. Por exemplo, você não está acessando o campo na referência de memória, está acessando um método que alterará o campo (referência). Esse método é capaz de executar algumas operações que um usuário não está disposto a conhecer ( comportamento de encapsulamento ), como no seu exemplo. Imagine, por exemplo, que uma dúzia de classes use seu campo público e você precise alterar a maneira como é usado ... Você precisaria examinar cada uma dessas classes para alterar a maneira como elas estão usando o campo ... Não então "OOlysh".

Mas, por exemplo, se você tiver um campo booleano chamado dead. Você deve pensar duas vezes antes de declarar um setDead e um isDead. Você deve escrever acessadores legíveis por humanos , por exemplo, kill () em vez de setDead.

No entanto, existem muitas estruturas que assumem que você está seguindo a convenção de nomenclatura JavaBean (falando sobre Java aqui); portanto, nesses casos, você deve declarar todos os getters e setters após a convenção de nomenclatura.

wleao
fonte
2
Agora, "legível por humanos" é finalmente um argumento que eu posso "grunhir". Todos os outros argumentos eu costumo ouvir são algum tipo de futuro prova que são tão facilmente abordadas quando surge a necessidade ...
Marjan Venema
13
Seu desdém por "provas futuras" é comum, mas equivocado. Sim, você pode resolver essas alterações quando necessário, se você for o único cliente desse código . Se você nunca publicar sua solução para clientes externos, poderá simplesmente contrair dívidas técnicas e ninguém precisará saber. Mas os projetos internos costumam se tornar públicos quando você menos espera, e ai de você.
Kilian Foth 01/08/19
2
Você realmente levanta um ponto fantástico. kill não é um set / get, é uma ação. Oculta completamente a implementação - é como você codifica OO. Propriedades, por outro lado, são tão estúpidas quanto setters / getters - mais estúpidas, mesmo porque a sintaxe fácil incentiva as pessoas a adicioná-las às variáveis ​​quando elas nem são necessárias (OO-Furbar esperando para acontecer). Quem pensaria em usar kill () quando poderia simplesmente adicionar uma tag "property" à sua bandeira morta?
Bill K
1
A pergunta está marcada como C ++. O C ++ não suporta propriedades, então por que você postaria isso?
Thomas Eding
1
@ Thomashoding: C ++ é um pouco único em ter operadores de atribuição substituíveis. Enquanto C ++ não usa o termo "propriedade" da mesma maneira que linguagens como C # ou VB.NET, o conceito ainda está lá. É possível no C ++ usar a sobrecarga do operador para que foo.bar = biz.baz+5uma função seja chamada bizpara avaliar baz, adicione cinco a ela e chame uma função foopara definir baro valor resultante. Tais sobrecargas não são chamadas de "propriedades", mas podem servir ao mesmo objetivo.
11125
25

Esta não é a opinião mais popular, mas não vejo muita diferença.

Setters e getters são uma péssima idéia. Eu pensei sobre isso e honestamente não consigo chegar a uma diferença entre um levantador / levantador de uma propriedade e uma variável pública na prática.

Em THEORY, um setter e getter ou propriedade adicionam um local para executar algumas ações extras quando uma variável é configurada / obtida e, em teoria, eles isolam seu código de alterações.

Na realidade, raramente vejo setters e getters usados ​​para adicionar uma ação, e quando você deseja adicionar uma ação, você deseja adicioná-la a TODOS os setters ou getters de uma classe (como log), o que deve fazer você pensar que deve ser uma solução melhor.

Quanto ao isolamento das decisões de projeto, se você alterar um int por muito tempo, ainda precisará alterar seus setters e, pelo menos, verificar todas as linhas que os acessam manualmente - não há muito isolamento lá.

As classes mutáveis ​​devem ser evitadas por padrão de qualquer maneira, portanto, adicionar um setter deve ser o último recurso. Isso é mitigado com o padrão do construtor, em que um valor pode ser definido até que o objeto esteja no estado desejado, para que a classe se torne imutável e seus configuradores lançem exceções.

Quanto aos getters - ainda não consigo encontrar muita diferença entre um getter e uma variável final pública. O problema aqui é que é ruim OO em ambos os casos. Você não deve estar pedindo um valor a um objeto e operando nele - você deve estar pedindo a um objeto que faça uma operação para você.

A propósito, não estou de maneira alguma defendendo variáveis ​​públicas - estou dizendo que setters e getters (e até propriedades) estão muito perto de já serem variáveis ​​públicas.

O grande problema é simplesmente que as pessoas que não são programadores de OO ficam tentadas a usar setters e getters para transformar objetos em bolas de propriedades (estruturas) que são passadas e operadas, praticamente o oposto de como o código orientado a objetos funciona.

Bill K
fonte
Uma coisa boa, além de outras respostas aqui, sobre o getter setter é: posso adicionar algumas validações / conversões dentro dele, pois a variável privada real assume seu valor.
Winw
1
@ Kiran true, mas se você configurá-lo no construtor, poderá ter validações E ter uma classe imutável. Para os setters, não estou dizendo que uma variável pública gravável é boa, estou dizendo que até os setters são ruins. Para getters, posso considerar uma variável final pública uma alternativa viável.
Bill K
Em alguns idiomas como C #, as propriedades não são compatíveis com binárias com variáveis ​​públicas; portanto, se você faz uma variável pública parte de sua API, mas deseja adicionar alguma validação, interromperá o programa do cliente ao convertê-lo em uma propriedade. Melhor torná-lo uma propriedade pública desde o início.
Robert Harvey
@RobertHarvey Isso ainda significa que você está expondo propriedades. Meu argumento principal era que a exposição de propriedades deveria ser evitada e, quando necessário, você deveria tentar restringi-las a getters e tornar sua classe imutável. Se sua classe é imutável, por que você precisaria de código em um getter - e, portanto, segue-se que você também pode não ter um getter. Se você é incapaz de escrever uma classe sem propriedades mutáveis ​​expostas, sim, um levantador é sempre melhor que uma variável pública gravável (e levar um tiro no pé é sempre melhor do que ser esfaqueado no estômago).
Bill K
1
O estado mutável é aceitável, mas, novamente, você deve solicitar que seu objeto faça algo por você, não faça algo com seu objeto - portanto, quando você modifica seu estado, um setter não é uma ótima maneira de fazê-lo, tente escrever um método comercial com lógica nele em vez disso. Por exemplo, "move (Up5) em vez de setZLocation (getZLocation () + 5)".
Bill K
8

O usuário de getters e setters entra no princípio do encapsulamento . Isso permitirá que você altere como as coisas funcionam dentro da classe e mantenha tudo funcionando.

Por exemplo, se três outros objetos chamarem foo.barpara obter o valor da barra e você decidir alterar o nome da barra para longe, terá um problema em suas mãos. Se os objetos chamados, foo.barvocê teria que alterar todas as classes que possuem isso. Se um setter / getter for usado, você não terá nada para mudar. Outra possibilidade é alterar o tipo da variável; nesse caso, basta adicionar algum código de transformação ao getter / setter e você estará bem.


fonte
1
+1 - a variável membro é implementação, getter e setter são interface. Somente a interface deve ser pública. Além disso, os nomes de getter e setter devem fazer sentido em termos de abstração, independentemente de haver uma variável de membro simples subjacente a eles ou alguma outra implementação. Não são exceções - casos em que um subsistema construído a partir de classes rigidamente acoplados é a maneira mais fácil - mas isso apenas empurra o limite de dados de esconderijo para um nível acima de qualquer maneira, para a classe que representa todo o subsistema.
Steve314
Eu não poderia simplesmente apresentar o getter e / ou setter quando preciso alterar o trabalho dentro da classe? É algo que é feito facilmente em Delphi, mas talvez não em outros idiomas?
Marjan Venema
@ marjan Claro, você pode apresentar o getter / setter a qualquer momento posteriormente. O problema é que você precisa voltar e alterar TODO o código que estava usando o campo público em vez de getter / setter. Não tenho certeza se sou eu quem está confusa ou você. 0o
Pete
3
Pessoalmente, acho que este é um argumento um pouco falso. Se você alterar o significado de uma variável, mas a variável tiver um getter e um setter, não importa como é chamado. Não é uma parte visível da interface. É muito mais provável que você queira alterar o nome dos getters e setters para indicar aos clientes a alteração ou esclarecimento do significado da variável encapsulada. Neste último caso, você não está em melhor posição do que com uma variável pública. Isso está além do argumento de que uma variável com getters e setters está mais exposta do que encapsulada.
CB Bailey
1
Uma operação de refatoração é praticamente gratuita de qualquer maneira, então eu realmente não compro esta. Além disso, é uma péssima forma usar um levantador ou levantador, a menos que seja absolutamente necessário - eventualmente, as pessoas que não entendem o conceito adicionam automaticamente levantadores e levantadores para os membros, em vez de quando são necessários, o que está apenas pingando sementes do mal em toda a sua base de código.
Bill K
5

O uso de getters e setters também permite controlar o conteúdo que é armazenado em uma variável específica. Se o conteúdo precisar ser de um determinado tipo ou valor, parte do seu código de configuração pode garantir que o novo valor atenda a esses requisitos. Se a variável for pública, não será possível garantir que esses requisitos sejam atendidos.

Essa abordagem também torna seu código mais adaptável e gerenciável. É muito mais fácil fazer alterações na arquitetura de uma classe se você tiver funções que mantêm essa arquitetura oculta de todas as outras classes ou funções que utilizam essa classe. A mudança já mencionada de um nome de variável é apenas uma das muitas mudanças que são muito mais fáceis de fazer se você tiver funções como getters e setters. A idéia geral é manter o máximo de privacidade possível, especialmente as variáveis ​​de classe.

Kenneth
fonte
Muito obrigado! Eu estava pensando o mesmo caso que você mencionou. Se eu quiser uma variável que se contentar em [0,1] que eu deveria verificar o valor de entrada em seu setter :)
Oni
Quando você é o único desenvolvedor que trabalha em um projeto, é fácil pensar que coisas como essa são inúteis, mas quando você se encontra trabalhando com uma equipe, verá que o hábito de técnicas como essa será muito útil e ajudará você evite muitos erros no caminho.
Kenneth
Se você estiver usando C #, use properties, poderá usar uma propriedade para definir uma instância privada, se necessário, com base em alguma lógica na parte do setter da propriedade.
developerdoug
2

Você diz "O único caso em que acho que você precisa usar getters e setters é se você precisar fazer alguma operação além do set ou do get".

Você deve usar getters e setters se em algum momento no futuro, você pode precisar fazer alguma operação além do set e get e você não deseja alterar milhares de linhas de código-fonte, quando isso acontece.

Você deve usar getters e setters se não quiser que alguém pegue o endereço da variável e o repasse, com conseqüências desastrosas se essa variável puder ser alterada sem nenhum código fonte mencioná-la ou mesmo depois que o objeto deixar de existir. .

gnasher729
fonte
Seu último parágrafo faz uma excelente observação, que se divide nos dois sentidos: exigir o uso de getters e setters impedirá que outro código tome o endereço de algo. Nos casos em que haveria benefícios significativos e poucas desvantagens em ter o código externo tomando os endereços das coisas, essa restrição poderia ser uma coisa ruim. se houvesse poucos benefícios e graves desvantagens em permitir esse comportamento, restringi-lo poderia ser uma coisa boa.
11123
1

Primeiro, vamos deixar claro o paradigma.

  • Estruturas de dados -> um layout de memória que pode ser percorrido e manipulado por funções com conhecimento adequado.
  • Objetos -> um módulo independente que oculta sua implementação e fornece uma interface que pode ser comunicada.

Onde um getter / setter é útil?

Os getters / setters são úteis nas estruturas de dados? Não.

Uma estrutura de dados é uma especificação de layout de memória comum e manipulada por uma família de funções.

Geralmente, qualquer nova função antiga pode surgir e manipular uma estrutura de dados, se o fizer de uma maneira que as outras funções ainda possam entendê-la, então a função se juntará à família. Caso contrário, é uma função desonesta e uma fonte de bugs.

Não me interpretem mal, poderia haver várias famílias de funções lutando sobre essa estrutura de dados com delatores, casacos de turno e agentes duplos em todos os lugares. Tudo bem quando cada um tem sua própria estrutura de dados para brincar, mas quando o compartilha ... imagine várias famílias de criminosos discordando sobre política, pode se tornar uma bagunça muito rápida.

Dada a bagunça que as famílias de funções estendidas podem alcançar, existe uma maneira de codificar a estrutura de dados para que as funções não autorizadas não estraguem tudo? Sim, eles são chamados de objetos.

Os getters / setters são úteis nos objetos? Não.

O objetivo de agrupar uma estrutura de dados em um objeto era garantir que nenhuma função invasora pudesse existir. Se a função quisesse se juntar à família, ela teria que ser cuidadosamente examinada primeiro e depois se tornar parte do objeto.

O objetivo / objetivo de um getter e de um setter é permitir que funções fora do objeto alterem diretamente o layout da memória do objeto. Isso soa como uma porta aberta para permitir que bandidos ...

The Edge Case

Existem duas situações em que um getter / setter público faz sentido.

  • Uma parte da estrutura de dados dentro do objeto é gerenciada pelo objeto, mas não é controlada pelo objeto.
  • Uma interface que descreve uma abstração de alto nível de uma estrutura de dados em que se espera que alguns elementos não estejam no controle do objeto de implementação.

Contêineres e interfaces de contêiner são exemplos perfeitos dessas duas situações. O contêiner gerencia internamente as estruturas de dados (lista vinculada, mapa, árvore), mas passa o controle sobre o elemento específico para todos. A interface abstrai isso e ignora completamente a implementação e descreve apenas as expectativas.

Infelizmente, muitas implementações entendem isso errado e definem a interface desses tipos de objetos para fornecer acesso direto ao objeto real. Algo como:

interface Container<T>
{
    typedef ...T... TRef; //<somehow make TRef to be a reference or pointer to the memory location of T
    TRef item(int index); 
}

Isto está quebrado. As implementações do Container devem entregar explicitamente o controle de seus internos a quem os usar. Ainda não vi uma linguagem de valor mutável em que isso seja bom (linguagens com semântica de valor imutável são, por definição, ótimas do ponto de vista de corrupção de dados, mas não necessariamente do ponto de vista de espionagem de dados).

Você pode melhorar / corrigir os getters / setter usando apenas cópia semântica ou usando um proxy:

interface Proxy<T>
{
     operator T(); //<returns a copy
     ... operator ->(); //<permits a function call to be forwarded to an element
     Proxy<T> operator=(T); //< permits the specific element to be replaced/assigned by another T.
}

interface Container<T>
{
     Proxy<T> item(int index);
     T item(int index); //<When T is a copy of the original value.
     void item(int index, T new_value); //<where new_value is used to replace the old value
}

Indiscutivelmente, uma função não autorizada ainda pode causar confusão aqui (com esforço suficiente, a maioria das coisas é possível), mas a cópia-semântica e / ou proxy reduz a chance de vários erros.

  • transbordar
  • underflow
  • as interações com o subelemento são verificadas por tipo / verificáveis ​​por tipo (no tipo perder idiomas, isso é um benefício)
  • O elemento real pode ou não ser residente na memória.

Getters / Setters privados

Este é o último bastião de getters e setters trabalhando diretamente no tipo. Na verdade, eu nem chamaria esses getters e setters, mas acessores e manipuladores.

Nesse contexto, às vezes, manipular uma parte específica da estrutura de dados sempre / quase sempre / geralmente exige que ocorra manutenção contábil específica. Digamos que quando você atualiza a raiz de uma árvore, o cache oculto precisa ser eliminado ou quando você acessa o elemento de dados externos, um bloqueio precisa ser obtido / liberado. Nesses casos, faz sentido aplicar o principal DRY e agrupar essas ações.

Dentro do contexto privado, ainda é possível para as outras funções da família desviar esses 'getters e setters' e manipular a estrutura de dados. Por isso, penso neles mais como acessores e manipuladores. Você pode acessar os dados diretamente ou confiar em outro membro da família para acertar essa parte.

Getters / Setters protegidos

Em um contexto protegido, não é muito diferente de um contexto público. Funções estrangeiras possivelmente desonestas desejam acessar a estrutura de dados. Portanto, não, se existem, eles operam como getters / setters públicos.

Kain0_0
fonte
0

Por experiência em C ++, o setter / getter é útil para 2 cenários:

  1. se você precisar executar uma verificação de sanidade antes de atribuir valor ao membro, como seqüências de caracteres ou matriz.
  2. pode ser útil quando a sobrecarga de função é necessária, como valor int para atribuição booleana quando -1 (ou valores negativos) for falso. e vice-versa para getter.

Além disso, a segurança é um ponto válido, mas sua importância é limitada a muito poucos aplicativos de desenvolvimento, como módulos relacionados ao login ou acesso ao banco de dados. Por que se preocupar em codificar extra quando apenas poucas pessoas usam seu módulo?

Ajay nandoriya
fonte