Você deve usar variáveis ​​de membro protegidas?

97

Você deve usar variáveis ​​de membro protegidas? Quais são as vantagens e quais problemas isso pode causar?

John Channing
fonte

Respostas:

73

Você deve usar variáveis ​​de membro protegidas?

Depende de quão exigente você é sobre esconder o estado.

  • Se você não quiser nenhum vazamento de estado interno, então declarar todas as suas variáveis ​​de membro privadas é o caminho a seguir.
  • Se você realmente não se importa se as subclasses podem acessar o estado interno, então protected é suficiente.

Se um desenvolvedor vier e criar uma subclasse de sua classe, ele pode bagunçar tudo porque não a entende completamente. Com membros privados, exceto a interface pública, eles não podem ver os detalhes específicos de implementação de como as coisas estão sendo feitas, o que lhe dá a flexibilidade de alterá-lo posteriormente.

Allain Lalonde
fonte
1
Você pode comentar sobre o desempenho de variáveis ​​protegidas em comparação a uma variável privada com um método get / set?
Jake
3
Eu diria que não é algo que valha a pena se preocupar, a menos que você descubra, por meio do perfil, que o gargalo acaba sendo os acessores (o que quase nunca é). Existem truques que podem ser feitos para tornar o JIT mais inteligente sobre as coisas, se isso acabar sendo um problema. Em java, por exemplo, você pode sugerir que o acessador pode ser embutido marcando-o como final. Embora, honestamente, o desempenho de getters e setters seja muito menos importante do que lidar com a organização do sistema e com os problemas de desempenho reais conforme determinado por um criador de perfil.
Allain Lalonde
23
@Jake: Você nunca deve tomar decisões de design com base em suposições de desempenho. Você toma decisões de design com base no que você acha que é o melhor design e somente se o perfil da vida real mostrar um gargalo em seu design, você o corrige. Normalmente, se o design for sólido, o desempenho também será bom.
Mecki
Com membros privados, além da interface pública, eles não podem ver os detalhes específicos da implementação. Eles podem apenas abrir a classe e procurá-la, então isso não faz sentido ?!
Black
2
@Black Claramente Allain quis dizer 'eles não podem acessar ' esses membros e, portanto, não podem construir código contra eles, deixando o autor da classe livre para remover / alterar os membros protegidos mais tarde. (Claro, o idioma pimpl permitiria ocultá-los visualmente e das unidades de tradução, incluindo o cabeçalho, também.)
underscore_d
31

O sentimento geral hoje em dia é que eles causam acoplamento indevido entre classes derivadas e suas bases.

Eles não têm nenhuma vantagem particular sobre métodos / propriedades protegidos (uma vez eles podem ter uma ligeira vantagem de desempenho), e eles também foram mais usados ​​em uma época em que herança muito profunda estava na moda, o que não é no momento.

Will Dean
fonte
2
Não deveria no particular advantage over protected methods/propertiesser no particular advantage over *private* methods/properties?
Penghe Geng
Não, porque estou / estava falando sobre as vantagens / desvantagens de várias formas de comunicação entre as classes derivadas e suas bases - todas essas técnicas seriam "protegidas" - a diferença é se elas são variáveis ​​de membro (campos) ou propriedades / métodos ( isto é, sub-rotinas de algum tipo).
Will Dean,
1
Obrigado pelo rápido esclarecimento. Estou feliz por ter a resposta do autor da postagem original em uma hora para minha pergunta em um post de 6 anos. Você não acha que isso pode acontecer na maioria dos outros fóruns online :)
Penghe Geng
9
Mais notável ainda é que eu realmente concordo comigo mesmo durante todo esse tempo ...
Will Dean
Uma ordem de negócios de um construtor é garantir que todas as variáveis ​​de estado sejam explicitamente inicializadas. Se você seguir essa convenção, poderá usar a superconstrução para chamar o construtor pai; ele então se encarregará de inicializar variáveis ​​de estado privado na classe pai.
ncmathsadist
31

Geralmente, se algo não é deliberadamente concebido como público, eu o torno privado.

Se surgir uma situação em que preciso acessar essa variável privada ou método de uma classe derivada, eu altero de privado para protegido.

Isso quase nunca acontece - eu realmente não sou um fã de herança, já que não é uma maneira particularmente boa de modelar a maioria das situações. De qualquer forma, continue, sem problemas.

Eu diria que isso é bom (e provavelmente a melhor maneira de fazer isso) para a maioria dos desenvolvedores.

