A visibilidade private
dos campos / propriedades / atributos da classe é útil? No OOP, mais cedo ou mais tarde, você criará uma subclasse de uma classe e, nesse caso, é bom entender e poder modificar completamente a implementação.
Uma das primeiras coisas que faço quando subclasses uma classe é alterar vários private
métodos para protected
. No entanto, esconder detalhes do mundo exterior é importante - portanto, precisamos protected
e não apenas public
.
Minha pergunta é: Você conhece um caso de uso importante em que, em private
vez de protected
ser uma boa ferramenta, ou duas opções " protected
& public
" seriam suficientes para idiomas OOP?
object-oriented
encapsulation
access-modifiers
Adam Libuša
fonte
fonte
Respostas:
Porque, como você diz,
protected
ainda deixa você com a capacidade de "modificar completamente a implementação". Não protege genuinamente nada dentro da classe.Por que nos preocupamos em "proteger genuinamente" as coisas dentro da classe? Porque, caso contrário , seria impossível alterar os detalhes da implementação sem quebrar o código do cliente . Em outras palavras, as pessoas que escrevem subclasses também são "o mundo exterior" da pessoa que escreveu a classe base original.
Na prática, os
protected
membros são essencialmente uma "API pública para subclasses" da classe e precisam permanecer estáveis e compatíveis com versões anteriores tanto quanto ospublic
membros. Se não tivéssemos a capacidade de criarprivate
membros verdadeiros , nada em uma implementação seria seguro para mudar, porque você não seria capaz de descartar a possibilidade de que o código do cliente (não malicioso) de alguma forma tenha conseguido depender isto.Aliás, enquanto "No POO, mais cedo ou mais tarde, você fará uma subclasse de uma classe" é tecnicamente verdadeiro, seu argumento parece estar assumindo muito mais fortemente que "mais cedo ou mais tarde, você fará uma subclasse de toda classe ", o que quase certamente não é o caso.
fonte
private
que realmente faz sentido para mim. A perspectiva da lib precisa ser usada com mais frequência; caso contrário, qualquer pessoa que goste da mentalidade de codificação "controle absoluto, responsabilidade absoluta" (tipo C) pode sinalizá-la como "me protege de mim". Observe que eu ainda usavaprivate
anteriormente, sempre senti uma boa documentação e uma convenção de nomenclatura gostaria_foo
de indicar que você provavelmente não deveria estar mexendo nela era equivalente, se não melhor. Ser capaz de dizer deterministicamente "nada vai quebrar" é umprivate
recurso legítimo e único.Isto está errado. Nem todas as classes devem ser subclassificadas e algumas linguagens OOP de tipo estaticamente têm até recursos para evitá-la, por exemplo,
final
(Java e C ++) ousealed
(C #).Não, não é. É bom que uma classe seja capaz de definir claramente sua interface pública e preservar seus invariantes, mesmo que sejam herdados.
Em geral, o controle de acesso é sobre compartimentação. Você deseja que uma parte individual do código seja entendida sem precisar entender em detalhes como ela interage com o restante do código. O acesso privado permite isso. Se tudo estiver ao menos protegido, você precisará entender o que cada subclasse faz para entender como a classe base funciona.
Ou, nos termos de Scott Meyers: partes particulares de uma classe são afetadas por uma quantidade finita de código: o código da própria classe.
As partes públicas são potencialmente afetadas por todo bit de código existente e todo bit de código ainda a ser gravado, que é uma quantidade infinita de código.
As partes protegidas são potencialmente afetadas por todas as subclasses existentes e todas as subclasses ainda a serem gravadas, o que também é uma quantidade infinita de código.
A conclusão é que o protegido oferece muito pouco mais que o público, enquanto o privado oferece um aprimoramento real. É a existência do especificador de acesso protegido que é questionável, não privado.
fonte
virtual
, pelas razões descritas nesta resposta.protected
é para métodos, não para membros de dados.sealed
grandes partes da biblioteca da .net e do IIRC do MS, por que ele gostaria de ter trancado o resto. No momento em que você permite que os herdeiros de terceiros comecem a tocar nos valores internos, é necessário adicionar uma enorme quantidade de verificações de validação / sanidade a todos os métodos, porque você não pode mais confiar em nenhum invariável de design sobre o estado interno dos objetos.Sim, campos privados são absolutamente necessários. Apenas nesta semana, eu precisava escrever uma implementação de dicionário personalizada, onde eu controlava o que era colocado no dicionário. Se o campo do dicionário fosse tornado protegido ou público, os controles que eu escrevi com tanto cuidado poderiam ter sido facilmente contornados.
Os campos particulares geralmente fornecem garantias de que os dados são os esperados pelo codificador original. Torne tudo protegido / público e você montará um treinador e cavalos através desses procedimentos e validação.
fonte
private
é apenas uma maneira de dizer "não toque neles" e aplicá-lo dentro do contrato do compilador. Em sistemas como o .NET, isso também tem implicações importantes de segurança - você só pode tocar em particular de outras pessoas quando tiver confiança total (basicamente o equivalente ao acesso de administrador / raiz).Ao tentar argumentar formalmente sobre a correção de um programa Orientado a Objetos , é típico usar uma abordagem modular envolvendo invariantes de objetos . Nesta abordagem
O raciocínio modular sobre um objeto procede da seguinte forma (para uma primeira aproximação, pelo menos)
Imagine que verifiquemos
object A
usando a abordagem acima. E agora deseja verificarmethod g
doobject B
que chamamethod f
deobject A
. O raciocínio modular nos permite raciocinarmethod g
sem ter que reconsiderar a implementação demethod f
. Desde que possamos estabelecer a invarianteobject A
e a pré-condiçãomethod f
no local da chamadamethod g,
, podemos considerar a condição de postagemmethod f
como um resumo do comportamento da chamada do método. Além disso, também saberemos que, após a chamada retornar, o invariante deA
ainda se mantém.Essa modularidade do raciocínio é o que nos permite pensar formalmente sobre grandes programas. Podemos raciocinar sobre cada um dos métodos individualmente e depois compor os resultados desse raciocínio, por sua vez, para raciocinar sobre partes maiores do programa.
Os campos particulares são muito úteis nesse processo. Para saber que a invariante de um objeto continua retida entre duas chamadas de método nesse objeto, normalmente contamos com o fato de que o objeto não é modificado no período intermediário.
Para que o raciocínio modular funcione em um contexto em que os objetos não possuam campos particulares, teríamos que ter alguma maneira de garantir que, independentemente do que um campo fosse definido por outro objeto, a invariante fosse sempre restabelecida (após o campo conjunto). É difícil imaginar um objeto invariável que ambos retenha, independentemente do valor que os campos do objeto tenham, e também é útil para raciocinar sobre a correção do programa. Provavelmente teríamos que inventar uma convenção complicada sobre o acesso ao campo. E provavelmente também perderá parte (na pior das hipóteses, toda) de nossa capacidade de raciocinar de forma modular.
Campos protegidos
Os campos protegidos restauram parte de nossa capacidade de raciocinar de forma modular. Dependendo do idioma,
protected
pode restringir a capacidade de definir um campo para todas as subclasses ou todas as subclasses e classes do mesmo pacote. Geralmente, não temos acesso a todas as subclasses quando pensamos sobre a correção de um objeto que estamos escrevendo. Por exemplo, você pode escrever um componente ou biblioteca que mais tarde será usado em um programa maior (ou em vários programas maiores) - alguns dos quais talvez nem tenham sido gravados ainda. Normalmente, você não saberá se e de que maneira pode ser subclassificado.No entanto, geralmente é uma responsabilidade de uma subclasse manter o objeto invariável da classe que ela estende. Portanto, em um idioma em que proteger significa apenas "subclasse", e onde somos disciplinados para garantir que as subclasses sempre mantenham os invariantes de sua superclasse, você poderia argumentar que a escolha de usar protegido em vez de privado perde apenas modularidade mínima .
Embora eu tenha falado sobre o raciocínio formal, muitas vezes se pensa que quando os programadores argumentam informalmente sobre a exatidão de seu código, às vezes também se apóiam em tipos semelhantes de argumentos.
fonte
private
variáveis em uma classe são melhores do queprotected
pela mesma razão que umabreak
instrução dentro de umswitch
bloco é melhor que umagoto label
instrução; que é que os programadores humanos são propensos a erros.protected
As variáveis se prestam a abuso não intencional (erros de programadores), assim como agoto
declaração se presta à criação de código espaguete.É possível escrever código livre de erros usando
protected
variáveis de classe? Sim, claro! Assim como é possível escrever código livre de erros usandogoto
; mas como diz o clichê "Só porque você pode, não significa que você deveria!"As classes e, de fato, o paradigma OO, existem para se proteger contra infelizes programadores humanos propensos a erros que cometem erros. A defesa contra erros humanos é tão boa quanto as medidas defensivas incorporadas à classe. Fazer a implementação de sua classe
protected
é o equivalente a abrir um enorme buraco nas paredes de uma fortaleza.As classes base não têm absolutamente nenhum conhecimento das classes derivadas. No que diz respeito a uma classe base,
protected
na verdade não oferece mais proteção do quepublic
, porque não há nada que impeça uma classe derivada de criar umpublic
getter / setter que se comporte como um backdoor.Se uma classe base permite acesso sem impedimentos a seus detalhes internos de implementação, torna-se impossível para a própria classe se defender contra erros. As classes base não têm absolutamente nenhum conhecimento de suas classes derivadas e, portanto, não têm como se proteger contra os erros cometidos nessas classes derivadas.
A melhor coisa que uma classe base pode fazer é ocultar o máximo possível de sua implementação
private
e impor restrições suficientes para impedir a quebra de alterações de classes derivadas ou qualquer outra coisa fora da classe.Por fim, existem linguagens de alto nível para minimizar erros humanos. Boas práticas de programação (como os princípios do SOLID ) também existem para minimizar erros humanos.
Os desenvolvedores de software que ignoram as boas práticas de programação têm uma chance muito maior de falha e têm maior probabilidade de produzir soluções quebradas e não sustentáveis. Aqueles que seguem boas práticas têm uma chance muito menor de fracassar e têm maior probabilidade de produzir soluções que possam ser mantidas em funcionamento.
fonte
public
detalhes da implementação porque se presta a um código não sustentável. Um programador competente evitariagoto
porque se presta a um código não sustentável. No entanto, o mundo real é tal que, às vezes, você se perde em uma terrível bagunça de código legado e não tem escolha a não ser usargoto
, e pelo mesmo motivo, às vezes, não tem escolha a não ser usar detalhes de implementação públicos / protegidos.public
propriedades / interface, mas não com código que apenas usagoto
.goto
ou é apenas porque leu artigos como esse? Eu entendo por que ogoto
mal é porque passei 2 anos trabalhando no código antigo que o usa; e passei mais tempo trabalhando no código em que "classes" vazavam por toda parte com detalhes de implementaçãopublic
efriend
especificadores usados como hacks rápidos / fáceis / sujos. Não aceito o argumento de que "expor publicamente os detalhes da implementação" não pode causar uma bagunça de espaguete entrelaçada no mesmo nível de severidade,goto
porque absolutamente pode.As classes herdáveis têm dois contratos - um com detentores de referências a objetos e com classes derivadas. Membros públicos estão vinculados pelo contrato com titulares de referência e membros protegidos são vinculados pelo contrato com classes derivadas.
Tornar membros a
protected
torna uma classe base mais versátil, mas frequentemente limita as maneiras pelas quais as versões futuras da classe podem mudar. Criar membrosprivate
permite que o autor da classe tenha mais versatilidade para alterar o funcionamento interno da classe, mas limita os tipos de classes que podem ser úteis a partir dela.Como exemplo, o
List<T>
.NET torna o armazenamento de backup privado; se protegidos, os tipos derivados poderiam fazer algumas coisas úteis que, de outra forma, não seriam possíveis, mas as versões futurasList<T>
teriam para sempre usar seu armazenamento monolítico desajeitado, mesmo para listas que contêm milhões de itens. Tornar o armazenamento secundário privado permitiria que versões futurasList<T>
usassem um armazenamento secundário mais eficiente sem interromper as classes derivadas.fonte
Eu acho que há uma suposição fundamental no seu argumento de que, quando alguém escreve uma classe, não sabe quem pode estendê-la no futuro e por que motivo . Dada essa suposição, seu argumento faria todo o sentido, pois cada variável que você tornar privada poderia potencialmente cortar alguma avenida de desenvolvimento no caminho. No entanto, eu rejeitaria essa suposição.
Se essa suposição for rejeitada, haverá apenas dois casos a serem considerados.
Nesse caso, o autor sabe que alguém estenderá a classe e por que e, portanto, saberá exatamente o que proteger e o que tornar privado. Eles estão usando a distinção privada / protegida para comunicar uma espécie de interface ao usuário que cria a subclasse.
Esse caso deve ser raro (você pode argumentar que não é legítimo) e não é preferível apenas modificar a classe original na base de código original. Também pode ser um sintoma de mau design. Nesses casos, eu preferiria que a pessoa hackeie no comportamento, use outros hacks, como amigos (C / C ++) e
setAccessible(true)
(Java).Eu acho que é seguro rejeitar essa suposição.
Isso geralmente volta à idéia de composição sobre herança . A herança geralmente é ensinada como uma maneira ideal de reduzir a reutilização de código, no entanto, raramente deve ser a primeira opção para reutilização de código. Eu não tenho um argumento simples e pode ser um pouco difícil e controverso de entender. No entanto, em minha experiência com modelagem de domínio, descobri que raramente uso herança sem ter uma compreensão muito clara de quem herdará minha classe e por quê.
fonte
Todos os três níveis de acesso têm seu caso de uso; o OOP estaria incompleto sem nenhum deles. Geralmente você faz
E você se desvia deste esquema geral apenas se houver uma boa razão ™ . Cuidado com "isso facilitará minha vida quando eu puder acessá-la livremente de fora" (e aqui fora também inclui subclasses). Quando implemento hierarquias de classes, geralmente começo com classes que não têm membros protegidos, até chegar a subclassificar / estender / especializá-las, tornando-me as classes base de um framework / kit de ferramentas e às vezes movendo parte de sua funcionalidade original para um nível acima.
fonte
Uma pergunta mais interessante, talvez, é por que qualquer outro tipo de campo além do privado é necessário. Quando uma subclasse precisa interagir com os dados de uma superclasse, isso cria diretamente um acoplamento direto entre os dois, enquanto o uso de métodos para proporcionar a interação entre os dois permite um nível de indireção que possibilita fazer alterações no superclasse que seria muito difícil.
Vários idiomas (por exemplo, Ruby e Smalltalk) não fornecem campos públicos, de modo que os desenvolvedores são desencorajados a permitir o acoplamento direto às implementações de classe, mas por que não ir além e ter apenas campos privados? Não haveria perda de generalidade (porque a superclasse sempre pode fornecer acessadores protegidos para a subclasse), mas garantiria que as classes sempre tenham pelo menos um pequeno grau de isolamento de suas subclasses. Por que esse design não é mais comum?
fonte
Muitas respostas boas aqui, mas de qualquer maneira eu colocarei meus dois centavos. :-)
Privado é bom pelo mesmo motivo que os dados globais são ruins.
Se uma classe declara dados privados, você sabe absolutamente que o único código que mexe com esses dados é o código da classe. Quando há um erro, você não precisa pesquisar por toda a criação para encontrar todos os lugares que possam alterar esses dados. Você sabe que está na aula. Quando você altera o código e altera algo sobre como esse campo é usado, não precisa rastrear todos os locais em potencial que podem usar esse campo e estudar se a alteração planejada os quebrará. Você sabe que os únicos lugares estão dentro da classe.
Tive muitas e muitas vezes que tive que fazer alterações nas classes que estão em uma biblioteca e usadas por vários aplicativos, e tenho que seguir com muito cuidado para garantir que não quebre nenhum aplicativo que não conheço nada. Quanto mais dados públicos e protegidos houver, maior o potencial de problemas.
fonte
Acho que vale a pena mencionar algumas opiniões divergentes.
Em teoria, é bom ter um nível de acesso controlado por todos os motivos mencionados em outras respostas.
Na prática, muitas vezes, ao revisar o código, vejo pessoas (que gostam de usar privado), alterando o nível de acesso de privado -> protegido e não com muita freqüência de protegido -> público. Quase sempre, a alteração das propriedades de classe envolve a modificação de setters / getters. Eles desperdiçaram muito do meu tempo (revisão de código) e deles (alteração de código).
Também me incomoda que isso signifique que suas classes não estão fechadas para modificação.
Isso ocorreu com o código interno, onde você sempre pode alterá-lo, se precisar também. A situação é pior com o código de terceiros quando não é tão fácil alterar o código.
Então, quantos programadores acham que é um aborrecimento? Bem, quantos estão usando linguagens de programação que não têm privado? Obviamente, as pessoas não estão apenas usando esses idiomas porque não possuem especificadores particulares, mas isso ajuda a simplificar os idiomas e a simplicidade é importante.
Imo é muito semelhante à digitação dinâmica / estática. Em teoria, a digitação estática é muito boa. Na prática, só previne como 2% de erros A Eficácia Unreasonable de Tipagem Dinâmica ... . O uso privado provavelmente evita erros menores que isso.
Eu acho que os princípios do SOLID são bons, desejo que as pessoas se importem mais com eles do que com a criação de uma classe com público, proteção e privacidade.
fonte
Eu também gostaria de adicionar outro exemplo prático do porquê
protected
não é suficiente. Na minha universidade, os primeiros anos realizam um projeto no qual eles precisam desenvolver uma versão para desktop de um jogo de tabuleiro (para o qual uma IA é desenvolvida posteriormente e é conectada a outros jogadores através de uma rede). É fornecido algum código parcial para eles começarem, incluindo uma estrutura de teste. Algumas das propriedades da classe principal do jogo são expostasprotected
para que as classes de teste que estendem essa classe tenham acesso a elas. Mas esses campos não são informações confidenciais.Como AT da unidade, muitas vezes vejo os alunos simplesmente criando todo o código adicionado
protected
oupublic
(talvez porque tenham visto o outroprotected
e outraspublic
coisas e tenham assumido que deveriam seguir o exemplo). Pergunto a eles por que seu nível de proteção é inadequado e muitos não sabem o porquê. A resposta é que as informações confidenciais que eles estão expondo às subclasses significa que outro jogador pode trapacear simplesmente estendendo essa classe e acessando as informações altamente confidenciais do jogo (essencialmente o local oculto do oponente, eu acho semelhante ao que seria se você podia ver as peças dos seus oponentes em um tabuleiro de navios de guerra estendendo alguma classe). Isso torna seu código muito perigoso no contexto do jogo.Fora isso, existem muitos outros motivos para manter algo particular até mesmo nas suas subclasses. Pode ser para ocultar detalhes de implementação que podem atrapalhar o funcionamento correto da classe, se alterados por alguém que não sabe necessariamente o que está fazendo (principalmente pensando em outras pessoas usando o seu código aqui).
fonte
Métodos / variáveis particulares geralmente serão ocultados de uma subclasse. Isso pode ser uma coisa boa.
Um método privado pode fazer suposições sobre parâmetros e deixar a verificação de sanidade para o chamador.
Um método protegido deve verificar as entradas de sanidade.
fonte
"privado" significa: não se destina a ser alterado ou acessado por ninguém, exceto a própria classe. Não se destina a ser alterado ou acessado por subclasses. Subclasses? Quais subclasses? Você não deve subclassificar isso!
"protegido" significa: destinado apenas a ser alterado ou acessado por classes ou subclasses. Dedução provável que você deve subclassificar; caso contrário, por que "protegido" e não "privado"?
Há uma clara diferença aqui. Se eu tornar algo particular, você deve manter seus dedos sujos afastados. Mesmo se você for uma subclasse.
fonte
Alguns argumentos sobre
private
vs.protected
métodos :private
métodos evitam a reutilização de código. Uma subclasse não pode usar o código no método privado e pode ter que implementá-lo novamente - ou reimplementar o (s) método (s) que originalmente dependem do método privado & c.Por outro lado, qualquer método que não
private
seja pode ser visto como uma API fornecida pela classe ao "mundo exterior", no sentido de que subclasses de terceiros também são consideradas "mundo exterior", como alguém sugeriu em sua resposta já.Isso é uma coisa ruim? - Acho que não.
Obviamente, uma API pública (pseudo-) bloqueia o programador original e dificulta a refatoração dessas interfaces. Mas, visto o contrário, por que um programador não deve projetar seus próprios "detalhes de implementação" de maneira tão limpa e estável quanto sua API pública? Ele deveria usar
private
para que ele possa ser desleixado ao estruturar seu código "privado"? Pensando que talvez ele pudesse limpá-lo mais tarde, porque ninguém notará? - Não.O programador também deve pensar um pouco em seu código "privado", para estruturá-lo de uma maneira que permita ou promova a reutilização do máximo possível em primeiro lugar. Então, as partes não-privadas podem não se tornar tão onerosas no futuro quanto alguns temem.
Um monte de código (framework) Vejo adota o uso inconsistente de
private
:protected
, métodos não-finais, que quase não fazem nada mais do que delegar a um método particular é comumente encontrado.protected
, métodos não finais, cujo contrato só pode ser cumprido através do acesso direto a campos particulares também.Esses métodos não podem ser substituídos / aprimorados logicamente, embora tecnicamente não exista nada para tornar isso (compilador) óbvio.
Quer extensão e herança? Não faça seus métodos
private
.Não quer que certos comportamentos da sua classe sejam alterados? Faça seus métodos
final
.Realmente não pode ter seu método chamado fora de um determinado contexto bem definido? Torne seu método
private
e / ou pense em como você pode disponibilizar o contexto bem definido necessário para reutilização através de outroprotected
método de wrapper.É por isso que eu advogo a usar
private
com moderação. E para não confundirprivate
comfinal
. - Se a implementação de um método é vital para o contrato geral da classe e, portanto, não deve ser substituída / substituída, faça-ofinal
!Para campos,
private
não é realmente ruim. Contanto que os campos possam ser razoavelmente "usados" através de métodos apropriados (isso não égetXX()
ousetXX()
!).fonte
private
vsprotected
original, fornece conselhos equivocados ("useprivate
com moderação"?) E contraria o consenso geral sobre o design de OO. O usoprivate
não tem nada a ver com ocultar o código incorreto.private
ajuda a manter a API limpa.Privado : quando você tem algo que nunca será útil para qualquer subclasse chamar ou substituir.
Protegido : quando você tem algo que possui uma implementação / constante específica da subclasse.
Um exemplo:
fonte
Foi difícil para mim entender esse assunto, então gostaria de compartilhar um pouco da minha experiência:
$classInstance->field
. E o truque que é "é isso". As crianças da sua turma terão acesso total a ela, porque é a parte interna legítima delas.UPDATE: um exemplo prático de uma tarefa real que resolvi. Aqui está: você tem um token, como USB ou LPT (esse foi o meu caso), e você tem um middleware. O token solicita um código PIN, abre se estiver correto e você pode enviar parte criptografada e várias chaves para decifrar. As chaves são armazenadas em token, você não pode lê-las, apenas usá-las. E havia chaves temporárias para uma sessão, assinadas por uma chave em um token, mas armazenadas no próprio middleware. A chave temporária não deveria vazar para todo o lado de fora, apenas para existir no nível do driver. E usei um campo privado para armazenar essa chave temporária e alguns dados relacionados à conexão de hardware. Portanto, não derivados foram capazes de usar não apenas uma interface pública, mas também alguns protegidosSub-rotinas "práticas" que criei para uma tarefa, mas não foi possível abrir um cofre com as chaves e a interação HW. Faz sentido?
fonte