Pergunta

Raramente uso herança, mas quando uso, nunca uso atributos protegidos porque acho que isso quebra o encapsulamento das classes herdadas.

Você usa atributos protegidos?pra quê você usa eles ?

Foi útil?

Solução

Nisso entrevista em Design por Bill Venners, Joshua Bloch, autor de Java eficaz diz:

Confiando em subclasses

Bill Venners: Devo confiar nas subclasses mais intimamente do que as não subclasses?Por exemplo, eu facilito que uma implementação de subclasse me quebre do que faria para uma não subclasse?Em particular, como você se sente sobre os dados protegidos?

Josh Bloch: Escrever algo que seja subclassável e robusto contra uma subclasse maliciosa é realmente uma coisa muito difícil de fazer, supondo que você forneça acesso à subclasse às suas estruturas de dados internas.Se a subclasse não tiver acesso a nada que um usuário comum não tenha, é mais difícil para a subclasse causar danos.Mas, a menos que você chegue a todos os seus métodos, a subclasse ainda pode quebrar seus contratos apenas fazendo as coisas erradas em resposta à invocação do método.É exatamente por isso que as classes críticas de segurança como String são finais.Caso contrário, alguém poderia escrever uma subclasse que faça com que as cordas pareçam mutáveis, o que seria suficiente para quebrar a segurança.Então você deve confiar em suas subclasses.Se você não confia neles, não pode permitir que eles, porque as subclasses podem facilmente fazer com que uma classe viole seus contratos.

Quanto aos dados protegidos em geral, é um mal necessário.Deve ser mantido no mínimo.A maioria dos dados protegidos e métodos protegidos são de comprometer com um detalhe de implementação.Um campo protegido é um detalhe de implementação que você está tornando visível para as subclasses.Mesmo um método protegido é uma estrutura interna que você está tornando visível para as subclasses.

A razão pela qual você o torna visível é que geralmente é necessário para permitir que as subclasses façam seu trabalho ou fazê -lo com eficiência.Mas depois de fazer isso, você está comprometido com isso.Agora é algo que você não tem permissão para mudar, mesmo que mais tarde encontre uma implementação mais eficiente que não envolva mais o uso de um campo ou método específico.

Portanto, todas as outras coisas são iguais, você não deve ter nenhum membro protegido.Mas isso dito, se você tiver muito poucos, sua classe pode não ser utilizável como uma super classe, ou pelo menos não como uma super classe eficiente.Muitas vezes você descobre depois do fato.Minha filosofia é ter o mínimo de membros protegidos possível quando você escreve a classe pela primeira vez.Em seguida, tente subclassificá-lo.Você pode descobrir que, sem um método específico protegido, todas as subclasses terão que fazer algo ruim.

Por exemplo, se você olhar para AbstractList, você descobrirá que existe um método protegido para excluir uma variedade da lista em uma foto (removeRange).Por que isso está aí?Porque o idioma normal para remover um intervalo, com base na API público, é ligar subList para obter um sub-List, e então ligue clear naquele sub-List.Sem esse método protegido em particular, no entanto, a única coisa que clear Pode fazer é remover repetidamente elementos individuais.

Pense nisso.Se você tem uma representação de matriz, o que isso fará?Ele entrará em colapso repetidamente da matriz, fazendo a ordem n trabalho n vezes.Portanto, será necessário uma quantidade quadrática de trabalho, em vez da quantidade linear de trabalho que deveria.Ao fornecer esse método protegido, permitimos qualquer implementação que possa excluir com eficiência um intervalo inteiro para fazê -lo.E qualquer razoável List A implementação pode excluir um intervalo de maneira mais eficiente de uma só vez.

Que precisaríamos desse método protegido é algo que você teria que ser muito mais inteligente do que eu para saber antecipadamente.Basicamente, eu implementei a coisa.Então, quando começamos a subclassem, percebemos que o intervalo de exclusão era quadrático.Não podíamos pagar isso, então eu coloquei o método protegido.Eu acho que essa é a melhor abordagem com métodos protegidos.Coloque o mínimo possível e adicione mais conforme necessário.Os métodos protegidos representam compromissos com os projetos que você pode querer mudar.Você sempre pode adicionar métodos protegidos, mas não pode retirá -los.

Bill Venners: E dados protegidos?

Josh Bloch: A mesma coisa, mas ainda mais.Os dados protegidos são ainda mais perigosos em termos de bagunçar seus invariantes de dados.Se você der acesso a alguém a alguns dados internos, ela terá reinado livre sobre eles.

Versão curta:quebra o encapsulamento, mas é um mal necessário que deve ser reduzido ao mínimo.

Outras dicas

C#:

Eu uso protegido para métodos abstratos ou virtuais que desejo que as classes base substituam.Também faço um método protegido se puder ser chamado por classes base, mas não quero que seja chamado fora da hierarquia de classes.

Você pode precisar deles para atributos estáticos (ou 'globais') dos quais deseja que suas subclasses ou classes do mesmo pacote (se for sobre java) se beneficiem.

Esses atributos finais estáticos que representam algum tipo de 'valor constante' raramente têm uma função getter, portanto, um atributo final estático protegido pode fazer sentido nesse caso.

Scott Meyers diz não use atributos protegidos em C++ Efetivo (3ª ed.):

Artigo 22:Declare os membros dos dados como privados.

O motivo é o mesmo que você dá:ele quebra encapsulamentos.A consequência é que, caso contrário, alterações locais no layout da classe poderão quebrar os tipos dependentes e resultar em alterações em muitos outros locais.

Nunca há boas razões para ter atributos protegidos.Uma classe base deve poder depender do estado, o que significa restringir o acesso aos dados por meio de métodos acessadores.Você não pode dar acesso aos seus dados privados a ninguém, nem mesmo a crianças.

O protected palavra-chave é um erro conceitual e uma falha no design da linguagem, e várias linguagens modernas, como Nim e Ceylon (veja http://ceylon-lang.org/documentation/faq/linguagem-design/#no_protected_modifier), que foram cuidadosamente projetados em vez de apenas copiar erros comuns, não possuem essa palavra-chave.

Não são os membros protegidos que quebram o encapsulamento, é a exposição de membros que não deveriam ser expostos que quebra o encapsulamento...não importa se eles são protegidos ou públicos.O problema com protected é que é equivocado e enganoso...declarando membros protected (em vez de private) não os protege, faz o contrário, exatamente como public faz.Um membro protegido, sendo acessível fora da classe, fica exposto ao mundo e por isso sua semântica deve ser mantida para sempre, assim como é o caso de public.Toda a ideia de "protegido" é um absurdo...encapsulamento não é segurança e a palavra-chave apenas aumenta a confusão entre os dois.Você pode ajudar um pouco evitando todos os usos de protected em suas próprias classes - se algo é parte interna da implementação, não faz parte da semântica da classe e pode mudar no futuro, torne-o privado ou interno ao seu pacote, módulo, montagem, etc.Se for uma parte imutável da semântica da classe, torne-a pública e você não incomodará os usuários da sua classe que podem ver que há um membro útil na documentação, mas não podem usá-lo, a menos que estejam criando seu próprias instâncias e pode obtê-lo por meio de subclasses.

Eu não uso atributos protegidos em Java porque eles são protegidos apenas por pacotes lá.Mas em C++, vou usá-los em classes abstratas, permitindo que a classe herdeira os herde diretamente.

Em geral, não, você realmente não deseja usar membros de dados protegidos.Isso é duplamente verdadeiro se você estiver escrevendo uma API.Uma vez que alguém herda de sua classe, você nunca poderá realmente fazer manutenção e não quebrá-los de uma forma estranha e às vezes selvagem.

Acho que atributos protegidos são uma má ideia.eu uso Estilo de verificação para fazer cumprir essa regra com minhas equipes de desenvolvimento Java.

Recentemente trabalhei em um projeto onde o membro "protegido" era uma ideia muito boa.A hierarquia de classes era algo como:

[+] Base
 |
 +--[+] BaseMap
 |   |
 |   +--[+] Map
 |   |
 |   +--[+] HashMap
 |
 +--[+] // something else ?

A Base implementou um std::list mas nada mais.O acesso direto à lista era proibido ao usuário, mas como a classe Base estava incompleta, ele dependia de qualquer maneira de classes derivadas para implementar o direcionamento à lista.

A indireção pode vir de pelo menos dois sabores:std::map e stdext::hash_map.Ambos os mapas se comportarão da mesma maneira, mas pelo fato de o hash_map precisar que a chave seja hashável (no VC2003, convertível para size_t).

Portanto, o BaseMap implementou um TMap como um tipo de modelo que era um contêiner semelhante a um mapa.

Map e HashMap eram duas classes derivadas de BaseMap, uma especializada em BaseMap em std::map e a outra em stdext::hash_map.

Então:

  • O Base não era utilizável como tal (sem acessadores públicos!) e fornecia apenas recursos e códigos comuns

  • BaseMap precisava de fácil leitura/gravação em um std::list

  • Map e HashMap precisavam de acesso fácil de leitura/gravação ao TMap definido no BaseMap.

Para mim, a única solução foi usar protected para as variáveis ​​​​de membro std::list e TMap.Não havia como colocá-los como "privados" porque, de qualquer maneira, exporia todos ou quase todos os seus recursos por meio de acessadores de leitura/gravação.

No final, acho que se você dividir sua classe em vários objetos, cada derivação adicionando os recursos necessários à sua classe mãe, e apenas a classe mais derivada sendo realmente utilizável, então protegido é o caminho a seguir.O fato de o “membro protegido” ser uma classe e, portanto, ser quase impossível de “quebrar”, ajudou.

Mas caso contrário, protegido deve ser evitado tanto quanto possível (ou seja:Use private por padrão e public quando precisar expor o método).

Eu os uso.Resumindo, é um bom caminho, se você deseja compartilhar alguns atributos.É verdade que você poderia escrever funções set/get para eles, mas se não houver validação, qual é o sentido?Também é mais rápido.

Considere isto:você tem uma classe que é sua classe base.Possui alguns atributos que você não deseja usar nos objetos filhos.Você pode escrever uma função get/set para cada um ou simplesmente configurá-los.

Meu exemplo típico é um manipulador de arquivo/stream.Você deseja acessar o manipulador (ou seja,descritor de arquivo), mas deseja ocultá-lo de outras classes.É muito mais fácil do que escrever uma função set/get para isso.

Em geral, sim.Um método protegido geralmente é melhor.

Em uso, há um nível de simplicidade proporcionado pelo uso de um protegido final variável para um objeto que é compartilhado por todos os filhos de uma classe.Eu sempre desaconselho usá-lo com primitivos ou coleções, pois é impossível definir contratos para esses tipos.

Ultimamente, tenho separado as coisas que você faz com coleções primitivas e brutas das coisas que você faz com classes bem formadas.Primitivos e coleções devem SEMPRE ser privados.

Além disso, comecei ocasionalmente a expor variáveis ​​de membros públicos quando elas são declaradas como finais e são classes bem formadas que não são muito flexíveis (novamente, não são primitivas ou coleções).

Este não é um atalho estúpido, pensei muito seriamente e decidi que não há absolutamente nenhuma diferença entre um público final variável expondo um objeto e um getter.

Depende do que você quer.Se você deseja uma classe rápida, os dados devem ser protegidos e usar métodos públicos e protegidos.Porque acho que você deve assumir que seus usuários que derivam de sua classe a conhecem muito bem ou pelo menos leram seu manual sobre a função que irão substituir.

Se seus usuários mexerem com sua turma, não é problema seu.Todo usuário mal-intencionado pode adicionar as seguintes linhas ao substituir um de seus virtuais:

(C#)

static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...

Você não pode selar todas as classes para evitar isso.

É claro que se você quiser uma restrição em alguns de seus campos, use funções ou propriedades de acesso ou algo que desejar e torne esse campo privado porque não há outra solução...

Mas eu pessoalmente não gosto de seguir os princípios oop a todo custo.Especialmente criando propriedades com o único propósito de tornar os membros dos dados privados.

(C#):

private _foo;
public foo
{
   get {return _foo;}
   set {_foo=value;}
}

Esta foi a minha opinião pessoal.

Mas faça o que seu chefe exige (se ele quiser campos privados, faça isso).

Eu uso variáveis/atributos protegidos em classes base que sei que não pretendo transformar em métodos.Dessa forma, as subclasses têm acesso total às suas variáveis ​​herdadas e não têm a sobrecarga (criada artificialmente) de passar por getters/setters para acessá-las.Um exemplo é uma classe que usa um fluxo de E/S subjacente;há poucos motivos para não permitir às subclasses acesso direto ao fluxo subjacente.

Isso é adequado para variáveis ​​de membro que são usadas de maneira simples e direta na classe base e em todas as subclasses.Mas para uma variável que tem um uso mais complicado (por exemplo, acessá-la causa efeitos colaterais em outros membros da classe), uma variável diretamente acessível não é apropriada.Nesse caso, ele pode ser tornado privado e, em vez disso, getters/setters públicos/protegidos podem ser fornecidos.Um exemplo é um mecanismo de buffer interno fornecido pela classe base, onde acessar os buffers diretamente de uma subclasse comprometeria a integridade dos algoritmos usados ​​pela classe base para gerenciá-los.

É uma decisão de julgamento de design, baseada em quão simples é a variável-membro e como se espera que seja em versões futuras.

O encapsulamento é ótimo, mas pode ser levado longe demais.Já vi classes cujos próprios métodos privados acessavam suas variáveis-membro usando apenas métodos getter/setter.Isso é um exagero, pois se uma classe não pode confiar em seus próprios métodos privados com seus próprios dados privados, em quem ela poderá confiar?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top