Por que ter campos particulares, não é protegido o suficiente?

265

A visibilidade privatedos 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 privatemétodos para protected. No entanto, esconder detalhes do mundo exterior é importante - portanto, precisamos protectede não apenas public.

Minha pergunta é: Você conhece um caso de uso importante em que, em privatevez de protectedser uma boa ferramenta, ou duas opções " protected& public" seriam suficientes para idiomas OOP?

Adam Libuša
fonte
236
Para os que rejeitam o voto: Embora eu também discorde totalmente das premissas do OP, estou votando positivamente nesta questão porque ela é perfeitamente coerente e vale a pena responder. Sim, o OP precisa ser contada por que isso é errado, mas a maneira de fazer isso é escrever uma resposta (ou sugerir edições para respostas existentes), não downvote só porque ele não percebi isso por si mesmo ainda.
Ixrec 11/03/16
18
Classes derivadas fazem parte do mundo exterior.
CodesInChaos
20
Não se esqueça que protegido nem sempre significa que o acesso está bloqueado na hierarquia de herança. Em Java, também concede acesso no nível do pacote.
berry120
8
Meu professor costumava dizer que "existem coisas que eu não diria aos meus filhos. Esses são meus campos particulares".
SáT

Respostas:

225

Porque, como você diz, protectedainda 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 protectedmembros 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 os publicmembros. Se não tivéssemos a capacidade de criar privatemembros 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.

Ixrec
fonte
11
Eu jogaria mais de +1 se eu pudesse, pois esta é a primeira vez privateque 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 usava privateanteriormente, sempre senti uma boa documentação e uma convenção de nomenclatura gostaria _foode indicar que você provavelmente não deveria estar mexendo nela era equivalente, se não melhor. Ser capaz de dizer deterministicamente "nada vai quebrar" é um privaterecurso legítimo e único.
abluejelly
Eu originalmente ignorei o caso do código de bibliotecas e estruturas públicas e pensei mais ou menos apenas em termos de "código do cliente". Otimizar a implementação interna é um bom exemplo para minha pergunta, embora eu me pergunte: se isso realmente acontece na realidade (especialmente quando muitas pessoas recomendam uma classe não deve ter mais de 1000 linhas de código). Geralmente, eu gosto da abordagem do Ruby, onde o privado é uma recomendação: "Aqui sejam dragões, prossiga com cuidado".
Adam Libuša 13/03/16
9
@ AdamLibuša Embora esse seja um negócio muito maior se o seu código for público, ele ainda se aplica mesmo que você seja o autor de todos os clientes da classe. O problema simplesmente muda de certos refatores que são impossíveis para certos refatores serem tediosos e propensos a erros. A otimização é realmente o motivo menos comum para esses refatores na minha experiência (embora eu principalmente faça Javascript), geralmente é mais como um bug que expôs uma falha de implementação fundamental que exige a reestruturação do gráfico de dependência / chamada dos vários bits internos para alcançar uma correção verdadeiramente robusta.
Ixrec 13/03/19
5
"" mais cedo ou mais tarde, você fará uma subclasse de todas as classes "quase certamente não é o caso." E mais ao ponto, você quase certamente não, e certamente não deve, substituir todas as funções de uma classe e alterar o uso de todos os elementos de dados de uma classe. Algumas coisas devem logicamente ser escritas em pedra para que a classe tenha algum significado.
Jay
1
Eu atirar-lhe mais de um, se eu pudesse => Isso é o que chamamos de recompensas @abluejelly
Thomas Ayoub
256