O simples fato da questão é que , se algum outro desenvolvedor aparecer um ano depois e decidir que precisa de acesso à sua variável de membro privada, ele simplesmente editará o código, mudará para protegido e continuará com seus negócios.

As únicas exceções reais a isso são se você está no negócio de remessa de DLLs binárias na forma de caixa preta para terceiros. Isso consiste basicamente na Microsoft, aqueles fornecedores de 'Controle de DataGrid personalizado' e talvez alguns outros aplicativos grandes que vêm com bibliotecas de extensibilidade. A menos que você esteja nessa categoria, não vale a pena gastar tempo / esforço para se preocupar com esse tipo de coisa.

Orion Edwards
fonte
8

O problema principal para mim é que, depois de tornar uma variável protegida, você não pode permitir que nenhum método em sua classe dependa de seu valor estar dentro de um intervalo, porque uma subclasse sempre pode colocá-lo fora do intervalo.

Por exemplo, se eu tenho uma classe que define a largura e a altura de um objeto renderizável e faço essas variáveis ​​protegidas, não posso fazer suposições sobre (por exemplo), a proporção de aspecto.

Criticamente, nunca posso fazer essas suposições em qualquer ponto a partir do momento em que o código é lançado como uma biblioteca, pois mesmo que eu atualize meus setters para manter a proporção de aspecto, não tenho garantia de que as variáveis ​​estão sendo definidas por meio dos setters ou acessadas por meio do getters no código existente.

Nenhuma subclasse de minha classe pode optar por fazer essa garantia, pois eles também não podem impor os valores das variáveis, mesmo que esse seja o ponto inteiro de sua subclasse .

Como um exemplo:

  • Eu tenho uma classe de retângulo com largura e altura sendo armazenadas como variáveis ​​protegidas.
  • Uma subclasse óbvia (dentro do meu contexto) é uma classe "DisplayedRectangle", onde a única diferença é que eu restrinjo as larguras e alturas a valores válidos para uma exibição gráfica.
  • Mas isso é impossível agora , uma vez que minha classe DisplayedRectangle não pode realmente restringir esses valores, já que qualquer subclasse dela poderia substituir os valores diretamente, enquanto ainda é tratada como um DisplayedRectangle.

Ao restringir as variáveis ​​para que sejam privadas, posso então impor o comportamento que desejo por meio de setters ou getters.

orvalho
fonte
7

Em geral, eu manteria suas variáveis ​​de membro protegidas para o caso raro em que você tenha controle total sobre o código que as usa também. Se você estiver criando uma API pública, eu diria que nunca. A seguir, vamos nos referir à variável de membro como uma "propriedade" do objeto.

Aqui está o que sua superclasse não pode fazer depois de tornar uma variável de membro protegida em vez de privada com acessadores:

  1. Preguiçosamente crie um valor na hora quando a propriedade está sendo lida. Se você adicionar um método getter protegido, poderá criar preguiçosamente o valor e passá-lo de volta.

  2. saber quando a propriedade foi modificada ou excluída. Isso pode introduzir bugs quando a superclasse faz suposições sobre o estado dessa variável. Fazer um método setter protegido para a variável mantém esse controle.

  3. Defina um ponto de interrupção ou adicione saída de depuração quando a variável for lida ou gravada.

  4. Renomeie essa variável de membro sem pesquisar em todo o código que pode usá-la.

Em geral, acho que seria raro eu recomendar fazer uma variável de membro protegida. É melhor você gastar alguns minutos expondo a propriedade por meio de getters / setters do que horas depois rastreando um bug em algum outro código que modificou a variável protegida. Além disso, você está seguro contra a adição de funcionalidades futuras (como carregamento lento) sem quebrar o código dependente. É mais difícil fazer isso depois do que agora.

Michael Bishop
fonte
7

No nível de design, pode ser apropriado usar uma propriedade protegida, mas para implementação não vejo nenhuma vantagem em mapear isso para uma variável de membro protegida em vez de métodos acessadores / modificadores.

As variáveis ​​de membro protegido têm desvantagens significativas porque permitem efetivamente o acesso do código do cliente (a subclasse) ao estado interno da classe base. Isso evita que a classe base mantenha efetivamente suas invariantes.

Pelo mesmo motivo, as variáveis ​​de membro protegidas também tornam a gravação de código multithread seguro significativamente mais difícil, a menos que seja constante garantida ou confinada a uma única thread.

Os métodos de acesso / mutador oferecem consideravelmente mais estabilidade de API e flexibilidade de implementação sob manutenção.

