Por que precisamos de variáveis privadas nas classes?
Todo livro sobre programação que li diz que essa é uma variável privada, é assim que você a define, mas pára por aí.
A redação dessas explicações sempre me pareceu realmente ter uma crise de confiança em nossa profissão. As explicações sempre soaram como outros programadores que estão tentando bagunçar nosso código. No entanto, existem muitas linguagens de programação que não possuem variáveis privadas.
O que as variáveis privadas ajudam a impedir?
Como você decide se uma propriedade específica deve ser privada ou não? Se, por padrão, todos os campos DEVEM ser privados, por que existem membros de dados públicos em uma classe?
Em que circunstâncias uma variável deve ser tornada pública?
fonte
Respostas:
Não se trata tanto de confiança, mas de gerenciar a complexidade.
Um membro público pode ser acessado de fora da classe, o que por considerações práticas significa "potencialmente em qualquer lugar". Se algo der errado com um campo público, o culpado pode estar em qualquer lugar; portanto, para rastrear o bug, talvez seja necessário analisar bastante código.
Um membro privado, por outro lado, só pode ser acessado de dentro da mesma classe; portanto, se algo der errado com isso, geralmente haverá apenas um arquivo de origem para examinar. Se você possui um milhão de linhas de código em seu projeto, mas suas classes são mantidas pequenas, isso pode reduzir seu esforço de rastreamento de bugs em um fator de 1000.
Outra vantagem está relacionada ao conceito de 'acoplamento'. Um membro do público
m
de uma classeA
que é usado por outra classeB
introduz uma dependência: se você mudarm
emA
, você também tem que verificar usos dem
noB
. Pior ainda, nada na classeA
informa ondem
está sendo usado; então, novamente, você precisa pesquisar em toda a base de código; se é uma biblioteca que você está escrevendo, você precisa garantir o código foraseu projeto não falha por causa de sua alteração. Na prática, as bibliotecas tendem a permanecer com suas assinaturas de métodos originais o maior tempo possível, não importa o quão doloroso, e depois introduzir um bloco de alterações quebradas com uma atualização de versão principal. Com os membros privados, por outro lado, você pode excluir dependências imediatamente - elas não podem ser acessadas de fora, portanto todas as dependências estão contidas na classe.Nesse contexto, "outros programadores" incluem seu futuro e o passado. Provavelmente, você sabe agora que não deve fazer essa coisa X com sua variável Y, mas você deve ter esquecido três meses depois, quando um cliente precisa urgentemente que você implemente algum recurso, e você se pergunta por que fazer X quebra Y de maneiras obscuras.
Portanto, quando você deve tornar as coisas privadas: eu diria que tornar tudo privado por padrão e depois expõe apenas as partes que absolutamente precisam ser públicas. Quanto mais você puder tornar privado, melhor.
fonte
Variáveis privadas ajudam a impedir que as pessoas dependam de certas partes do seu código. Por exemplo, digamos que você queira implementar alguma estrutura de dados. Você deseja que os usuários da sua estrutura de dados não se importem com a forma como a implementou, mas apenas utilizem a implementação por meio de sua interface bem definida. O motivo é que, se ninguém depender da sua implementação, você poderá alterá-la quando quiser. Por exemplo, você pode alterar a implementação de back-end para melhorar o desempenho. Quaisquer outros desenvolvedores que dependem de suas implementações serão interrompidos, enquanto os usuários da interface ficarão bem. Ter a flexibilidade de alterar implementações sem afetar os usuários da classe é um grande benefício que o uso de variáveis privadas (e de forma mais abrangente, encapsulamento ) oferece a você.
Além disso, não é realmente uma "crise de confiança". Se você tornar público um dado, não poderá garantir que ninguém dependa dele. Muitas vezes, é muito conveniente depender de alguma variável específica da implementação, em vez de passar pela interface pública, especialmente no calor de um prazo. Além disso, os desenvolvedores nem sempre percebem que dependem de algo que pode mudar.
Portanto, esse tipo de resposta a suas outras perguntas, espero. Todos os detalhes de sua implementação devem ser privados e a parte pública deve ser uma interface pequena, concisa e bem definida para o uso de sua classe.
fonte
private
é menor que "não se destina principalmente à segurança", não fornece "segurança" para falar. É apenas uma dica forte (fornecida pelo compilador) para não acessá-lo.A palavra-chave aqui é Encapsulamento . No OOP, você deseja usar variáveis privadas para impor o encapsulamento adequado de seus objetos / classes.
Embora outros programadores não estejam buscando você, eles interagem com seu código. Se você não tornar uma variável privada, ela poderá se referir a ela em seu próprio código, sem querer causar nenhum dano. No entanto, se você precisar voltar para a aula e mudar alguma coisa, não saberá mais quem usa qual variável e onde. O objetivo do encapsulamento é tornar explícita a interface externa da classe, para que você saiba que apenas esses métodos (normalmente) poderiam ter sido usados por outras pessoas.
Portanto, variáveis privadas garantem que a variável correspondente permaneça apenas na classe de definição. Se você precisar alterá-lo, a alteração será local para a própria classe.
Em linguagens tradicionais como C ++ ou Java, você geralmente torna tudo privado e acessível apenas pelos getters e setters correspondentes. Não há necessidade de decidir muito realmente.
Às vezes, f.ex. em uma estrutura C ++, você precisa de uma classe apenas como uma maneira de agrupar várias coisas. Por exemplo, considere uma classe Vector que possua apenas
x
umy
atributo e. Nesses casos, você pode permitir acesso direto a esses atributos declarando-os públicos. Em particular, você não deve se importar se algum código externo modifica um objeto da sua classe escrevendo diretamente novos valores emx
ouy
.Como um ponto adicional, observe que esse assunto é considerado um pouco diferente em outros idiomas. Por exemplo, linguagens fortemente enraizadas na programação funcional enfatizam a imutabilidade dos dados, ou seja, os valores dos dados não podem ser alterados. Nesses casos, você não precisa se preocupar com o que outros programadores (ou o código deles) fazem com seus dados. Eles simplesmente não podem fazer nada que possa afetar seu código, porque todos os dados são imutáveis.
Nessas linguagens, você obtém algo chamado princípio de acesso uniforme , onde deliberadamente não distingue métodos como getters e setters, mas fornece acesso direto à variável / função. Então, você pode dizer que as declarações privadas são muito mais populares nos cenários orientados a objetos.
Isso também mostra como a ampliação de seu conhecimento para incluir outros campos faz com que você visualize os conceitos existentes de uma maneira totalmente nova.
fonte
Variáveis privadas garantem que referências posteriores fora do escopo de um objeto ou função não afetem inadvertidamente outras variáveis. Para grandes projetos de programação, isso pode ajudar a evitar muitos problemas interessantes (que geralmente não são detectados pelo compilador).
Por exemplo, linguagens prototípicas como o Javascript podem permitir que os usuários pesquisem variáveis conforme entenderem:
Se essa variável fosse privada, não poderia ser alterada de fora:
Dito isto, nem todas as variáveis precisam ser privadas e o uso excessivo de variáveis privadas pode realmente tornar o código mais complicado. Campos triviais que não afetam seriamente um objeto não precisam de métodos
get()
eset()
métodos.As variáveis privadas também facilitam a expansão do código no futuro, sem depender de documentação pesada sobre o que as muitas variáveis públicas fazem. Existe menos risco de quebrar acidentalmente a funcionalidade se menos variáveis puderem ser substituídas.
Variáveis públicas devem ser usadas quando um objeto / função acessa outros objetos / funções, pois, como regra, variáveis privadas não podem fazer isso. Você pode ter de algumas a várias variáveis públicas, e não é ruim usá-las se elas forem usadas corretamente.
fonte
Existem algumas boas respostas citando os benefícios para futuros mantenedores e usuários de uma classe. No entanto, também existem benefícios para o design original. Ele fornece um ponto de demarcação claro entre quando seu objetivo é facilitar as coisas para si mesmo e quando seu objetivo é facilitar as coisas para o usuário da classe. Quando você escolhe entre público e privado, ele sinaliza para o seu cérebro mudar para um modo ou outro, e você acaba com uma API melhor.
Não é que idiomas sem privacidade tornem esse design impossível, apenas menos provável. Em vez de ser solicitado a escrever algo como
foo.getBar()
, as pessoas tendem a pensar que um getter não é necessário, porque é mais fácil escreverfoo.bar
, mas essa decisão não é revisitada quando você acaba com monstruosidades mais longasobj.container[baz].foo.bar
. Programadores que nunca trabalharam em uma linguagem mais rígida podem nem perceber o que há de errado nisso.É por isso que em idiomas sem privacidade, as pessoas geralmente adotam padrões de nomes que simulam isso, como acrescentar um sublinhado a todos os membros "particulares". É um sinal útil, mesmo quando o idioma não o aplica.
fonte
Todas as variáveis devem ser privadas, a menos que sejam absolutamente públicas (o que quase nunca é necessário, você deve usar propriedades / getters e setters).
As variáveis fornecem amplamente o estado do objeto, e as variáveis privadas impedem que outros entrem e alterem o estado do objeto.
fonte
Trata-se de deixar claro quais propriedades e métodos são interface e quais são realmente as principais funcionalidades. Métodos / propriedades públicas são sobre outro código, geralmente o código de outro desenvolvedor usando seus objetos. Eu nunca trabalhei em equipes de mais de 100 no mesmo projeto, mas na minha experiência de talvez equipes de 3 a 5 com até 20 outros desenvolvedores usando coisas que eu escrevi, as outras preocupações parecem tolas.
Nota: Sou principalmente um desenvolvedor de JavaScript. Geralmente, não me preocupo com outros desenvolvedores que visualizam meu código e opero com plena consciência de que eles poderiam simplesmente redefinir minhas coisas a qualquer momento. Suponho que sejam competentes o suficiente para saber o que estão fazendo primeiro. Caso contrário, é improvável que eu estivesse trabalhando em uma equipe que não usa controle de origem.
Eu costumava pensar que era bobagem colocar getters e setters em propriedades privadas quando você não estava realmente realizando nenhum tipo de validação ou outro tratamento especial da propriedade a ser configurada. Ainda não acho que seja sempre necessário, mas quando você está lidando com larga escala, alta complexidade, mas o mais importante é que muitos outros desenvolvedores confiam nos seus objetos se comportando de maneira consistente, pode ser útil fazer as coisas de uma maneira maneira consistente e uniforme. Quando as pessoas vêem métodos chamados
getThat
esetThat
, eles sabem exatamente quais são as intenções e podem escrever seus objetos para interagir com os seus com a expectativa de que sempre consigam tomates em vez de tomahtos. Caso contrário, eles sabem que seu objeto está dando a eles algo que provavelmente não deveria, o que significa que está permitindo que outro objeto faça algo com esses dados que provavelmente não deveria. Você pode controlar isso conforme a necessidade.A outra grande vantagem é que é mais fácil ter seus objetos entregando ou interpretando valores de maneira diferente de um getter ou setter com base em algum tipo de contexto de estado variável. Como eles acessam suas coisas com um método, é muito mais fácil alterar a maneira como seu objeto opera mais tarde, sem quebrar outro código que depende dele. O princípio importante é que apenas o seu objeto realmente altera os dados pelos quais você o responsabilizou. Isso é particularmente importante para manter seu código fracamente acoplado, o que é uma grande vitória em termos de portabilidade e facilidade de modificação.
Com funções de primeira classe (você pode passar funções como argumentos), não consigo pensar em muitas razões excelentes além de não ser extremamente necessário fazê-lo em projetos de menor escala. Sem ele, suponho que você possa ter membros que estão sendo processados pesada e regularmente por outros objetos, na medida em que parece um pouco complicado chamar constantemente get e sets em um objeto que realmente não toca esses dados, mas que em em si me faria pensar por que o objeto era responsável por esses dados. De um modo geral, eu tenderia a reexaminar minha arquitetura quanto a falhas antes de decidir que uma propriedade de dados públicos é necessária. Na IMO, não há nada errado com um idioma que permita fazer algo que geralmente é uma má ideia. Alguns idiomas discordam.
fonte
int[]
,double[]
,Object[]
, etc. Deve-se, no entanto, ser muito cuidadosos sobre como se expõe casos de tal classe. O significado devoid CopyLocationTo(Point p);
[aceitar umPoint
do chamador] é mais claro quePoint location()
, uma vez que não está claro qual efeitoPoint pt = foo.location(); pt.x ++;
terá no local defoo
.Veja este post meu para uma pergunta relacionada ao SO .
O escopo é que o escopo variável permite mostrar aos consumidores do seu código o que eles devem e não devem estar brincando. Uma variável privada pode conter dados que foram "controlados" usando um configurador de propriedades ou um método de processamento para garantir que o objeto inteiro esteja em estado consistente. Alterar o valor da variável privada diretamente pode fazer com que o objeto se torne inconsistente. Ao torná-lo privado, alguém precisa trabalhar muito e ter permissões de tempo de execução muito altas para alterá-lo.
O escopo adequado dos membros é, portanto, essencial no código de auto-documentação.
fonte
Vou falar sobre o valor do encapsulamento de uma perspectiva diferente, usando um exemplo real. Há muito tempo (início dos anos 80), um jogo foi escrito para o Radio Shack Color Computer chamado Dungeons of Daggorath. Alguns anos atrás, Richard Hunerlach o transportou de uma montadora de papel listando para C / C ++ ( http://mspencer.net/daggorath/dodpcp.html ). Algum tempo depois, obtive o código e comecei a fatorá-lo novamente para fornecer um melhor encapsulamento ( http://dbp-consulting.com/stuff/ ).
O código já foi fatorado em objetos diferentes para lidar com agendamento, vídeo, entrada do usuário, criação de masmorras, monstros etc. mas você definitivamente pode dizer que foi portado pelo assembler. Minha maior tarefa era aumentar o encapsulamento. Com isso, pretendo obter diferentes partes do código para tirar o nariz dos negócios um do outro. Havia muitos lugares, por exemplo, que alteravam as variáveis de vídeo diretamente para ter algum efeito. Alguns fizeram isso com verificação de erros, outros não, alguns tiveram idéias sutilmente diferentes sobre o que significava mudar isso. Houve muita duplicação de código. Em vez disso, a seção de vídeo precisava ter uma interface para realizar as tarefas desejadas, e o código para fazer isso pode estar em um só lugar, uma ideia, um lugar para depuração. Esse tipo de coisa era galopante.
Quando o código começou a ser separado, os bugs que ainda não haviam sido diagnosticados desapareceram. O código se tornou de maior qualidade. Cada tarefa passou a ser de responsabilidade de um lugar no código e havia apenas um lugar para acertar. (Ainda encontro mais sempre que faço outra passagem pelo código.)
Tudo o que é visível sobre o seu código é a sua interface. Quer você queira que seja ou não. Se você limitar a visibilidade o máximo possível, não será tentado a colocar o código no lugar errado mais tarde. Torna óbvio qual parte do código é responsável por quais dados. Ele cria um design melhor, mais limpo, mais simples e mais elegante.
Se você responsabiliza um objeto por seus próprios dados e fornece interfaces para todos os outros que precisam que algo aconteça, seu código fica MUITO mais simples. Apenas cai. Você tem pequenas rotinas simples que fazem apenas uma coisa. Menos complexidade == menos erros.
fonte
Não tem nada a ver com confiança ou medo de ataques, é apenas sobre encapsulamento - não forçando informações desnecessárias sobre o usuário da classe.
Considere constantes privadas - elas não devem conter valores secretos (eles devem ser armazenados em outro lugar), não podem ser alterados, não precisam ser passados para a sua classe (caso contrário, teriam que ser públicos). O único uso possível para eles é como constantes em OUTRAS classes. Mas se você fizer isso, essas classes agora dependem da sua classe, para realizar um trabalho não relacionado à sua classe. Se você alterar a constante, outras classes poderão quebrar. Isso é ruim para os dois lados - como escritor da sua turma, você quer a liberdade de mudar o máximo possível e não quer se preocupar com coisas fora do seu controle. Um consumidor da sua classe deseja poder depender dos detalhes expostos da sua classe, sem se preocupar com a alteração e a quebra do código.
Um consumidor da sua classe quer saber tudo o que é necessário para interagir com a sua classe e não quer saber nada sobre a sua classe que não mude a maneira como eles fazem isso: é uma trivialidade inútil. Se você usou um idioma com reflexão, com que frequência o aprendeu não como uma classe faz alguma coisa ou onde está lançando uma exceção inesperadamente, mas simplesmente o nome dos campos e métodos particulares? Eu estou apostando nunca. Porque você não tem utilidade para esse conhecimento.
fonte
O conceito OOP tem herança e tem um de seus recursos (Java ou C ++). Portanto, se vamos herdar (o que significa que vamos acessar as variáveis da classe herdada), existe a possibilidade de afetar essas variáveis. Portanto, temos que decidir se as variáveis podem ser alteradas ou não.
Apenas para esse fim, estamos usando modificadores de acesso no OOP. Um dos modificadores é privado, o que significa que ele pode ser acessado apenas por essa classe. Qualquer outra classe não pode afetar essas variáveis.
Como sabemos, protegido significa que ele pode ser acessado pela classe que vai herdar.
O motivo pelo qual existe um conceito modificador no OOP é devido a esses motivos (qualquer classe pode acessar outras variáveis de classe usando o conceito OOP). Se não houver um conceito de modificador, significa que teria sido difícil herdar classes ou usar outros conceitos de OOP.
fonte
Conforme declarado por outros, variáveis privadas são boas para evitar erros de uso que levam o objeto a um status inconsistente e difíceis de rastrear bugs e exceções imprevistas.
Mas, por outro lado, o que tem sido principalmente ignorado pelos outros é sobre campos protegidos.
Uma subclasse estendida terá acesso total aos campos protegidos, tornando o objeto tão frágil como se esses campos fossem públicos, mas essa fragilidade é limitada à própria classe de extensão (a menos que exponha ainda mais esses campos).
Portanto, os campos públicos são difíceis de serem considerados bons e, até a data, o único motivo para usá-los é nas classes usadas como parâmetro de configuração (uma classe muito simples, com muitos campos e nenhuma lógica, para que a classe seja passada apenas como um parâmetro para algum método).
Mas, por outro lado, os campos privados diminuem a flexibilidade do seu código para outros usuários.
Flexibilidade vs problemas, prós e contras:
Objetos instanciados pelo seu código na classe vanilla com campos protegidos são seguros e são de sua exclusiva responsabilidade.
Por outro lado, os objetos que estendem sua classe com campos protegidos, instanciados pelos usuários do seu código, são de responsabilidade deles, não sua.
Portanto, campos / métodos protegidos não bem documentados, ou se os usuários não entenderem realmente como esses campos e métodos devem ser usados, têm uma boa chance de causar problemas desnecessários a eles e a você.
Por outro lado, tornar a maioria das coisas privadas reduzirá a flexibilidade dos usuários e pode até afastá-los à procura de alternativas mantidas, pois eles podem não querer criar e manter uma bifurcação apenas para que as coisas aconteçam do seu jeito.
Portanto, um bom equilíbrio entre privado, protegido e público é o que realmente importa.
Agora, decidir entre privado e protegido é o verdadeiro problema.
Quando usar protegido?
Sempre que você entender que um campo pode ser altamente flexível, ele deve ser codificado como protegido. Essa flexibilidade é: de se tornar nulo (onde nulo é sempre verificado e reconhecido como um estado válido, sem gerar exceções), a ter restrições antes de ser usado por sua classe ex. > = 0, <100 etc, e corrigido automaticamente para valores acima / abaixo do fluxo, emitindo no máximo uma mensagem de aviso.
Portanto, para esse campo protegido, você pode criar um getter e usá-lo apenas (em vez de usar diretamente a variável de campo), enquanto outros usuários podem não usá-lo, caso desejem mais flexibilidade para seu código específico, no meu exemplo pode ser : se eles querem que valores negativos funcionem bem em sua classe estendida.
fonte
imo @tdammers não está certo e é realmente enganador.
Existem variáveis privadas para ocultar os detalhes da implementação. Sua turma
A
pode estar usando umarray
para armazenar pontuações. Amanhã você pode querer usar umtree
ou empriority queue
vez disso. Todos os usuários de sua classe precisam de uma maneira de inserir pontuações e nomesaddScore()
e uma maneira de descobrir quem são os 10 melhoresgetTop(n)
.Eles não precisam acessar a estrutura de dados subjacente. Parece bom? Bem, existem algumas ressalvas.
Você não deve armazenar muito estado de qualquer maneira, a maior parte não é necessária. O estado de armazenamento complicará seu código mesmo quando ele estiver "segregado" para uma classe específica. Pense bem, essa variável privada provavelmente mudou porque outro objeto chamou um método dessa classe. Você ainda precisa descobrir quando e onde esse método foi chamado e esse método público pode ser chamado em qualquer lugar, teoricamente.
A melhor coisa que você pode fazer é limitar a quantidade de estado que seu programa contém e usar funções puras sempre que possível.
fonte
fonte
Você deve primeiro entender o conceito de programação orientada a objetos. Possui abstração, encapsulamento etc.
Abstração - Você obtém a idéia da lógica sem precisar saber detalhes sublinhados da implementação.
Encapsulamento - Você não pode ver a implementação sublinhada do objeto. Somente você pode ver a interface pública do objeto.
Agora em implementação específica com um programa orientado a objetos como C #, Java, C ++, você pode implementar esses conceitos.
private - A implementação que deve ser oculta do mundo exterior. Para que você possa mudar isso e o usuário da sua classe não seja afetado.
public - Esta é a interface pela qual se pode usar seu objeto.
fonte