No POO, mais cedo ou mais tarde, você fará uma subclasse de uma classe

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 ++) ou sealed(C #).

é bom entender e poder modificar completamente a implementação.

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.

Sebastian Redl
fonte
34
+1 para o cenário teórico de "afetado por uma quantidade infinita de código"
Broken_Window 11/03/16
13
Talvez seja interessante notar também que os designers de C # decidiram realmente proibir a substituição de um método herdado, a menos que seja explicitamente marcado como virtual, pelas razões descritas nesta resposta.
Will Vousden
11
Ou, simplificando: protectedé para métodos, não para membros de dados.
Matthieu M.Mar
7
Não consigo encontrá-lo agora, mas lembro de ler uma resposta bem escrita de @EricLippert sobre o porquê de sealedgrandes 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.
Dan Neely
4
@ ThorbjørnRavnAndersen "durante o desenvolvimento, mas não quando lançado" - como diabos uma classe deve saber? Deseja realmente compilar uma versão de teste diferente da versão lançada quando você não pode nem mesmo testá-la? Os testes não precisam acessar coisas particulares de qualquer maneira.
Sebastian Redl 14/03
33

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.

Robbie Dee
fonte
2
+1 em "montar um ônibus e cavalos" em qualquer coisa. É uma ótima frase que eu gostaria de ouvir mais.
22816 Nic Hartley
2
Se alguém precisar subclassificar sua classe, talvez ele deva ter acesso às suas salvaguardas? E se você não quer que alguém mude suas salvaguardas para alcançar algum objetivo, talvez não compartilhe o código? Funciona assim em Ruby - private é mais ou menos uma recomendação.
Adam Libuša
3
@ AdamLibuša "Não compartilhe o código"? Assim que você publica qualquer DLL, você compartilha o código - todas as estruturas e métodos e tudo o que existe para o mundo inteiro ver, especialmente em idiomas que suportam reflexão por padrão. Todo mundo pode fazer o que quiser com "seu código" - 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).
Luaan 14/03
2
@ AdamLibuša Acho que sua confusão decorre principalmente das diferentes abordagens OOP adotadas por diferentes idiomas. A raiz do OOP (como originalmente definida) é de mensagens - isso significa que tudo é privado, exceto as mensagens às quais você responde. Na maioria dos idiomas OOPish, isso é exposto como "mantenha seus dados privados" - uma maneira de tornar a interface pública o menor possível. A única maneira de o usuário (seja uma subclasse ou outra classe) manipular sua classe é através da interface pública que você definiu - semelhante à maneira como você costuma usar o volante e os pedais para dirigir seu carro :)
Luaan
3
@Luaan +1 para quando está tudo bem tocar nos assuntos alheios!
Nigel Touch
12

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

  1. Os métodos associaram a eles condições pré e pós (contratos).
  2. Os objetos se associaram a eles invariantes.

O raciocínio modular sobre um objeto procede da seguinte forma (para uma primeira aproximação, pelo menos)

  1. Prove que o construtor do objeto estabelece a invariante
  2. Para cada método não privado, assuma que o objeto invariável e a pré-condição do método permanecem na entrada e, em seguida, prove que o corpo do código implica que a pós-condição e o invariante permanecem na saída do método

Imagine que verifiquemos object Ausando a abordagem acima. E agora deseja verificar method gdo object Bque chama method fde object A. O raciocínio modular nos permite raciocinar method gsem ter que reconsiderar a implementação de method f. Desde que possamos estabelecer a invariante object Ae a pré-condição method fno local da chamada method g,, podemos considerar a condição de postagem method fcomo um resumo do comportamento da chamada do método. Além disso, também saberemos que, após a chamada retornar, o invariante de Aainda 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, protectedpode 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.

flamingpenguin
fonte
8

privatevariáveis ​​em uma classe são melhores do que protectedpela mesma razão que uma breakinstrução dentro de um switchbloco é melhor que uma goto labelinstrução; que é que os programadores humanos são propensos a erros.

protectedAs variáveis ​​se prestam a abuso não intencional (erros de programadores), assim como a gotodeclaração se presta à criação de código espaguete.

É possível escrever código livre de erros usando protectedvariáveis ​​de classe? Sim, claro! Assim como é possível escrever código livre de erros usando goto; 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, protectedna verdade não oferece mais proteção do que public, porque não há nada que impeça uma classe derivada de criar um publicgetter / 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 privatee 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.

Ben Cottrell
fonte
Para downvoter - o que poderia ser alterado / melhorado nesta resposta?
Ben Cottrell
Não votei para baixo, mas comparando o nível de acesso com o break / goto?
Imel96
@ imel96 Não, comparando o motivo pelo qual eles são evitados e desencorajados pelas "Melhores práticas" (principalmente para escrever um novo código). ou seja, um programador competente evitaria os publicdetalhes da implementação porque se presta a um código não sustentável. Um programador competente evitaria gotoporque 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 usar goto, e pelo mesmo motivo, às vezes, não tem escolha a não ser usar detalhes de implementação públicos / protegidos.
Ben Cottrell
A gravidade é diferente em magnitude, é incomparável. Eu poderia viver com código que possui apenas publicpropriedades / interface, mas não com código que apenas usa goto.
imel96
2
@ imel96 Você já trabalhou em código que usa gotoou é apenas porque leu artigos como esse? Eu entendo por que o goto 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ção publice friendespecificadores 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, gotoporque absolutamente pode.
Ben Cottrell 14/03
4

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 protectedtorna uma classe base mais versátil, mas frequentemente limita as maneiras pelas quais as versões futuras da classe podem mudar. Criar membros privatepermite 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 futuras List<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 futuras List<T>usassem um armazenamento secundário mais eficiente sem interromper as classes derivadas.

