Por que o Código Limpo sugere evitar variáveis protegidas?

161

Código limpo sugere evitar variáveis protegidas na seção "Distância vertical" do capítulo "Formatação":

Concepts that are closely related should be kept vertically close to each other. Clearly this rule doesn't work for concepts that belong in separate files. But then closely related concepts should not be separated into different files unless you have a very good reason. Indeed, this is one of the reasons that protected variables should be avoided.

Qual é o raciocínio?

    
por Matsemann 28.08.2012 / 17:04
fonte

10 respostas

270

As variáveis protegidas devem ser evitadas porque:

  1. Eles tendem a gerar problemas de YAGNI . A menos que você tenha uma classe descendente que realmente faça coisas com o membro protegido, torne-a privada.
  2. Eles tendem a gerar problemas de LSP . Variáveis protegidas geralmente têm alguma invariância intrínseca associada a elas (ou então elas seriam públicas). Os herdeiros precisam manter essas propriedades, que as pessoas podem estragar ou violar intencionalmente.
  3. Eles tendem a violar o OCP . Se a classe base faz muitas suposições sobre o membro protegido, ou o herdeiro é muito flexível com o comportamento da classe, isso pode levar ao comportamento da classe base sendo modificada por essa extensão.
  4. Eles tendem a levar à herança por extensão, em vez de composição. Isso tende a levar a um acoplamento mais rígido, mais violações de SRP , testes mais difíceis e uma série de outras coisas que se enquadram a discussão "favorecer composição sobre herança".

Mas, como você vê, todos estes são 'tendem a'. Às vezes, um membro protegido é a solução mais elegante. E as 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 cometem erros e, no mundo da programação, isso significa bugs e problemas de design.

    
por 28.08.2012 / 18:02
fonte
52

É realmente a mesma razão pela qual você evita globals, apenas em menor escala. É porque é difícil encontrar em todos os lugares uma variável que está sendo lida ou, pior, gravada e difícil de tornar o uso consistente. Se o uso for limitado, como ser escrito em um lugar óbvio na classe derivada e lido em um lugar óbvio na classe base, então variáveis protegidas podem tornar o código mais Claro e conciso. Se você estiver tentado a ler e escrever uma variável por vários arquivos, é melhor encapsulá-la.

    
por 28.08.2012 / 18:06
fonte
26

Eu não li o livro, mas posso entender o que o tio Bob quis dizer.

Se você colocar protected em algo, isso significa que uma classe pode herdá-lo. Mas as variáveis de membro devem pertencer à classe em que estão contidas; isso faz parte do encapsulamento básico. Colocar protected em uma variável de membro interrompe o encapsulamento porque agora uma classe derivada tem acesso aos detalhes de implementação da classe base. É o mesmo problema que ocorre quando você faz uma variável public em uma classe comum.

Para corrigir o problema, você pode encapsular a variável em uma propriedade protegida, assim:

protected string Name
{
    get { return name; }
    private set { name = value; }
}

Isso permite que name seja definido com segurança a partir de uma classe derivada usando um argumento de construtor, sem expor detalhes de implementação da classe base.

    
por 28.08.2012 / 18:05
fonte
17

O argumento do tio Bob é basicamente de distância: se você tiver um conceito que seja importante para uma classe, agrupe o conceito junto com a classe desse arquivo. Não separados em dois arquivos no disco.

As variáveis de membro protegidas estão espalhadas em dois lugares e, parece mágica. Você faz referência a essa variável, mas ela não está definida aqui ... onde é definida? E assim a caça começa. Melhor evitar protected , seu argumento.

Agora, eu não acredito que a regra precise ser obedecida à letra o tempo todo (como: você não usará protected ). Veja o espírito do que ele está fazendo ... agrupe coisas relacionadas em um único 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 sobre isso.

    
por 28.08.2012 / 23:23
fonte
9

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 obtenha informações sobre o C ++, onde você normalmente tem dois arquivos).

Portanto, é virtualmente impossível manter as funções em uma classe derivada acessando uma variável protegida de uma classe base "verticalmente fechada" no código fonte para a definição da variável. Mas idealmente eles devem ser mantidos lá, já que as variáveis protegidas são destinadas ao uso interno, o que faz com que as funções que as acessam frequentemente "sejam um conceito estreitamente relacionado".

    
por 28.08.2012 / 18:44
fonte
4

