Privado x protegido - preocupação com as boas práticas de visibilidade [fechado]

145

Estive pesquisando e conheço a diferença teórica.

  • public - Qualquer classe / função pode acessar o método / propriedade.
  • protected - Somente esta classe e quaisquer subclasses podem acessar o método / propriedade.
  • privado - Somente esta classe pode acessar o método / propriedade. Nem será herdado.

Está tudo bem e bem, a questão é: qual é a diferença prática entre eles? Quando você usaria privatee quando você usaria protected? Existe uma boa prática padrão ou aceitável sobre esta?

Até agora, para manter o conceito de herança e polimorfismo, eu uso publicpara qualquer coisa que deva ser acessada de fora (como construtores e funcionalidade de classe principal) e protectedpara métodos internos (lógica, métodos auxiliares etc.). Estou no caminho certo?

(Observe que esta pergunta é para mim, mas também para referência futura, pois ainda não vi uma pergunta como essa).

Fantasma de Madara
fonte
33
Isso importa? Qualquer idioma com suporte a OOP tem essa preocupação. Por acaso eu programa em PHP, mas acho que a pergunta se aplica a qualquer linguagem de suporte ao OOP.
Madara's Ghost
2
Tudo bem, basta perguntar se você esqueceu de marcar. Agora eu vejo a tag oop.
Paul Bellora 02/12/19
Eu tenho que definir a visibilidade (privada / pública / protegida) para cada propriedade da classe? Ou apenas alguns deles precisam ter um tipo de visibilidade? Em caso afirmativo, como decidir qual propriedade precisa ter uma visibilidade definida no topo da classe?
user4271704
1
de imediato, a diferença entre protegido e privado parece óbvia. Use protected se as subclasses usarão o método / variável, caso contrário, use private. Especificamente, se as subclasses precisarem redefinir uma variável privada muito semelhante no pai, apenas a proteja.
iPherian 23/10
Há outro especificador de acesso internalna linguagem C # que restringe o nível de acesso de uma classe ou de seus membros em um assembly físico. No entanto, não tenho certeza sobre o suporte ou algo semelhante em outros idiomas.
RBT

Respostas:

128

Não, você não está no caminho certo. Uma boa regra geral é: torne tudo o mais privado possível. Isso torna sua classe mais encapsulada e permite alterar os elementos internos da classe sem afetar o código usando sua classe.

Se você projetar sua classe para ser herdável, escolha cuidadosamente o que pode ser substituído e acessível pelas subclasses e torne-o protegido (e final, falando sobre Java, se você deseja torná-lo acessível, mas não substituível). Mas lembre-se de que, assim que você aceitar ter subclasses da sua classe e houver um campo ou método protegido, esse campo ou método fará parte da API pública da classe e não poderá ser alterado posteriormente sem quebrar as subclasses.

Uma classe que não deve ser herdada deve ser finalizada (em Java). Você pode relaxar algumas regras de acesso (privadas para protegidas, finais para não finais) para testar a unidade, mas depois documentá-las e deixar claro que, embora o método esteja protegido, não deve ser substituído.

JB Nizet
fonte
1
A verdadeira questão aqui é sobre privatevs. protectedQuando quero que uma propriedade seja herdada e quando não quero? Eu realmente não posso dizer se um usuário, por vezes, no futuro quer levar minha classe e estendê-lo ...
O fantasma de Madara
16
Bem, a questão não é o que o usuário deseja substituir, a questão é o que você deseja permitir que seja substituído. Geralmente, ajuda a mudar de lado e tentar pensar: se eu usasse essa classe e criasse uma subclasse, o que eu gostaria de substituir? Às vezes, outros usuários ainda vai perder as coisas ...
Thorsten Dittmar
3
Os campos protegidos são uma má prática na herança! . Por favor, tenha cuidado com isso. Aqui está o porquê
RBT
4
Os campos privados são ruins na prática na herança !. (exagero) Por favor, leia minha resposta.
Larry
6
Opinião típica de controle e aberração.
Bruno desthuilliers
58

Permita-me começar dizendo que estou falando principalmente sobre o acesso ao método aqui e, em menor grau, marcando as classes como finais, e não como membro.

A velha sabedoria