supercat
fonte
4

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.

  1. O autor da classe original tinha idéias muito claras sobre por que ela pode ser estendida (por exemplo, é um BaseFoo e haverá várias implementações concretas de Foo no caminho).

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.

  1. O autor da classe filho está tentando invadir algum comportamento em uma classe pai.

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ê.

Ritmo
fonte
2

Todos os três níveis de acesso têm seu caso de uso; o OOP estaria incompleto sem nenhum deles. Geralmente você faz

  • torne todas as variáveis ​​/ membros de dados privados . Você não quer que alguém de fora mexa com seus dados internos. Também métodos que fornecem funcionalidade auxiliar (pense em cálculos com base em várias variáveis ​​de membro) à sua interface pública ou protegida - isto é apenas para uso interno e você pode querer alterá-lo / melhorá-lo no futuro.
  • torne pública a interface geral da sua classe . É com isso que os usuários da sua classe original devem trabalhar e como você acha que as classes derivadas também devem ser. Para fornecer o encapsulamento adequado, geralmente são apenas métodos (e classes / estruturas auxiliares, enumerações, typedefs, o que o usuário precisar para trabalhar com seus métodos), não variáveis.
  • declare os métodos protegidos que podem ser úteis para alguém que deseja estender / especializar a funcionalidade de sua classe, mas não deve fazer parte da interface pública - na verdade, você normalmente cria membros privados para protegidos quando necessário. Em caso de dúvida, não, até que você saiba que
    1. sua classe pode / pode / será subclassificada,
    2. e tenha uma ideia clara de quais podem ser os casos de uso da subclasse.

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.

Murphy
fonte
1

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?

Jules
fonte
1

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.

Jay
fonte
1

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.

imel96
fonte
1
Se você precisar alterar o código de terceiros ao usá-lo, ele não será bem projetado, ou você não o estará reutilizando. Você sempre pode reutilizar uma classe não abstrata, encapsulando-a, mas na prática você quase nunca precisa subclassificá-la.
Little Santi
0

Eu também gostaria de adicionar outro exemplo prático do porquê protectednã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 expostas protectedpara 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 protectedou public(talvez porque tenham visto o outro protectede outras publiccoisas 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).

J_mie6
fonte
1
Eu não entendo A menos que os jogadores estendam dinamicamente as aulas enquanto o jogo está em execução, como isso pode ser usado para trapacear? Isso implica que você está importando código não confiável para sua base de código. Se você está dizendo que está dando aulas compiladas aos alunos e impedindo-os de trapacear em suas tarefas, modificando os detalhes da implementação, definir um nível de proteção não tornará a tarefa mais segura. Os alunos podem descompilar bibliotecas ou usar a reflexão em seu proveito. Obviamente, isso depende do nível de aplicação que você procura.
Sam
@sam Geralmente, o código deve ser escrito com a suposição de que quem quer que o modifique a seguir não precisará conhecer toda a base de código de dentro para fora. Às vezes, esse poderia ser o autor original (passagem do tempo, falta de sono ...). Trapacear poderia ser dado aos alunos o código do esqueleto para aperfeiçoar e mudar a lógica que eles não deveriam tocar.
Phil Lello
0

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.

Phil Lello
fonte
-1

"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.

gnasher729
fonte
-1

Uma das primeiras coisas que faço ao subclassificar uma classe é alterar vários métodos particulares para protegidos

Alguns argumentos sobre privatevs. protected métodos :

privatemé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 privatepara 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 privatee / ou pense em como você pode disponibilizar o contexto bem definido necessário para reutilização através de outro protectedmétodo de wrapper.

É por isso que eu advogo a usar privatecom moderação. E para não confundir privatecom final. - 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-o final!

Para campos, privatenão é realmente ruim. Contanto que os campos possam ser razoavelmente "usados" através de métodos apropriados (isso não é getXX()ou setXX()!).