A idéia básica é que um "campo" (variável no nível da instância) declarado como protegido é provavelmente 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 o equivalente a "acessível apenas por classes filho dentro do mesmo conjunto", concedendo a você a capacidade de definir seus próprios filhos que podem acessar o campo em sua montagem, mas não permitir que crianças criem em outras assembléias o mesmo acesso; C # tem modificadores internos e protegidos, mas combiná-los torna o acesso "interno ou protegido", não "interno e protegido". Assim, um campo que é protegido é acessível a qualquer criança, independentemente de você ter escrito essa criança ou alguém. 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 a alterá-los. em C # você pode fazer um readonly, 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 é sobre isso. Assim, mesmo protegidos, seus filhos (que você não pode confiar) têm acesso a esse campo e podem configurá-lo para 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 o getter (pelo menos) público. Se apenas crianças precisarem, proteja o getter.

Outra abordagem que responde à pergunta é perguntar a si mesmo; Por que o código em um método filho precisa ter a capacidade de modificar meus dados de estado diretamente? O que isso diz sobre esse código? Esse é o argumento da "distância vertical" em sua face. Se há código em um filho que deve alterar diretamente o estado pai, então talvez esse código deva pertencer ao pai em primeiro lugar?

    
por 29.08.2012 / 03:35
fonte
3

É uma discussão interessante.

Para ser franco, boas ferramentas podem atenuar muitos desses problemas "verticais". Na verdade, na minha opinião, a noção do "arquivo" é, na verdade, algo que impede o desenvolvimento de software - algo em que o projeto Light Table está trabalhando (http://www.chris-granger.com/2012/04/12/light- tabela --- um-novo-ide-conceito /).

Retire os arquivos da imagem e o escopo protegido torna-se muito mais atraente. O conceito protegido torna-se mais claro em linguagens como o SmallTalk - onde qualquer herança simplesmente estende o conceito original de uma classe. Ele 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 vindo da composição. Em tais modelos, não vejo motivos para que variáveis privadas sejam não 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, acredito que os métodos privados são inerentemente ruins), por que a classe estendida não deveria representar o armazenamento de tais dados também? >

Para colocar isso de outra forma - em qualquer instância em que eu sinto que uma variável privada seria melhor do que protegida, a hereditariedade não é a solução para o problema em primeiro lugar.

    
por 29.08.2012 / 08:34
fonte
0

Como diz, essa é apenas uma das razões, ou seja, o código logicamente vinculado deve ser colocado dentro de entidades físicas ligadas (arquivos, pacotes e outros). Em uma escala menor, isso é o mesmo que o motivo pelo qual você não coloca uma classe que faz consultas de banco de dados dentro do pacote com classes que exibem a interface do usuário.

A principal razão, no entanto, que as variáveis protegidas não são recomendadas é porque elas tendem a quebrar o encapsulamento. Variáveis e métodos devem ter visibilidade tão limitada quanto possível; para referência adicional, veja o Effective Java de Joshua Bloch, Item 13 - "Minimizar a acessibilidade de classes e membros".

No entanto, você não deve pegar todo este anúncio litteram, apenas como um guildline; se as variáveis protegidas são muito ruins, elas não seriam colocadas dentro da linguagem em primeiro lugar. Um lugar razoável para os campos protegidos é que ele está dentro da classe base de um framework de teste (classes de teste de integração o estendem).

    
por 28.08.2012 / 22:43
fonte
0

Acho que usar variáveis protegidas para testes JUnit pode ser útil. Se você torná-los privados, não poderá acessá-los durante o teste sem reflexão. Isso pode ser útil se você estiver testando um processo complexo por meio de muitas mudanças de estado.

Você poderia torná-los privados, com métodos getter protegidos, mas se as variáveis só forem usadas externamente durante um teste JUnit, não tenho certeza se essa é uma solução melhor.

    
por 29.08.2012 / 11:07
fonte
-1

Sim, eu concordo que é um tanto estranho, dado que (se bem me lembro) o livro também discute todas as variáveis não-privadas como algo a ser evitado.

Acho que o problema com o modificador protegido é que o membro exposto às subclasses não só fica visível para todo o pacote. Eu acho que é esse duplo aspecto que o tio Bob está tomando exceção aqui. É claro que nem sempre é possível ter métodos protegidos e, portanto, a qualificação de variável protegida.

Eu acho que uma abordagem mais interessante é tornar tudo público, o que vai forçá-lo a ter turmas pequenas, o que, por sua vez, torna a métrica de distância vertical inteira um pouco irrelevante.

    
por 28.08.2012 / 22:32
fonte