"marque-o como privado, a menos que você tenha um bom motivo para não"

fazia sentido nos dias em que foi escrito, antes que o código aberto dominasse o espaço da biblioteca do desenvolvedor e o VCS / mgmt de dependência. tornou-se hiper colaborativo graças ao Github, Maven, etc. Naquela época, também havia dinheiro a ser ganho, restringindo a (s) maneira (s) em que uma biblioteca poderia ser utilizada. Passei provavelmente os primeiros 8 ou 9 anos da minha carreira aderindo estritamente a essa "melhor prática".

Hoje, acredito que seja um mau conselho. Às vezes, existe um argumento razoável para marcar um método como privado ou como final de aula, mas é extremamente raro e, mesmo assim, provavelmente não está melhorando nada.

Você já:

  • Ficou desapontado, surpreso ou magoado por uma biblioteca, etc. que tinha um bug que poderia ter sido corrigido com herança e poucas linhas de código, mas devido a métodos / classes particulares / finais, foram forçados a esperar por um patch oficial que talvez nunca viesse? Eu tenho.
  • Queria usar uma biblioteca para um caso de uso ligeiramente diferente do que foi imaginado pelos autores, mas não conseguiu fazê-lo devido a métodos e classes particulares / finais? Eu tenho.
  • Ficou decepcionado, surpreso ou magoado com uma biblioteca etc. que era excessivamente permissiva em sua extensibilidade? Eu não tenho.

Estas são as três maiores racionalizações que ouvi sobre como marcar métodos privados por padrão:

Racionalização nº 1: não é seguro e não há motivo para substituir um método específico

Não posso contar o número de vezes que me enganei sobre a necessidade ou não de substituir um método específico que escrevi. Tendo trabalhado em várias bibliotecas populares de código aberto, aprendi da maneira mais difícil o verdadeiro custo de marcar as coisas como privadas. Geralmente, elimina a única solução prática para problemas imprevistos ou casos de uso. Por outro lado, nunca em mais de 16 anos de desenvolvimento profissional me arrependi de marcar um método protegido em vez de privado por motivos relacionados à segurança da API. Quando um desenvolvedor decide estender uma classe e substituir um método, conscientemente está dizendo "eu sei o que estou fazendo". e por uma questão de produtividade que deve ser suficiente. período. Se for perigoso, observe nos Javadocs de classe / método, não basta fechar a porta às cegas.

Os métodos de marcação protegidos por padrão são uma atenuação para um dos principais problemas no desenvolvimento moderno de SW: falta de imaginação.

Racionalização # 2: mantém a API / Javadocs públicos limpos

Essa é mais razoável e, dependendo do público-alvo, pode até ser a coisa certa a se fazer, mas vale a pena considerar qual é o custo de manter a API "limpa": extensibilidade. Pelas razões mencionadas acima, provavelmente faz mais sentido marcar itens protegidos por padrão por precaução.

Racionalização nº 3: meu software é comercial e preciso restringir seu uso.

Isso também é razoável, mas como consumidor, eu iria sempre com o concorrente menos restritivo (assumindo que não existem diferenças significativas de qualidade).

Nunca diga nunca

Não estou dizendo que nunca marque métodos como particulares. Estou dizendo que a melhor regra é "tornar os métodos protegidos, a menos que haja uma boa razão para não fazê-lo".

Este conselho é mais adequado para quem trabalha em bibliotecas ou projetos de maior escala que foram divididos em módulos. Para projetos menores ou mais monolíticos, isso não costuma importar muito, já que você controla todo o código e é fácil alterar o nível de acesso do seu código, se / quando você precisar. Mesmo assim, eu ainda daria o mesmo conselho :-)

