O Código Limpo sugere evitar variáveis protegidas na seção "Distância Vertical" do capítulo "Formatação":
Os conceitos intimamente relacionados devem ser mantidos verticalmente próximos um do outro. Claramente, esta regra não funciona para conceitos que pertencem a arquivos separados. Porém, conceitos estreitamente relacionados não devem ser separados em arquivos diferentes, a menos que você tenha uma boa razão. De fato, esse é um dos motivos pelos quais as variáveis protegidas devem ser evitadas .
Qual é o raciocínio?
code-quality
clean-code
variables
Matsemann
fonte
fonte
Respostas:
Variáveis protegidas devem ser evitadas porque:
Mas, como você vê, todos estes são "tendem a". Às vezes, um membro protegido é a solução mais elegante. E funções protegidas tendem a ter menos desses problemas. Mas há várias coisas que fazem com que sejam tratadas com cuidado. Com qualquer coisa que exija esse tipo de cuidado, as pessoas cometerão erros e, no mundo da programação, isso significa bugs e problemas de design.
fonte
É realmente a mesma razão pela qual você evita os globais, apenas em uma escala menor. É porque é difícil encontrar em todos os lugares que uma variável está sendo lida, ou pior, gravada e difícil tornar o uso consistente. Se o uso for limitado, como ser escrito em um local óbvio na classe derivada e lido em um local óbvio na classe base, as variáveis protegidas podem tornar o código mais claro e conciso. Se você está tentado a ler e escrever uma variável à vontade em vários arquivos, é melhor encapsulá-la.
fonte
Não li o livro, mas posso dar uma facada no que o tio Bob quis dizer.
Se você colocar
protected
algo, isso significa que uma classe pode herdá-lo. Mas as variáveis-membro devem pertencer à classe em que estão contidas; isso faz parte do encapsulamento básico. A colocaçãoprotected
de uma variável de membro quebra o encapsulamento porque agora uma classe derivada tem acesso aos detalhes de implementação da classe base. É o mesmo problema que ocorre quando você cria uma variávelpublic
em uma classe comum.Para corrigir o problema, você pode encapsular a variável em uma propriedade protegida, da seguinte maneira:
Isso permite
name
que seja definido com segurança a partir de uma classe derivada usando um argumento construtor, sem expor os detalhes de implementação da classe base.fonte
protected
epublic
membros. SeBaseType
expõe um membro público, isso implica que todos os tipos derivados devem ter o mesmo membro funcionando da mesma maneira, uma vez que umaDerivedType
instância pode ser passada para o código que recebe umaBaseType
referência e espera usar esse membro. Por outro lado, seBaseType
expõe umprotected
membro,DerivedType
pode-se esperar acessar esse membrobase
, masbase
não pode ser outra coisa senão aBaseType
.O argumento do tio Bob é principalmente de distância: se você tiver um conceito importante para uma classe, agrupe o conceito com a classe nesse arquivo. Não é separado entre dois arquivos no disco.
As variáveis de membro protegidas estão espalhadas em dois lugares e meio que parecem mágicas. Você faz referência a essa variável, mas ela não está definida aqui ... onde está definida? E assim começa a caçada. Melhor evitar
protected
completamente, seu argumento.Agora eu não acredito que essa regra precise ser obedecida à letra o tempo todo (como: você não deve usar
protected
). Veja o espírito do que ele está conseguindo ... agrupe as coisas relacionadas em um arquivo - use técnicas e recursos de programação para fazer isso. Eu recomendaria que você não analisasse demais e se envolvesse nos detalhes.fonte
Algumas respostas muito boas já estão aqui. Mas uma coisa a acrescentar:
Na maioria das linguagens OO modernas, é um bom hábito (no Java AFAIK é necessário) colocar cada classe em seu próprio arquivo (por favor, não se preocupe com o C ++, onde você costuma ter dois arquivos).
Portanto, é praticamente impossível manter as funções em uma classe derivada acessando uma variável protegida de uma classe base "verticalmente próxima" no código fonte da definição de variável. Mas, idealmente, elas devem ser mantidas lá, pois as variáveis protegidas são destinadas ao uso interno, o que torna as funções que as acessam frequentemente "um conceito intimamente relacionado".
fonte
A idéia básica é que um "campo" (variável no nível da instância) declarado como protegido provavelmente seja mais visível do que deveria ser e menos "protegido" do que você gostaria. Não há modificador de acesso em C / C ++ / Java / C # que seja equivalente a "acessível apenas por classes filho dentro do mesmo assembly", garantindo assim a capacidade de definir seus próprios filhos que podem acessar o campo em seu assembly, mas não permitir que crianças criadas em outras assembléias tenham o mesmo acesso; O C # possui modificadores internos e protegidos, mas combiná-los torna o acesso "interno ou protegido", não "interno e protegido". Portanto, um campo protegido é acessível a qualquer criança, independentemente de você ter escrito essa criança ou outra pessoa. Protegido é, portanto, uma porta aberta para um hacker.
Além disso, os campos por sua definição praticamente não têm validação inerente à sua alteração. em C #, você pode criar um somente leitura, o que torna os tipos de valor efetivamente constantes e os tipos de referência incapazes de serem reinicializados (mas ainda muito mutáveis), mas é isso. Como tal, mesmo protegidos, seus filhos (nos quais você não pode confiar) têm acesso a esse campo e podem defini-lo como algo inválido, tornando o estado do objeto inconsistente (algo a ser evitado).
A maneira aceita de trabalhar com campos é torná-los privados e acessá-los com uma propriedade e / ou um método getter e setter. Se todos os consumidores da classe precisarem do valor, torne público o publicador (pelo menos). Se apenas crianças precisarem, proteja o getter.
Outra abordagem que responde à pergunta é se perguntar; por que o código em um método filho precisa da capacidade de modificar meus dados de estado diretamente? O que isso diz sobre esse código? Esse é o argumento da "distância vertical". Se houver código em um filho que deva alterar diretamente o estado pai, talvez esse código deva pertencer ao pai em primeiro lugar?
fonte
É uma discussão interessante.
Para ser franco, boas ferramentas podem atenuar muitos desses problemas "verticais". De fato, na minha opinião, a noção de "arquivo" é algo que dificulta o desenvolvimento de software - algo no qual o projeto Light Table está trabalhando (http://www.chris-granger.com/2012/04/12/light- tabela --- um-novo-ide-conceito /).
Tire os arquivos da imagem e o escopo protegido se tornará muito mais atraente. O conceito protegido fica mais claro em idiomas como o SmallTalk - onde qualquer herança simplesmente estende o conceito original de uma classe. Deve fazer tudo e ter tudo o que a classe pai fez.
Na minha opinião, as hierarquias de classe devem ser o mais superficial possível, com a maioria das extensões provenientes da composição. Nesses modelos, não vejo motivos para que as variáveis privadas não sejam protegidas. Se a classe estendida deve representar uma extensão, incluindo todo o comportamento da classe pai (o que eu acredito que deveria e, portanto, os métodos privados são inerentemente ruins), por que a classe estendida não deve representar também o armazenamento desses dados?
Dito de outra maneira - em qualquer caso em que eu ache que uma variável privada seria melhor que uma protegida, a herança não é a solução para o problema em primeiro lugar.
fonte
Como diz lá, esse é apenas um dos motivos, ou seja, o código vinculado logicamente deve ser colocado dentro de entidades vinculadas físicas (arquivos, pacotes e outros). Em uma escala menor, é o mesmo que o motivo pelo qual você não coloca uma classe que faz consultas ao banco de dados dentro do pacote com classes que exibem a interface do usuário.
A principal razão, no entanto, de que variáveis protegidas não são recomendadas é porque elas tendem a quebrar o encapsulamento. Variáveis e métodos devem ter a visibilidade mais limitada possível; para referência adicional, consulte Java Efetivo de Joshua Bloch , Item 13 - "Minimize a acessibilidade de classes e membros".
No entanto, você não deve levar todo esse ad litteram, apenas como uma linha de guilda; se as variáveis protegidas forem muito ruins, elas não teriam sido colocadas no idioma em primeiro lugar. Um local razoável para os campos protegidos imho está dentro da classe base a partir de uma estrutura de teste (as classes de teste de integração a estendem).
fonte
Acho que o uso de variáveis protegidas para testes JUnit pode ser útil. Se você os tornar privados, não será possível acessá-los durante o teste sem reflexão. Isso pode ser útil se você estiver testando um processo complexo por meio de muitas alterações de estado.
Você pode torná-los privados, com métodos getter protegidos, mas se as variáveis forem usadas apenas externamente durante um teste JUnit, não tenho certeza de que seja uma solução melhor.
fonte
Sim, eu concordo que isso é um tanto estranho, uma vez que (se bem me lembro) o livro também discute todas as variáveis não privadas como algo a ser evitado.
Eu acho que o problema com o modificador protegido é que não apenas o membro exposto às subclasses também é tornado visível para todo o pacote. Eu acho que é esse aspecto duplo que o tio Bob está fazendo exceção aqui. Obviamente, nem sempre se pode ter métodos protegidos e, portanto, a qualificação de variável protegida.
Eu acho que uma abordagem mais interessante é tornar tudo público, que forçará você a ter turmas pequenas, o que, por sua vez, torna toda a métrica de distância vertical um pouco discutível.
fonte