JimmyB
fonte
2
-1 Isso não aborda a questão privatevs protectedoriginal, fornece conselhos equivocados ("use privatecom moderação"?) E contraria o consenso geral sobre o design de OO. O uso privatenão tem nada a ver com ocultar o código incorreto.
Andrés F.
3
Você menciona que uma classe tem uma API, mas não parece estabelecer a conexão que o usuário da API não deseja que seja sobrecarregado com detalhes que eles não precisam saber. A situação ideal para o usuário de uma classe é que a interface contenha apenas os métodos necessários e nada mais. Fazer métodos privateajuda a manter a API limpa.
Ben Cottrell
1
@ HannoBinder Concordo que existem muitas classes por aí que sofrem com rigidez, no entanto, flexibilidade e reutilização de código são muito menos importantes que o encapsulamento e a manutenção a longo prazo. Considere o seu caso em que todas as classes têm membros protegidos e as classes derivadas podem mexer nos detalhes de implementação que desejarem; como você testar de maneira confiável essas classes, sabendo que qualquer coisa em qualquer parte do código ainda não escrito pode causar a falha desse código a qualquer momento? quantos desses "hacks" o código poderia levar antes de degenerar em uma grande bola de lama?
Ben Cottrell
2
Você disse "membros". O que estou dizendo está se referindo apenas a métodos . As variáveis ​​de membro (campos) são uma história diferente, onde a privacidade é muito mais justificada.
JimmyB
2
A nomenclatura na discussão está em todo lugar porque não é específica para um idioma específico. "Membro" em C ++ pode ser dados, função, tipo ou modelo.
JDługosz 12/03
-1

Você conhece um importante caso de uso em que privado, em vez de protegido, é uma boa ferramenta, ou duas opções "protected & public" seriam suficientes para idiomas OOP?

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:

public abstract Class MercedesBenz() extends Car {
  //Might be useful for subclasses to know about their customers
  protected Customer customer; 

  /* Each specific model has its own horn. 
     Therefore: protected, so that each subclass might implement it as they wish
  */
  protected abstract void honk();

  /* Taken from the car class. */
  @Override
  public void getTechSupport(){
     showMercedesBenzHQContactDetails(customer);
     automaticallyNotifyLocalDealer(customer);
  }

  /* 
     This isn't specific for any subclass.
     It is also not useful to call this from inside a subclass,
     because local dealers only want to be notified when a 
     customer wants tech support. 
   */
  private void automaticallyNotifyLocalDealer(){
    ...
  }
}
Arnab Datta
fonte
2
quando você tem algo que nunca será útil para qualquer subclasse chamar ou substituir - E isso é algo que você, na minha opinião, nunca conhece com antecedência.
Adam Libuša 18/03/16
Bem, talvez também seja necessário pressionar o botão de reset no meu CMOS. Mas a suposição padrão é que eu realmente não precisarei dela e, portanto, ela é colocada dentro do chassi. O mesmo vale para métodos. Você o torna protegido se a subclasse precisar absolutamente reimplementar / chamá-lo (consulte: buzinando). Você o torna público se outras pessoas precisarem chamá-lo.
Arnab Datta
-2

Foi difícil para mim entender esse assunto, então gostaria de compartilhar um pouco da minha experiência:

  • Qual é o campo protegido ? É nada mais do que um campo, que não pode ser acessada fora de uma classe, ou seja publicamente como este: $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.
  • Qual é o campo privado ? É um " verdadeiro privado " para sua própria classe e sua própria implementação desta classe. "Manter fora do alcance das crianças", como no frasco de um medicamento. Você terá a garantia de que é irreversível pelos derivados da sua classe, seus métodos - quando chamados - terão exatamente o que você declarou

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?

Alexey Vesnin
fonte
Desculpem rapazes! apenas estraguei tudo - atualizando minha resposta =) OBRIGADO! Eu estava com pressa e com erros de digitação =) #
Alexey Vesnin
2
Acho que o OP está ciente das diferenças entre os dois níveis de proteção, mas está interessado em saber por que um deve ser usado em detrimento do outro.
21416 Sam Sam
@ Sam, por favor, faça uma leitura mais profunda da minha resposta. Eles são tipos completamente diferentes de campo! a única coisa que eles têm em comum é que ambos não pode ser referenciado publicamente
Alexey Vesnin
2
Não sei ao que você está se referindo. Estou ciente das diferenças entre privado e protegido. Talvez você tenha entendido mal o que eu quis dizer com níveis de proteção? Estou me referindo a 'níveis de acesso. Eu estava tentando ressaltar que, embora sua resposta não seja ruim, ela não aborda diretamente a questão. O OP parece saber o que ambos protegem / privado / público significa, mas não tem certeza em que circunstâncias eles gostariam de escolher um sobre o outro. Pode ser que você seja rejeitado (não por mim).
Sam
@ Sam Eu acho que entendi seu ponto - acrescentou um caso prático da minha prática / experiência real. É o que está faltando no seu ponto?
Alexey Vesnin