usuario
fonte
WRT seu Rationalization # 1- Quando eu marca de algo tão protegido que eu escolher para fazer isso explicitamente como eu sinto que este comportamento não é definitiva e pode ser um caso onde os meus filhos aulas gostaria de substituí-lo. Se não tenho tanta certeza, gostaria de torná-lo privado por padrão. Especialmente em um idioma como C #, que mantém um método não virtual por padrão, adicionar apenas um especificador de acesso protegido não faz sentido. Você precisa adicionar palavras-chave virtuale ambos protectedpara deixar sua intenção muito clara. Além disso, as pessoas preferem associação sobre a herança assim protectedcomo padrão é difícil de perceber
RBT
4
A combinação de palavras-chave necessárias para substituir um método é um IMHO de detalhes de linguagem. O contra-argumento que apresento à racionalização nº 1 visa diretamente o caso que você menciona "Se não tenho tanta certeza ...". Na prática, se você não consegue pensar em uma razão pela qual seria perigoso, então há mais a ganhar ao optar pela extensibilidade. Quando você diz 'associação', considero isso sinônimo de composição, caso em que não vejo importância para os dois lados.
26417 Nick
3
Não me lembro quantas vezes tive que "clonar" as classes subjacentes apenas porque queria substituir 1 ou 2 ou os métodos. E, como você disse, com a tendência social de compartilhar mais código do que nunca, faz muito mais sentido usar o protegido do que o privado por padrão. ou seja, seja mais aberto à sua própria lógica.
shinkou
1
Aceita. Eu só queria ajustar o BufferedReader do Java para lidar com delimitadores de linha personalizados. Graças a campos particulares, tenho que clonar a classe inteira, em vez de simplesmente estendê-la e substituir readLine ().
Ron Dunn
21

Pare de abusar de campos particulares !!!

Os comentários aqui parecem ser extremamente favoráveis ​​ao uso de campos particulares. Bem, então eu tenho algo diferente a dizer.

Os campos privados são bons em princípio? Sim. Mas dizer que uma regra de ouro é tornar tudo privado quando você não tem certeza se está definitivamente errado ! Você não verá o problema até encontrar um. Na minha opinião, você deve marcar os campos como protegidos se não tiver certeza .

Há dois casos em que você deseja estender uma classe:

  • Você deseja adicionar funcionalidade extra a uma classe base
  • Você deseja modificar a classe existente que está fora do pacote atual (talvez em algumas bibliotecas)

Não há nada de errado com campos particulares no primeiro caso. O fato de as pessoas estarem abusando de campos particulares o torna tão frustrante quando você descobre que não pode modificar nada.

Considere uma biblioteca simples que modela carros:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

O autor da biblioteca pensou: não há razão para que os usuários da minha biblioteca precisem acessar os detalhes da implementação, assembleCar()certo? Vamos marcar o parafuso como privado.

Bem, o autor está errado. Se você deseja modificar apenas o assembleCar()método sem copiar toda a classe no seu pacote, você está sem sorte. Você precisa reescrever seu próprio screwcampo. Digamos que este carro use uma dúzia de parafusos, e cada um deles envolva algum código de inicialização simples em diferentes métodos particulares, e esses parafusos são todos marcados como particulares. Neste ponto, ele começa a chupar.

Sim, você pode argumentar comigo que bem, o autor da biblioteca poderia ter escrito um código melhor, então não há nada errado com os campos privados . Não estou argumentando que o campo privado é um problema com OOP . É um problema quando as pessoas os estão usando.

A moral da história é que, se você está escrevendo uma biblioteca, nunca sabe se seus usuários desejam acessar um campo específico. Se não tiver certeza, marque-o protectedpara que todos fiquem mais felizes mais tarde. Pelo menos não abuse do campo privado .

Eu apoio muito a resposta de Nick.

Larry
fonte
2
O uso de campos particulares geralmente diz que herança é a abstração errada para alterar um determinado comportamento de uma classe / objeto (se usado conscientemente). A alteração normalmente viola o LSP silenciosamente, à medida que o comportamento é alterado sem a alteração da API. Ótima resposta, +1.
Ghost de Madara
Pregar! Privado é a desgraça da minha existência ao tentar escrever mods do Minecraft. Nada mais irritante do que ter que usar o Reflection ou ASM para corrigir isso.
RecursiveExceptionException
Como eu entendi a resposta de Nick, ele estava se referindo principalmente a métodos, não propriedades. Eu concordo totalmente que não devemos declarar métodos privados sempre que o uso deles precisar ser cuidadoso, mas por que você aconselharia a declarar propriedades protegidas por particulares apenas porque você gostaria de "reescrever" a classe com mais facilidade. Compartilhe totalmente sua frustração enquanto trabalho com o 3º p diariamente, mas vamos incentivar as pessoas a criar uma arquitetura sólida e não apenas dizer "o código acabaria sendo uma porcaria, então vamos nos proteger desde o início para que possamos reescrevê-lo facilmente". Embora eu compartilhe sua frustração.
sean662 22/04
16