Além disso, se você for um purista OO, os objetos colaboram / se comunicam enviando mensagens, não lendo / definindo o estado.

Em troca, oferecem poucas vantagens. Eu não os removeria necessariamente do código de outra pessoa, mas não os uso pessoalmente.

richj
fonte
4

Na maioria das vezes, é perigoso usar protected porque você quebra um pouco o encapsulamento de sua classe, que poderia muito bem ser dividida por uma classe derivada mal projetada.

Mas eu tenho um bom exemplo: digamos que você possa algum tipo de recipiente genérico. Possui uma implementação interna e acessores internos. Mas você precisa oferecer pelo menos 3 acesso público aos seus dados: map, hash_map, vector-like. Então você tem algo como:

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

Usei esse tipo de código há menos de um mês (portanto, o código vem da memória). Depois de pensar um pouco, acredito que embora o container Base genérico deva ser uma classe abstrata, mesmo que possa viver muito bem, porque usar o Base diretamente seria uma dor, deveria ser proibido.

Resumo Assim, você protegeu os dados usados ​​pela classe derivada. Ainda assim, devemos levar em consideração o fato de que a classe Base deve ser abstrata.

paercebal
fonte
ele não quebra o encapsulamento mais do que os membros públicos. É uma configuração para dizer que as classes derivadas podem usar o estado da classe que não é exposto aos usuários da classe.
gbjbaanb
@gbjbaanb: Você está se contradizendo "isso não quebra o encapsulamento mais do que os membros públicos" é diferente de "[apenas] classes derivadas podem usar o estado da classe". "protegido" é o meio entre o público e o privado. Portanto, "[...] quebra um pouco o encapsulamento protegido" ainda é verdade ...
paercebal
na verdade, na linguagem c ++, adaptadores de contêiner como std :: stack irão expor o objeto de contêiner subjacente com uma variável protegida chamada "c".
Johannes Schaub - litb
Eu sei que este post é bem antigo, mas eu sinto a necessidade de me intrometer. Você não quebra "um pouco" o encapsulamento, você o quebra completamente. protectednão é mais encapsulado do que public. Estou disposto a provar que estou errado. Tudo que você tem a fazer é escrever uma classe com um membro protegido e me proibir de modificá-lo. Obviamente, a classe não pode ser final, já que o objetivo de usar protected é para herança. Ou algo está encapsulado ou não. Não existe um estado intermediário.
Taekahn
3

Resumindo, sim.

As variáveis ​​de membro protegidas permitem o acesso à variável de qualquer subclasse, bem como de qualquer classe no mesmo pacote. Isso pode ser muito útil, especialmente para dados somente leitura. Não acredito que eles sejam necessários, no entanto, porque qualquer uso de uma variável de membro protegida pode ser replicado usando uma variável de membro privada e alguns getters e setters.

Jay Stramel
fonte
1
Por outro lado, as variáveis ​​de membro privado também nunca são necessárias; public é suficiente para qualquer uso.
Alice de
3

Apenas para registro, no item 24 de "Exceptional C ++", em uma das notas de rodapé, Sutter diz "você nunca escreveria uma classe que tivesse uma variável de membro pública ou protegida. Certo? (Apesar do exemplo pobre definido por algumas bibliotecas .) "

hAcKnRoCk
fonte
2

Para informações detalhadas sobre modificadores de acesso .Net, clique aqui

Não há vantagens ou desvantagens reais nas variáveis ​​de membro protegidas, é uma questão de o que você precisa em sua situação específica. Em geral, é uma prática aceita declarar variáveis ​​de membro como privadas e permitir o acesso externo por meio de propriedades. Além disso, algumas ferramentas (por exemplo, alguns mapeadores O / R) esperam que os dados do objeto sejam representados por propriedades e não reconhecem variáveis ​​de membro públicas ou protegidas. Mas se você sabe que deseja que suas subclasses (e SOMENTE suas subclasses) acessem uma determinada variável, não há razão para não declará-la como protegida.

Manu
fonte
Querer que as subclasses acessem uma variável é muito diferente de querer que elas possam alterá- la livremente . Esse é um dos principais argumentos contra as variáveis ​​protegidas: agora sua classe base não pode assumir que nenhuma de suas invariantes seja válida, porque qualquer classe derivada pode fazer absolutamente qualquer coisa com os membros protegidos. Esse é o principal argumento contra eles. Se eles só precisam acessar os dados, então ... escreva um acessador. : P (Eu uso variáveis ​​protegidas, embora provavelmente mais do que deveria, e vou tentar diminuir!)
sublinhado_d