Li um artigo há um tempo atrás, que falava em bloquear todas as aulas o máximo possível. Torne tudo final e privado, a menos que você precise expor imediatamente alguns dados ou funcionalidades ao mundo exterior. É sempre fácil expandir o escopo para ser mais permitido posteriormente, mas não o contrário. Primeiro, considere fazer o maior número possível de coisas, o finalque tornará a escolha entre privatee protectedmuito mais fácil.

  1. Torne todas as classes finais, a menos que você precise subclassificá-las imediatamente.
  2. Torne todos os métodos finais, a menos que você precise subclassificar e substituí-los imediatamente.
  3. Torne todos os parâmetros do método final, a menos que você precise alterá-los no corpo do método, o que é meio estranho na maioria das vezes.

Agora, se você tiver uma aula final, torne tudo privado, a menos que algo seja absolutamente necessário ao mundo - torne isso público.

Se você tiver uma classe que possui subclasse (s), examine cuidadosamente todas as propriedades e métodos. Primeiro considere se você deseja expor essa propriedade / método a subclasses. Se você o fizer, considere se uma subclasse pode causar estragos no seu objeto se isso prejudicou o valor da propriedade ou a implementação do método no processo de substituição. Se possível, e você deseja proteger a propriedade / método da sua classe, mesmo das subclasses (parece irônico, eu sei), então torne-a privada. Caso contrário, torne-o protegido.

Disclaimer: Eu não programa muito em Java :)

Anurag
fonte
2
Para esclarecer: A final classem Java é uma classe da qual não pode ser herdada. A final methodnão é virtual, ou seja, não pode ser substituído por uma subclasse (os métodos Java são virtuais por padrão). A final field/parameter/variableé uma referência variável imutável (no sentido de que não pode ser modificado após ter sido inicializado).
usar o seguinte código
1
Curiosamente, vi o conselho exatamente oposto: nada deve ser definitivo, a menos que seja certo que substituir a coisa em questão tenha o potencial de quebrar um invariante. Dessa forma, qualquer pessoa é livre para estender suas aulas conforme necessário.
GordonM
Você pode citar um caso em sua carreira de programação em que marcar um parâmetro do método "final" fez diferença no seu entendimento? (exceto se exigido pelo compilador)
user949300 23/08
7

Quando você usaria privatee quando você usaria protected?

A herança privada pode ser implementada em termos de relacionamento, e não em um relacionamento IS-A . Simplificando, a interface externa da classe herdada não possui relação (visível) com a classe herdada. Ela usa a privateherança apenas para implementar uma funcionalidade semelhante que a classe Base fornece.

Ao contrário de Herança Privada, a herança Protegida é uma forma restrita de Herança, em que a classe derivada é do tipo IS-A da classe Base e deseja restringir o acesso dos membros derivados apenas à classe derivada.

Alok Save
fonte
2
Que "Implementado em termos de relacionamento" é um relacionamento HAS-A. Se todos os atributos forem privados, a única maneira de acessá-los é através de métodos. É a mesma coisa que se a subclasse contivesse um objeto da superclasse. Eliminar todos os atributos protegidos de suas classes concretas significa eliminar também toda a herança deles.
precisa saber é o seguinte
2
Processo de pensamento interessante - herança privada . @shawnhcorey, obrigado por deixar explícito que está apontando para o relacionamento HAS-A, também conhecido como associação (que pode ser agregação ou composição). Sinto que este post também deve ser atualizado para esclarecer o mesmo.
RBT
1

Bem, é tudo uma questão de encapsulamento, se as classes de folha de pagamento lidam com cobrança de pagamento e, em seguida, na classe de produto, por que ela precisaria de todo o processo de cobrança? nada além desse público para aqueles em que outras classes usariam também, protegido para aqueles limites apenas para a extensão de classes. Como você é madara uchiha, o privado é como "limboo"você pode vê-lo (você classifica apenas uma classe).

ujwal dhakal
fonte